Remove air_quality
platform from Nettigo Air Monitor integration (#52152)
* Remove air_quality platform * Clean constants
This commit is contained in:
parent
a5ca25019c
commit
0730b375f3
6 changed files with 211 additions and 284 deletions
|
@ -14,19 +14,28 @@ from nettigo_air_monitor import (
|
||||||
NettigoAirMonitor,
|
NettigoAirMonitor,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST
|
from homeassistant.const import CONF_HOST
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import DEFAULT_NAME, DEFAULT_UPDATE_INTERVAL, DOMAIN, MANUFACTURER
|
from .const import (
|
||||||
|
ATTR_SDS011,
|
||||||
|
ATTR_SPS30,
|
||||||
|
DEFAULT_NAME,
|
||||||
|
DEFAULT_UPDATE_INTERVAL,
|
||||||
|
DOMAIN,
|
||||||
|
MANUFACTURER,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLATFORMS = ["air_quality", "sensor"]
|
PLATFORMS = ["sensor"]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
@ -43,6 +52,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
# Remove air_quality entities from registry if they exist
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
for sensor_type in ["sds", ATTR_SDS011, ATTR_SPS30]:
|
||||||
|
unique_id = f"{coordinator.unique_id}-{sensor_type}"
|
||||||
|
if entity_id := ent_reg.async_get_entity_id(
|
||||||
|
AIR_QUALITY_PLATFORM, DOMAIN, unique_id
|
||||||
|
):
|
||||||
|
_LOGGER.debug("Removing deprecated air_quality entity %s", entity_id)
|
||||||
|
ent_reg.async_remove(entity_id)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
"""Support for the Nettigo Air Monitor air_quality service."""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import logging
|
|
||||||
from typing import Union, cast
|
|
||||||
|
|
||||||
from homeassistant.components.air_quality import DOMAIN as PLATFORM, AirQualityEntity
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
|
||||||
from homeassistant.helpers import entity_registry
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
||||||
|
|
||||||
from . import NAMDataUpdateCoordinator
|
|
||||||
from .const import (
|
|
||||||
AIR_QUALITY_SENSORS,
|
|
||||||
ATTR_SDS011,
|
|
||||||
DEFAULT_NAME,
|
|
||||||
DOMAIN,
|
|
||||||
SUFFIX_P1,
|
|
||||||
SUFFIX_P2,
|
|
||||||
)
|
|
||||||
|
|
||||||
PARALLEL_UPDATES = 1
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
|
||||||
) -> None:
|
|
||||||
"""Add a Nettigo Air Monitor entities from a config_entry."""
|
|
||||||
coordinator: NAMDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
|
||||||
|
|
||||||
# Due to the change of the attribute name of one sensor, it is necessary to migrate
|
|
||||||
# the unique_id to the new name.
|
|
||||||
ent_reg = entity_registry.async_get(hass)
|
|
||||||
old_unique_id = f"{coordinator.unique_id}-sds"
|
|
||||||
new_unique_id = f"{coordinator.unique_id}-{ATTR_SDS011}"
|
|
||||||
if entity_id := ent_reg.async_get_entity_id(PLATFORM, DOMAIN, old_unique_id):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
|
|
||||||
entity_id,
|
|
||||||
old_unique_id,
|
|
||||||
new_unique_id,
|
|
||||||
)
|
|
||||||
ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id)
|
|
||||||
|
|
||||||
entities: list[NAMAirQuality] = []
|
|
||||||
for sensor in AIR_QUALITY_SENSORS:
|
|
||||||
if getattr(coordinator.data, f"{sensor}{SUFFIX_P1}") is not None:
|
|
||||||
entities.append(NAMAirQuality(coordinator, sensor))
|
|
||||||
|
|
||||||
async_add_entities(entities, False)
|
|
||||||
|
|
||||||
|
|
||||||
class NAMAirQuality(CoordinatorEntity, AirQualityEntity):
|
|
||||||
"""Define an Nettigo Air Monitor air quality."""
|
|
||||||
|
|
||||||
coordinator: NAMDataUpdateCoordinator
|
|
||||||
|
|
||||||
def __init__(self, coordinator: NAMDataUpdateCoordinator, sensor_type: str) -> None:
|
|
||||||
"""Initialize."""
|
|
||||||
super().__init__(coordinator)
|
|
||||||
self._attr_device_info = coordinator.device_info
|
|
||||||
self._attr_name = f"{DEFAULT_NAME} {AIR_QUALITY_SENSORS[sensor_type]}"
|
|
||||||
self._attr_unique_id = f"{coordinator.unique_id}-{sensor_type}"
|
|
||||||
self.sensor_type = sensor_type
|
|
||||||
|
|
||||||
@property
|
|
||||||
def particulate_matter_2_5(self) -> int | None:
|
|
||||||
"""Return the particulate matter 2.5 level."""
|
|
||||||
return cast(
|
|
||||||
Union[int, None],
|
|
||||||
getattr(self.coordinator.data, f"{self.sensor_type}{SUFFIX_P2}"),
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def particulate_matter_10(self) -> int | None:
|
|
||||||
"""Return the particulate matter 10 level."""
|
|
||||||
return cast(
|
|
||||||
Union[int, None],
|
|
||||||
getattr(self.coordinator.data, f"{self.sensor_type}{SUFFIX_P1}"),
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def carbon_dioxide(self) -> int | None:
|
|
||||||
"""Return the particulate matter 10 level."""
|
|
||||||
return cast(Union[int, None], self.coordinator.data.mhz14a_carbon_dioxide)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def available(self) -> bool:
|
|
||||||
"""Return if entity is available."""
|
|
||||||
available = super().available
|
|
||||||
|
|
||||||
# For a short time after booting, the device does not return values for all
|
|
||||||
# sensors. For this reason, we mark entities for which data is missing as
|
|
||||||
# unavailable.
|
|
||||||
return (
|
|
||||||
available
|
|
||||||
and getattr(self.coordinator.data, f"{self.sensor_type}{SUFFIX_P2}")
|
|
||||||
is not None
|
|
||||||
)
|
|
|
@ -9,6 +9,8 @@ from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_ICON,
|
ATTR_ICON,
|
||||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
CONCENTRATION_PARTS_PER_MILLION,
|
||||||
|
DEVICE_CLASS_CO2,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_PRESSURE,
|
DEVICE_CLASS_PRESSURE,
|
||||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||||
|
@ -36,12 +38,17 @@ ATTR_DHT22_HUMIDITY: Final = "dht22_humidity"
|
||||||
ATTR_DHT22_TEMPERATURE: Final = "dht22_temperature"
|
ATTR_DHT22_TEMPERATURE: Final = "dht22_temperature"
|
||||||
ATTR_HECA_HUMIDITY: Final = "heca_humidity"
|
ATTR_HECA_HUMIDITY: Final = "heca_humidity"
|
||||||
ATTR_HECA_TEMPERATURE: Final = "heca_temperature"
|
ATTR_HECA_TEMPERATURE: Final = "heca_temperature"
|
||||||
|
ATTR_MHZ14A_CARBON_DIOXIDE: Final = "mhz14a_carbon_dioxide"
|
||||||
ATTR_SDS011: Final = "sds011"
|
ATTR_SDS011: Final = "sds011"
|
||||||
|
ATTR_SDS011_P1: Final = f"{ATTR_SDS011}{SUFFIX_P1}"
|
||||||
|
ATTR_SDS011_P2: Final = f"{ATTR_SDS011}{SUFFIX_P2}"
|
||||||
ATTR_SHT3X_HUMIDITY: Final = "sht3x_humidity"
|
ATTR_SHT3X_HUMIDITY: Final = "sht3x_humidity"
|
||||||
ATTR_SHT3X_TEMPERATURE: Final = "sht3x_temperature"
|
ATTR_SHT3X_TEMPERATURE: Final = "sht3x_temperature"
|
||||||
ATTR_SIGNAL_STRENGTH: Final = "signal"
|
ATTR_SIGNAL_STRENGTH: Final = "signal"
|
||||||
ATTR_SPS30: Final = "sps30"
|
ATTR_SPS30: Final = "sps30"
|
||||||
ATTR_SPS30_P0: Final = f"{ATTR_SPS30}{SUFFIX_P0}"
|
ATTR_SPS30_P0: Final = f"{ATTR_SPS30}{SUFFIX_P0}"
|
||||||
|
ATTR_SPS30_P1: Final = f"{ATTR_SPS30}{SUFFIX_P1}"
|
||||||
|
ATTR_SPS30_P2: Final = f"{ATTR_SPS30}{SUFFIX_P2}"
|
||||||
ATTR_SPS30_P4: Final = f"{ATTR_SPS30}{SUFFIX_P4}"
|
ATTR_SPS30_P4: Final = f"{ATTR_SPS30}{SUFFIX_P4}"
|
||||||
ATTR_UPTIME: Final = "uptime"
|
ATTR_UPTIME: Final = "uptime"
|
||||||
|
|
||||||
|
@ -54,11 +61,6 @@ DEFAULT_UPDATE_INTERVAL: Final = timedelta(minutes=6)
|
||||||
DOMAIN: Final = "nam"
|
DOMAIN: Final = "nam"
|
||||||
MANUFACTURER: Final = "Nettigo"
|
MANUFACTURER: Final = "Nettigo"
|
||||||
|
|
||||||
AIR_QUALITY_SENSORS: Final[dict[str, str]] = {
|
|
||||||
ATTR_SDS011: "SDS011",
|
|
||||||
ATTR_SPS30: "SPS30",
|
|
||||||
}
|
|
||||||
|
|
||||||
MIGRATION_SENSORS: Final = [
|
MIGRATION_SENSORS: Final = [
|
||||||
("temperature", ATTR_DHT22_TEMPERATURE),
|
("temperature", ATTR_DHT22_TEMPERATURE),
|
||||||
("humidity", ATTR_DHT22_HUMIDITY),
|
("humidity", ATTR_DHT22_HUMIDITY),
|
||||||
|
@ -121,6 +123,30 @@ SENSORS: Final[dict[str, SensorDescription]] = {
|
||||||
ATTR_ENABLED: True,
|
ATTR_ENABLED: True,
|
||||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
},
|
},
|
||||||
|
ATTR_MHZ14A_CARBON_DIOXIDE: {
|
||||||
|
ATTR_LABEL: f"{DEFAULT_NAME} MH-Z14A Carbon Dioxide",
|
||||||
|
ATTR_UNIT: CONCENTRATION_PARTS_PER_MILLION,
|
||||||
|
ATTR_DEVICE_CLASS: DEVICE_CLASS_CO2,
|
||||||
|
ATTR_ICON: None,
|
||||||
|
ATTR_ENABLED: True,
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
},
|
||||||
|
ATTR_SDS011_P1: {
|
||||||
|
ATTR_LABEL: f"{DEFAULT_NAME} SDS011 Particulate Matter 10",
|
||||||
|
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
ATTR_DEVICE_CLASS: None,
|
||||||
|
ATTR_ICON: "mdi:blur",
|
||||||
|
ATTR_ENABLED: True,
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
},
|
||||||
|
ATTR_SDS011_P2: {
|
||||||
|
ATTR_LABEL: f"{DEFAULT_NAME} SDS011 Particulate Matter 2.5",
|
||||||
|
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
ATTR_DEVICE_CLASS: None,
|
||||||
|
ATTR_ICON: "mdi:blur",
|
||||||
|
ATTR_ENABLED: True,
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
},
|
||||||
ATTR_SHT3X_HUMIDITY: {
|
ATTR_SHT3X_HUMIDITY: {
|
||||||
ATTR_LABEL: f"{DEFAULT_NAME} SHT3X Humidity",
|
ATTR_LABEL: f"{DEFAULT_NAME} SHT3X Humidity",
|
||||||
ATTR_UNIT: PERCENTAGE,
|
ATTR_UNIT: PERCENTAGE,
|
||||||
|
@ -145,6 +171,22 @@ SENSORS: Final[dict[str, SensorDescription]] = {
|
||||||
ATTR_ENABLED: True,
|
ATTR_ENABLED: True,
|
||||||
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
},
|
},
|
||||||
|
ATTR_SPS30_P1: {
|
||||||
|
ATTR_LABEL: f"{DEFAULT_NAME} SPS30 Particulate Matter 10",
|
||||||
|
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
ATTR_DEVICE_CLASS: None,
|
||||||
|
ATTR_ICON: "mdi:blur",
|
||||||
|
ATTR_ENABLED: True,
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
},
|
||||||
|
ATTR_SPS30_P2: {
|
||||||
|
ATTR_LABEL: f"{DEFAULT_NAME} SPS30 Particulate Matter 2.5",
|
||||||
|
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
ATTR_DEVICE_CLASS: None,
|
||||||
|
ATTR_ICON: "mdi:blur",
|
||||||
|
ATTR_ENABLED: True,
|
||||||
|
ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||||
|
},
|
||||||
ATTR_SPS30_P4: {
|
ATTR_SPS30_P4: {
|
||||||
ATTR_LABEL: f"{DEFAULT_NAME} SPS30 Particulate Matter 4.0",
|
ATTR_LABEL: f"{DEFAULT_NAME} SPS30 Particulate Matter 4.0",
|
||||||
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
ATTR_UNIT: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
|
|
@ -1,173 +0,0 @@
|
||||||
"""Test air_quality of Nettigo Air Monitor integration."""
|
|
||||||
from datetime import timedelta
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from nettigo_air_monitor import ApiError
|
|
||||||
|
|
||||||
from homeassistant.components.air_quality import (
|
|
||||||
ATTR_CO2,
|
|
||||||
ATTR_PM_2_5,
|
|
||||||
ATTR_PM_10,
|
|
||||||
DOMAIN as AIR_QUALITY_DOMAIN,
|
|
||||||
)
|
|
||||||
from homeassistant.components.nam.const import DOMAIN
|
|
||||||
from homeassistant.const import (
|
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
|
||||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
||||||
STATE_UNAVAILABLE,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers import entity_registry as er
|
|
||||||
from homeassistant.setup import async_setup_component
|
|
||||||
from homeassistant.util.dt import utcnow
|
|
||||||
|
|
||||||
from . import INCOMPLETE_NAM_DATA, nam_data
|
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
|
||||||
from tests.components.nam import init_integration
|
|
||||||
|
|
||||||
|
|
||||||
async def test_air_quality(hass):
|
|
||||||
"""Test states of the air_quality."""
|
|
||||||
await init_integration(hass)
|
|
||||||
registry = er.async_get(hass)
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert state
|
|
||||||
assert state.state == "11"
|
|
||||||
assert state.attributes.get(ATTR_PM_10) == 19
|
|
||||||
assert state.attributes.get(ATTR_PM_2_5) == 11
|
|
||||||
assert state.attributes.get(ATTR_CO2) == 865
|
|
||||||
assert (
|
|
||||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
|
||||||
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
|
||||||
)
|
|
||||||
|
|
||||||
entry = registry.async_get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert entry
|
|
||||||
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011"
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sps30")
|
|
||||||
assert state
|
|
||||||
assert state.state == "34"
|
|
||||||
assert state.attributes.get(ATTR_PM_10) == 21
|
|
||||||
assert state.attributes.get(ATTR_PM_2_5) == 34
|
|
||||||
assert state.attributes.get(ATTR_CO2) == 865
|
|
||||||
assert (
|
|
||||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
|
||||||
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
|
||||||
)
|
|
||||||
|
|
||||||
entry = registry.async_get("air_quality.nettigo_air_monitor_sps30")
|
|
||||||
assert entry
|
|
||||||
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_air_quality_without_co2_value(hass):
|
|
||||||
"""Test states of the air_quality."""
|
|
||||||
await init_integration(hass, co2_sensor=False)
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert state
|
|
||||||
assert state.attributes.get(ATTR_CO2) is None
|
|
||||||
|
|
||||||
|
|
||||||
async def test_incompleta_data_after_device_restart(hass):
|
|
||||||
"""Test states of the air_quality after device restart."""
|
|
||||||
await init_integration(hass)
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert state
|
|
||||||
assert state.state == "11"
|
|
||||||
assert state.attributes.get(ATTR_PM_10) == 19
|
|
||||||
assert state.attributes.get(ATTR_PM_2_5) == 11
|
|
||||||
assert (
|
|
||||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
|
||||||
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
|
||||||
)
|
|
||||||
|
|
||||||
future = utcnow() + timedelta(minutes=6)
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.nam.NettigoAirMonitor._async_get_data",
|
|
||||||
return_value=INCOMPLETE_NAM_DATA,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, future)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert state
|
|
||||||
assert state.state == STATE_UNAVAILABLE
|
|
||||||
|
|
||||||
|
|
||||||
async def test_availability(hass):
|
|
||||||
"""Ensure that we mark the entities unavailable correctly when device causes an error."""
|
|
||||||
await init_integration(hass)
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert state
|
|
||||||
assert state.state != STATE_UNAVAILABLE
|
|
||||||
assert state.state == "11"
|
|
||||||
|
|
||||||
future = utcnow() + timedelta(minutes=6)
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.nam.NettigoAirMonitor._async_get_data",
|
|
||||||
side_effect=ApiError("API Error"),
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, future)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert state
|
|
||||||
assert state.state == STATE_UNAVAILABLE
|
|
||||||
|
|
||||||
future = utcnow() + timedelta(minutes=12)
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.nam.NettigoAirMonitor._async_get_data",
|
|
||||||
return_value=nam_data,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, future)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert state
|
|
||||||
assert state.state != STATE_UNAVAILABLE
|
|
||||||
assert state.state == "11"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_manual_update_entity(hass):
|
|
||||||
"""Test manual update entity via service homeasasistant/update_entity."""
|
|
||||||
await init_integration(hass)
|
|
||||||
|
|
||||||
await async_setup_component(hass, "homeassistant", {})
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.nam.NettigoAirMonitor._async_get_data",
|
|
||||||
return_value=nam_data,
|
|
||||||
) as mock_get_data:
|
|
||||||
await hass.services.async_call(
|
|
||||||
"homeassistant",
|
|
||||||
"update_entity",
|
|
||||||
{ATTR_ENTITY_ID: ["air_quality.nettigo_air_monitor_sds011"]},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert mock_get_data.call_count == 1
|
|
||||||
|
|
||||||
|
|
||||||
async def test_unique_id_migration(hass):
|
|
||||||
"""Test states of the unique_id migration."""
|
|
||||||
registry = er.async_get(hass)
|
|
||||||
|
|
||||||
registry.async_get_or_create(
|
|
||||||
AIR_QUALITY_DOMAIN,
|
|
||||||
DOMAIN,
|
|
||||||
"aa:bb:cc:dd:ee:ff-sds",
|
|
||||||
suggested_object_id="nettigo_air_monitor_sds011",
|
|
||||||
disabled_by=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
await init_integration(hass)
|
|
||||||
|
|
||||||
entry = registry.async_get("air_quality.nettigo_air_monitor_sds011")
|
|
||||||
assert entry
|
|
||||||
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011"
|
|
|
@ -3,9 +3,11 @@ from unittest.mock import patch
|
||||||
|
|
||||||
from nettigo_air_monitor import ApiError
|
from nettigo_air_monitor import ApiError
|
||||||
|
|
||||||
|
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM
|
||||||
from homeassistant.components.nam.const import DOMAIN
|
from homeassistant.components.nam.const import DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.const import STATE_UNAVAILABLE
|
from homeassistant.const import STATE_UNAVAILABLE
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
from tests.components.nam import init_integration
|
from tests.components.nam import init_integration
|
||||||
|
@ -15,7 +17,7 @@ async def test_async_setup_entry(hass):
|
||||||
"""Test a successful setup entry."""
|
"""Test a successful setup entry."""
|
||||||
await init_integration(hass)
|
await init_integration(hass)
|
||||||
|
|
||||||
state = hass.states.get("air_quality.nettigo_air_monitor_sds011")
|
state = hass.states.get("sensor.nettigo_air_monitor_sds011_particulate_matter_2_5")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state != STATE_UNAVAILABLE
|
assert state.state != STATE_UNAVAILABLE
|
||||||
assert state.state == "11"
|
assert state.state == "11"
|
||||||
|
@ -51,3 +53,32 @@ async def test_unload_entry(hass):
|
||||||
|
|
||||||
assert entry.state is ConfigEntryState.NOT_LOADED
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||||
assert not hass.data.get(DOMAIN)
|
assert not hass.data.get(DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_remove_air_quality_entities(hass):
|
||||||
|
"""Test remove air_quality entities from registry."""
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
registry.async_get_or_create(
|
||||||
|
AIR_QUALITY_PLATFORM,
|
||||||
|
DOMAIN,
|
||||||
|
"aa:bb:cc:dd:ee:ff-sds011",
|
||||||
|
suggested_object_id="nettigo_air_monitor_sds011",
|
||||||
|
disabled_by=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
registry.async_get_or_create(
|
||||||
|
AIR_QUALITY_PLATFORM,
|
||||||
|
DOMAIN,
|
||||||
|
"aa:bb:cc:dd:ee:ff-sps30",
|
||||||
|
suggested_object_id="nettigo_air_monitor_sps30",
|
||||||
|
disabled_by=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
await init_integration(hass)
|
||||||
|
|
||||||
|
entry = registry.async_get("air_quality.nettigo_air_monitor_sds011")
|
||||||
|
assert entry is None
|
||||||
|
|
||||||
|
entry = registry.async_get("air_quality.nettigo_air_monitor_sps30")
|
||||||
|
assert entry is None
|
||||||
|
|
|
@ -13,7 +13,11 @@ from homeassistant.components.sensor import (
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
|
ATTR_ICON,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
|
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
CONCENTRATION_PARTS_PER_MILLION,
|
||||||
|
DEVICE_CLASS_CO2,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_PRESSURE,
|
DEVICE_CLASS_PRESSURE,
|
||||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||||
|
@ -205,6 +209,113 @@ async def test_sensor(hass):
|
||||||
assert entry
|
assert entry
|
||||||
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-uptime"
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-uptime"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.nettigo_air_monitor_sds011_particulate_matter_10")
|
||||||
|
assert state
|
||||||
|
assert state.state == "19"
|
||||||
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||||
|
)
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:blur"
|
||||||
|
|
||||||
|
entry = registry.async_get(
|
||||||
|
"sensor.nettigo_air_monitor_sds011_particulate_matter_10"
|
||||||
|
)
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_p1"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.nettigo_air_monitor_sds011_particulate_matter_2_5")
|
||||||
|
assert state
|
||||||
|
assert state.state == "11"
|
||||||
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||||
|
)
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:blur"
|
||||||
|
|
||||||
|
entry = registry.async_get(
|
||||||
|
"sensor.nettigo_air_monitor_sds011_particulate_matter_2_5"
|
||||||
|
)
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sds011_p2"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.nettigo_air_monitor_sps30_particulate_matter_1_0")
|
||||||
|
assert state
|
||||||
|
assert state.state == "31"
|
||||||
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||||
|
)
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:blur"
|
||||||
|
|
||||||
|
entry = registry.async_get(
|
||||||
|
"sensor.nettigo_air_monitor_sps30_particulate_matter_1_0"
|
||||||
|
)
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_p0"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.nettigo_air_monitor_sps30_particulate_matter_10")
|
||||||
|
assert state
|
||||||
|
assert state.state == "21"
|
||||||
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||||
|
)
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:blur"
|
||||||
|
|
||||||
|
entry = registry.async_get("sensor.nettigo_air_monitor_sps30_particulate_matter_10")
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_p1"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.nettigo_air_monitor_sps30_particulate_matter_2_5")
|
||||||
|
assert state
|
||||||
|
assert state.state == "34"
|
||||||
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||||
|
)
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:blur"
|
||||||
|
|
||||||
|
entry = registry.async_get(
|
||||||
|
"sensor.nettigo_air_monitor_sps30_particulate_matter_2_5"
|
||||||
|
)
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_p2"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.nettigo_air_monitor_sps30_particulate_matter_4_0")
|
||||||
|
assert state
|
||||||
|
assert state.state == "25"
|
||||||
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||||
|
)
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:blur"
|
||||||
|
|
||||||
|
entry = registry.async_get(
|
||||||
|
"sensor.nettigo_air_monitor_sps30_particulate_matter_4_0"
|
||||||
|
)
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-sps30_p4"
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.nettigo_air_monitor_mh_z14a_carbon_dioxide")
|
||||||
|
assert state
|
||||||
|
assert state.state == "865"
|
||||||
|
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_CO2
|
||||||
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
== CONCENTRATION_PARTS_PER_MILLION
|
||||||
|
)
|
||||||
|
entry = registry.async_get("sensor.nettigo_air_monitor_mh_z14a_carbon_dioxide")
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff-mhz14a_carbon_dioxide"
|
||||||
|
|
||||||
|
|
||||||
async def test_sensor_disabled(hass):
|
async def test_sensor_disabled(hass):
|
||||||
"""Test sensor disabled by default."""
|
"""Test sensor disabled by default."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue