"""Test the Fresh-r sensor platform.""" from unittest.mock import MagicMock from aiohttp import ClientError from freezegun.api import FrozenDateTimeFactory from pyfreshr.exceptions import ApiResponseError from pyfreshr.models import DeviceReadings, DeviceSummary import pytest from syrupy.assertion import SnapshotAssertion from homeassistant.components.freshr.const import DOMAIN from homeassistant.components.freshr.coordinator import ( DEVICES_SCAN_INTERVAL, READINGS_SCAN_INTERVAL, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er from .conftest import DEVICE_ID, MOCK_DEVICE_CURRENT from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") async def test_entities( hass: HomeAssistant, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, device_registry: dr.DeviceRegistry, mock_config_entry: MockConfigEntry, ) -> None: """Test the sensor entities.""" await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) device_entry = device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) assert device_entry entity_entries = er.async_entries_for_config_entry( entity_registry, mock_config_entry.entry_id ) for entity_entry in entity_entries: assert entity_entry.device_id == device_entry.id @pytest.mark.usefixtures("entity_registry_enabled_by_default") async def test_sensor_none_values( hass: HomeAssistant, entity_registry: er.EntityRegistry, mock_config_entry: MockConfigEntry, mock_freshr_client: MagicMock, ) -> None: """Test sensors return unknown when all readings are None.""" mock_freshr_client.fetch_device_current.return_value = DeviceReadings( t1=None, t2=None, co2=None, hum=None, flow=None, dp=None ) mock_config_entry.add_to_hass(hass) await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() for key in ("t1", "t2", "co2", "hum", "flow", "dp"): entity_id = entity_registry.async_get_entity_id( "sensor", DOMAIN, f"{DEVICE_ID}_{key}" ) assert entity_id is not None assert hass.states.get(entity_id).state == "unknown" @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") @pytest.mark.parametrize( "error", [ApiResponseError("api error"), ClientError("network error")], ) async def test_readings_connection_error_makes_unavailable( hass: HomeAssistant, mock_freshr_client: MagicMock, freezer: FrozenDateTimeFactory, error: Exception, ) -> None: """Test that connection errors during readings refresh mark entities unavailable.""" mock_freshr_client.fetch_device_current.side_effect = error freezer.tick(READINGS_SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() state = hass.states.get("sensor.fresh_r_inside_temperature") assert state is not None assert state.state == "unavailable" DEVICE_ID_2 = "SN002" @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") async def test_device_reappears_after_removal( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_freshr_client: MagicMock, device_registry: dr.DeviceRegistry, entity_registry: er.EntityRegistry, freezer: FrozenDateTimeFactory, ) -> None: """Test that entities are re-created when a previously removed device reappears.""" assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) # Device disappears from the account mock_freshr_client.fetch_devices.return_value = [] freezer.tick(DEVICES_SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) is None # Device reappears mock_freshr_client.fetch_devices.return_value = [DeviceSummary(id=DEVICE_ID)] mock_freshr_client.fetch_device_current.return_value = MOCK_DEVICE_CURRENT freezer.tick(DEVICES_SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) t1_entity_id = entity_registry.async_get_entity_id( "sensor", DOMAIN, f"{DEVICE_ID}_t1" ) assert t1_entity_id assert hass.states.get(t1_entity_id).state != "unavailable" @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") async def test_dynamic_device_added( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_freshr_client: MagicMock, device_registry: dr.DeviceRegistry, entity_registry: er.EntityRegistry, freezer: FrozenDateTimeFactory, ) -> None: """Test that sensors are created for a device that appears after initial setup.""" assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID_2)}) is None mock_freshr_client.fetch_devices.return_value = [ DeviceSummary(id=DEVICE_ID), DeviceSummary(id=DEVICE_ID_2), ] mock_freshr_client.fetch_device_current.return_value = MOCK_DEVICE_CURRENT freezer.tick(DEVICES_SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID_2)}) t1_entity_id = entity_registry.async_get_entity_id( "sensor", DOMAIN, f"{DEVICE_ID_2}_t1" ) assert t1_entity_id assert entity_registry.async_get_entity_id("sensor", DOMAIN, f"{DEVICE_ID_2}_co2") assert hass.states.get(t1_entity_id).state != "unavailable"