diff --git a/homeassistant/components/airgradient/__init__.py b/homeassistant/components/airgradient/__init__.py index b611bf0fb74..da3edcf0453 100644 --- a/homeassistant/components/airgradient/__init__.py +++ b/homeassistant/components/airgradient/__init__.py @@ -2,24 +2,47 @@ from __future__ import annotations +from airgradient import AirGradientClient + from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DOMAIN -from .coordinator import AirGradientDataUpdateCoordinator +from .coordinator import AirGradientConfigCoordinator, AirGradientMeasurementCoordinator -PLATFORMS: list[Platform] = [Platform.SENSOR] +PLATFORMS: list[Platform] = [Platform.SELECT, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Airgradient from a config entry.""" - coordinator = AirGradientDataUpdateCoordinator(hass, entry.data[CONF_HOST]) + client = AirGradientClient( + entry.data[CONF_HOST], session=async_get_clientsession(hass) + ) - await coordinator.async_config_entry_first_refresh() + measurement_coordinator = AirGradientMeasurementCoordinator(hass, client) + config_coordinator = AirGradientConfigCoordinator(hass, client) - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + await measurement_coordinator.async_config_entry_first_refresh() + await config_coordinator.async_config_entry_first_refresh() + + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, measurement_coordinator.serial_number)}, + manufacturer="AirGradient", + model=measurement_coordinator.data.model, + serial_number=measurement_coordinator.data.serial_number, + sw_version=measurement_coordinator.data.firmware_version, + ) + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { + "measurement": measurement_coordinator, + "config": config_coordinator, + } await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/homeassistant/components/airgradient/coordinator.py b/homeassistant/components/airgradient/coordinator.py index d54e1b46efd..90aded9a4ba 100644 --- a/homeassistant/components/airgradient/coordinator.py +++ b/homeassistant/components/airgradient/coordinator.py @@ -2,31 +2,56 @@ from datetime import timedelta -from airgradient import AirGradientClient, AirGradientError, Measures +from airgradient import AirGradientClient, AirGradientError, Config, Measures +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import LOGGER -class AirGradientDataUpdateCoordinator(DataUpdateCoordinator[Measures]): +class AirGradientCoordinator[_DataT](DataUpdateCoordinator[_DataT]): """Class to manage fetching AirGradient data.""" - def __init__(self, hass: HomeAssistant, host: str) -> None: + _update_interval: timedelta + config_entry: ConfigEntry + + def __init__(self, hass: HomeAssistant, client: AirGradientClient) -> None: """Initialize coordinator.""" super().__init__( hass, logger=LOGGER, - name=f"AirGradient {host}", - update_interval=timedelta(minutes=1), + name=f"AirGradient {client.host}", + update_interval=self._update_interval, ) - session = async_get_clientsession(hass) - self.client = AirGradientClient(host, session=session) + self.client = client + assert self.config_entry.unique_id + self.serial_number = self.config_entry.unique_id - async def _async_update_data(self) -> Measures: + async def _async_update_data(self) -> _DataT: try: - return await self.client.get_current_measures() + return await self._update_data() except AirGradientError as error: raise UpdateFailed(error) from error + + async def _update_data(self) -> _DataT: + raise NotImplementedError + + +class AirGradientMeasurementCoordinator(AirGradientCoordinator[Measures]): + """Class to manage fetching AirGradient data.""" + + _update_interval = timedelta(minutes=1) + + async def _update_data(self) -> Measures: + return await self.client.get_current_measures() + + +class AirGradientConfigCoordinator(AirGradientCoordinator[Config]): + """Class to manage fetching AirGradient data.""" + + _update_interval = timedelta(minutes=5) + + async def _update_data(self) -> Config: + return await self.client.get_config() diff --git a/homeassistant/components/airgradient/entity.py b/homeassistant/components/airgradient/entity.py index e663a75bd91..4de07904bba 100644 --- a/homeassistant/components/airgradient/entity.py +++ b/homeassistant/components/airgradient/entity.py @@ -4,21 +4,17 @@ from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN -from .coordinator import AirGradientDataUpdateCoordinator +from .coordinator import AirGradientCoordinator -class AirGradientEntity(CoordinatorEntity[AirGradientDataUpdateCoordinator]): +class AirGradientEntity(CoordinatorEntity[AirGradientCoordinator]): """Defines a base AirGradient entity.""" _attr_has_entity_name = True - def __init__(self, coordinator: AirGradientDataUpdateCoordinator) -> None: + def __init__(self, coordinator: AirGradientCoordinator) -> None: """Initialize airgradient entity.""" super().__init__(coordinator) self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, coordinator.data.serial_number)}, - model=coordinator.data.model, - manufacturer="AirGradient", - serial_number=coordinator.data.serial_number, - sw_version=coordinator.data.firmware_version, + identifiers={(DOMAIN, coordinator.serial_number)}, ) diff --git a/homeassistant/components/airgradient/select.py b/homeassistant/components/airgradient/select.py new file mode 100644 index 00000000000..8dc13fe0eba --- /dev/null +++ b/homeassistant/components/airgradient/select.py @@ -0,0 +1,119 @@ +"""Support for AirGradient select entities.""" + +from collections.abc import Awaitable, Callable +from dataclasses import dataclass + +from airgradient import AirGradientClient, Config +from airgradient.models import ConfigurationControl, TemperatureUnit + +from homeassistant.components.select import SelectEntity, SelectEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import AirGradientConfigCoordinator, AirGradientMeasurementCoordinator +from .entity import AirGradientEntity + + +@dataclass(frozen=True, kw_only=True) +class AirGradientSelectEntityDescription(SelectEntityDescription): + """Describes AirGradient select entity.""" + + value_fn: Callable[[Config], str] + set_value_fn: Callable[[AirGradientClient, str], Awaitable[None]] + requires_display: bool = False + + +CONFIG_CONTROL_ENTITY = AirGradientSelectEntityDescription( + key="configuration_control", + translation_key="configuration_control", + options=[x.value for x in ConfigurationControl], + value_fn=lambda config: config.configuration_control, + set_value_fn=lambda client, value: client.set_configuration_control( + ConfigurationControl(value) + ), +) + +PROTECTED_SELECT_TYPES: tuple[AirGradientSelectEntityDescription, ...] = ( + AirGradientSelectEntityDescription( + key="display_temperature_unit", + translation_key="display_temperature_unit", + options=[x.value for x in TemperatureUnit], + value_fn=lambda config: config.temperature_unit, + set_value_fn=lambda client, value: client.set_temperature_unit( + TemperatureUnit(value) + ), + requires_display=True, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up AirGradient select entities based on a config entry.""" + + config_coordinator: AirGradientConfigCoordinator = hass.data[DOMAIN][ + entry.entry_id + ]["config"] + measurement_coordinator: AirGradientMeasurementCoordinator = hass.data[DOMAIN][ + entry.entry_id + ]["measurement"] + + entities = [AirGradientSelect(config_coordinator, CONFIG_CONTROL_ENTITY)] + + entities.extend( + AirGradientProtectedSelect(config_coordinator, description) + for description in PROTECTED_SELECT_TYPES + if ( + description.requires_display + and measurement_coordinator.data.model.startswith("I") + ) + ) + + async_add_entities(entities) + + +class AirGradientSelect(AirGradientEntity, SelectEntity): + """Defines an AirGradient select entity.""" + + entity_description: AirGradientSelectEntityDescription + coordinator: AirGradientConfigCoordinator + + def __init__( + self, + coordinator: AirGradientConfigCoordinator, + description: AirGradientSelectEntityDescription, + ) -> None: + """Initialize AirGradient select.""" + super().__init__(coordinator) + self.entity_description = description + self._attr_unique_id = f"{coordinator.serial_number}-{description.key}" + + @property + def current_option(self) -> str: + """Return the state of the select.""" + return self.entity_description.value_fn(self.coordinator.data) + + async def async_select_option(self, option: str) -> None: + """Change the selected option.""" + await self.entity_description.set_value_fn(self.coordinator.client, option) + await self.coordinator.async_request_refresh() + + +class AirGradientProtectedSelect(AirGradientSelect): + """Defines a protected AirGradient select entity.""" + + async def async_select_option(self, option: str) -> None: + """Change the selected option.""" + if ( + self.coordinator.data.configuration_control + is not ConfigurationControl.LOCAL + ): + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="no_local_configuration", + ) + await super().async_select_option(option) diff --git a/homeassistant/components/airgradient/sensor.py b/homeassistant/components/airgradient/sensor.py index 450655de67b..e2fc580fce5 100644 --- a/homeassistant/components/airgradient/sensor.py +++ b/homeassistant/components/airgradient/sensor.py @@ -24,8 +24,8 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from . import AirGradientDataUpdateCoordinator from .const import DOMAIN +from .coordinator import AirGradientMeasurementCoordinator from .entity import AirGradientEntity @@ -130,7 +130,9 @@ async def async_setup_entry( ) -> None: """Set up AirGradient sensor entities based on a config entry.""" - coordinator: AirGradientDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: AirGradientMeasurementCoordinator = hass.data[DOMAIN][entry.entry_id][ + "measurement" + ] listener: Callable[[], None] | None = None not_setup: set[AirGradientSensorEntityDescription] = set(SENSOR_TYPES) @@ -162,16 +164,17 @@ class AirGradientSensor(AirGradientEntity, SensorEntity): """Defines an AirGradient sensor.""" entity_description: AirGradientSensorEntityDescription + coordinator: AirGradientMeasurementCoordinator def __init__( self, - coordinator: AirGradientDataUpdateCoordinator, + coordinator: AirGradientMeasurementCoordinator, description: AirGradientSensorEntityDescription, ) -> None: """Initialize airgradient sensor.""" super().__init__(coordinator) self.entity_description = description - self._attr_unique_id = f"{coordinator.data.serial_number}-{description.key}" + self._attr_unique_id = f"{coordinator.serial_number}-{description.key}" @property def native_value(self) -> StateType: diff --git a/homeassistant/components/airgradient/strings.json b/homeassistant/components/airgradient/strings.json index f4e0dabced2..f4441a66209 100644 --- a/homeassistant/components/airgradient/strings.json +++ b/homeassistant/components/airgradient/strings.json @@ -23,6 +23,23 @@ } }, "entity": { + "select": { + "configuration_control": { + "name": "Configuration source", + "state": { + "cloud": "Cloud", + "local": "Local", + "both": "Both" + } + }, + "display_temperature_unit": { + "name": "Display temperature unit", + "state": { + "c": "Celsius", + "f": "Fahrenheit" + } + } + }, "sensor": { "total_volatile_organic_component_index": { "name": "Total VOC index" @@ -40,5 +57,10 @@ "name": "Raw nitrogen" } } + }, + "exceptions": { + "no_local_configuration": { + "message": "Device should be configured with local configuration to be able to change settings." + } } } diff --git a/tests/components/airgradient/conftest.py b/tests/components/airgradient/conftest.py index ed1f8acb381..aa2c1e783a4 100644 --- a/tests/components/airgradient/conftest.py +++ b/tests/components/airgradient/conftest.py @@ -3,7 +3,7 @@ from collections.abc import Generator from unittest.mock import patch -from airgradient import Measures +from airgradient import Config, Measures import pytest from homeassistant.components.airgradient.const import DOMAIN @@ -28,7 +28,7 @@ def mock_airgradient_client() -> Generator[AsyncMock, None, None]: """Mock an AirGradient client.""" with ( patch( - "homeassistant.components.airgradient.coordinator.AirGradientClient", + "homeassistant.components.airgradient.AirGradientClient", autospec=True, ) as mock_client, patch( @@ -37,9 +37,13 @@ def mock_airgradient_client() -> Generator[AsyncMock, None, None]: ), ): client = mock_client.return_value + client.host = "10.0.0.131" client.get_current_measures.return_value = Measures.from_json( load_fixture("current_measures.json", DOMAIN) ) + client.get_config.return_value = Config.from_json( + load_fixture("get_config.json", DOMAIN) + ) yield client diff --git a/tests/components/airgradient/fixtures/current_measures_outdoor.json b/tests/components/airgradient/fixtures/current_measures_outdoor.json new file mode 100644 index 00000000000..f5e63a095c2 --- /dev/null +++ b/tests/components/airgradient/fixtures/current_measures_outdoor.json @@ -0,0 +1,24 @@ +{ + "wifi": -64, + "serialno": "84fce60bec38", + "channels": { + "1": { + "pm01": 3, + "pm02": 5, + "pm10": 5, + "pm003Count": 753, + "atmp": 18.8, + "rhum": 68, + "atmpCompensated": 17.09, + "rhumCompensated": 92 + } + }, + "tvocIndex": 49, + "tvocRaw": 30802, + "noxIndex": 1, + "noxRaw": 16359, + "bootCount": 1, + "ledMode": "co2", + "firmware": "3.1.1", + "model": "O-1PPT" +} diff --git a/tests/components/airgradient/fixtures/get_config.json b/tests/components/airgradient/fixtures/get_config.json new file mode 100644 index 00000000000..db20f762037 --- /dev/null +++ b/tests/components/airgradient/fixtures/get_config.json @@ -0,0 +1,13 @@ +{ + "country": "DE", + "pmStandard": "ugm3", + "ledBarMode": "co2", + "displayMode": "on", + "abcDays": 8, + "tvocLearningOffset": 12, + "noxLearningOffset": 12, + "mqttBrokerUrl": "", + "temperatureUnit": "c", + "configurationControl": "both", + "postDataToAirGradient": true +} diff --git a/tests/components/airgradient/snapshots/test_select.ambr b/tests/components/airgradient/snapshots/test_select.ambr new file mode 100644 index 00000000000..e32b57758c1 --- /dev/null +++ b/tests/components/airgradient/snapshots/test_select.ambr @@ -0,0 +1,170 @@ +# serializer version: 1 +# name: test_all_entities[select.airgradient_configuration_source-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'cloud', + 'local', + 'both', + ]), + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.airgradient_configuration_source', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Configuration source', + 'platform': 'airgradient', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'configuration_control', + 'unique_id': '84fce612f5b8-configuration_control', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[select.airgradient_configuration_source-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Airgradient Configuration source', + 'options': list([ + 'cloud', + 'local', + 'both', + ]), + }), + 'context': , + 'entity_id': 'select.airgradient_configuration_source', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'both', + }) +# --- +# name: test_all_entities[select.airgradient_display_temperature_unit-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'c', + 'f', + ]), + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.airgradient_display_temperature_unit', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Display temperature unit', + 'platform': 'airgradient', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'display_temperature_unit', + 'unique_id': '84fce612f5b8-display_temperature_unit', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[select.airgradient_display_temperature_unit-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Airgradient Display temperature unit', + 'options': list([ + 'c', + 'f', + ]), + }), + 'context': , + 'entity_id': 'select.airgradient_display_temperature_unit', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'c', + }) +# --- +# name: test_all_entities_outdoor[select.airgradient_configuration_source-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'cloud', + 'local', + 'both', + ]), + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.airgradient_configuration_source', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Configuration source', + 'platform': 'airgradient', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'configuration_control', + 'unique_id': '84fce612f5b8-configuration_control', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities_outdoor[select.airgradient_configuration_source-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Airgradient Configuration source', + 'options': list([ + 'cloud', + 'local', + 'both', + ]), + }), + 'context': , + 'entity_id': 'select.airgradient_configuration_source', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'both', + }) +# --- diff --git a/tests/components/airgradient/test_select.py b/tests/components/airgradient/test_select.py new file mode 100644 index 00000000000..2988a5918ad --- /dev/null +++ b/tests/components/airgradient/test_select.py @@ -0,0 +1,115 @@ +"""Tests for the AirGradient select platform.""" + +from unittest.mock import AsyncMock, patch + +from airgradient import ConfigurationControl, Measures +import pytest +from syrupy import SnapshotAssertion + +from homeassistant.components.airgradient import DOMAIN +from homeassistant.components.select import ( + DOMAIN as SELECT_DOMAIN, + SERVICE_SELECT_OPTION, +) +from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError +from homeassistant.helpers import entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, load_fixture, snapshot_platform + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_airgradient_client: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.SELECT]): + await setup_integration(hass, mock_config_entry) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_all_entities_outdoor( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_airgradient_client: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + mock_airgradient_client.get_current_measures.return_value = Measures.from_json( + load_fixture("current_measures_outdoor.json", DOMAIN) + ) + with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.SELECT]): + await setup_integration(hass, mock_config_entry) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +async def test_setting_value( + hass: HomeAssistant, + mock_airgradient_client: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test setting value.""" + await setup_integration(hass, mock_config_entry) + + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.airgradient_configuration_source", + ATTR_OPTION: "local", + }, + blocking=True, + ) + mock_airgradient_client.set_configuration_control.assert_called_once_with("local") + assert mock_airgradient_client.get_config.call_count == 2 + + +async def test_setting_protected_value( + hass: HomeAssistant, + mock_airgradient_client: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test setting protected value.""" + await setup_integration(hass, mock_config_entry) + + mock_airgradient_client.get_config.return_value.configuration_control = ( + ConfigurationControl.CLOUD + ) + + with pytest.raises(ServiceValidationError): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.airgradient_display_temperature_unit", + ATTR_OPTION: "c", + }, + blocking=True, + ) + mock_airgradient_client.set_temperature_unit.assert_not_called() + + mock_airgradient_client.get_config.return_value.configuration_control = ( + ConfigurationControl.LOCAL + ) + + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.airgradient_display_temperature_unit", + ATTR_OPTION: "c", + }, + blocking=True, + ) + mock_airgradient_client.set_temperature_unit.assert_called_once_with("c") diff --git a/tests/components/airgradient/test_sensor.py b/tests/components/airgradient/test_sensor.py index de8f8a6add9..65c96a0669f 100644 --- a/tests/components/airgradient/test_sensor.py +++ b/tests/components/airgradient/test_sensor.py @@ -1,7 +1,7 @@ """Tests for the AirGradient sensor platform.""" from datetime import timedelta -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, patch from airgradient import AirGradientError, Measures from freezegun.api import FrozenDateTimeFactory @@ -9,7 +9,7 @@ import pytest from syrupy import SnapshotAssertion from homeassistant.components.airgradient import DOMAIN -from homeassistant.const import STATE_UNAVAILABLE +from homeassistant.const import STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -32,7 +32,8 @@ async def test_all_entities( entity_registry: er.EntityRegistry, ) -> None: """Test all entities.""" - await setup_integration(hass, mock_config_entry) + with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.SENSOR]): + await setup_integration(hass, mock_config_entry) await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) @@ -47,7 +48,8 @@ async def test_create_entities( mock_airgradient_client.get_current_measures.return_value = Measures.from_json( load_fixture("measures_after_boot.json", DOMAIN) ) - await setup_integration(hass, mock_config_entry) + with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.SENSOR]): + await setup_integration(hass, mock_config_entry) assert len(hass.states.async_all()) == 0 mock_airgradient_client.get_current_measures.return_value = Measures.from_json(