mirror of
https://github.com/home-assistant/core.git
synced 2025-04-22 16:27:56 +00:00
Add Airly gas sensors (#77908)
* Add support for gases queryable via Airly API: CO, NO₂, O₃, SO₂ * Add tests for above sensors and update test fixtures
This commit is contained in:
parent
06178d3446
commit
84cd0da26b
@ -7,11 +7,15 @@ ATTR_API_ADVICE: Final = "ADVICE"
|
||||
ATTR_API_CAQI: Final = "CAQI"
|
||||
ATTR_API_CAQI_DESCRIPTION: Final = "DESCRIPTION"
|
||||
ATTR_API_CAQI_LEVEL: Final = "LEVEL"
|
||||
ATTR_API_CO: Final = "CO"
|
||||
ATTR_API_HUMIDITY: Final = "HUMIDITY"
|
||||
ATTR_API_NO2: Final = "NO2"
|
||||
ATTR_API_O3: Final = "O3"
|
||||
ATTR_API_PM10: Final = "PM10"
|
||||
ATTR_API_PM1: Final = "PM1"
|
||||
ATTR_API_PM25: Final = "PM25"
|
||||
ATTR_API_PRESSURE: Final = "PRESSURE"
|
||||
ATTR_API_SO2: Final = "SO2"
|
||||
ATTR_API_TEMPERATURE: Final = "TEMPERATURE"
|
||||
|
||||
ATTR_ADVICE: Final = "advice"
|
||||
|
@ -33,11 +33,15 @@ from .const import (
|
||||
ATTR_API_CAQI,
|
||||
ATTR_API_CAQI_DESCRIPTION,
|
||||
ATTR_API_CAQI_LEVEL,
|
||||
ATTR_API_CO,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_NO2,
|
||||
ATTR_API_O3,
|
||||
ATTR_API_PM1,
|
||||
ATTR_API_PM10,
|
||||
ATTR_API_PM25,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_SO2,
|
||||
ATTR_API_TEMPERATURE,
|
||||
ATTR_DESCRIPTION,
|
||||
ATTR_LEVEL,
|
||||
@ -112,6 +116,34 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda value: round(value, 1),
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_CO,
|
||||
device_class=SensorDeviceClass.CO,
|
||||
name=ATTR_API_CO,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_NO2,
|
||||
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
|
||||
name=ATTR_API_NO2,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_SO2,
|
||||
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
|
||||
name=ATTR_API_SO2,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_O3,
|
||||
device_class=SensorDeviceClass.OZONE,
|
||||
name=ATTR_API_O3,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@ -191,4 +223,32 @@ class AirlySensor(CoordinatorEntity[AirlyDataUpdateCoordinator], SensorEntity):
|
||||
self._attrs[ATTR_PERCENT] = round(
|
||||
self.coordinator.data[f"{ATTR_API_PM10}_{SUFFIX_PERCENT}"]
|
||||
)
|
||||
if self.entity_description.key == ATTR_API_CO:
|
||||
self._attrs[ATTR_LIMIT] = self.coordinator.data[
|
||||
f"{ATTR_API_CO}_{SUFFIX_LIMIT}"
|
||||
]
|
||||
self._attrs[ATTR_PERCENT] = round(
|
||||
self.coordinator.data[f"{ATTR_API_CO}_{SUFFIX_PERCENT}"]
|
||||
)
|
||||
if self.entity_description.key == ATTR_API_NO2:
|
||||
self._attrs[ATTR_LIMIT] = self.coordinator.data[
|
||||
f"{ATTR_API_NO2}_{SUFFIX_LIMIT}"
|
||||
]
|
||||
self._attrs[ATTR_PERCENT] = round(
|
||||
self.coordinator.data[f"{ATTR_API_NO2}_{SUFFIX_PERCENT}"]
|
||||
)
|
||||
if self.entity_description.key == ATTR_API_SO2:
|
||||
self._attrs[ATTR_LIMIT] = self.coordinator.data[
|
||||
f"{ATTR_API_SO2}_{SUFFIX_LIMIT}"
|
||||
]
|
||||
self._attrs[ATTR_PERCENT] = round(
|
||||
self.coordinator.data[f"{ATTR_API_SO2}_{SUFFIX_PERCENT}"]
|
||||
)
|
||||
if self.entity_description.key == ATTR_API_O3:
|
||||
self._attrs[ATTR_LIMIT] = self.coordinator.data[
|
||||
f"{ATTR_API_O3}_{SUFFIX_LIMIT}"
|
||||
]
|
||||
self._attrs[ATTR_PERCENT] = round(
|
||||
self.coordinator.data[f"{ATTR_API_O3}_{SUFFIX_PERCENT}"]
|
||||
)
|
||||
return self._attrs
|
||||
|
@ -1,16 +1,28 @@
|
||||
{
|
||||
"PM1": 9.23,
|
||||
"PM25": 13.71,
|
||||
"PM10": 18.58,
|
||||
"PRESSURE": 1000.87,
|
||||
"HUMIDITY": 92.84,
|
||||
"TEMPERATURE": 14.23,
|
||||
"PM25_LIMIT": 25.0,
|
||||
"PM25_PERCENT": 54.84,
|
||||
"PM10_LIMIT": 50.0,
|
||||
"PM10_PERCENT": 37.17,
|
||||
"CAQI": 22.85,
|
||||
"PM1": 2.83,
|
||||
"PM25": 4.37,
|
||||
"PM10": 6.06,
|
||||
"CO": 162.49,
|
||||
"NO2": 16.04,
|
||||
"O3": 41.52,
|
||||
"SO2": 13.97,
|
||||
"PRESSURE": 1019.86,
|
||||
"HUMIDITY": 68.35,
|
||||
"TEMPERATURE": 14.37,
|
||||
"PM25_LIMIT": 15.0,
|
||||
"PM25_PERCENT": 29.13,
|
||||
"PM10_LIMIT": 45.0,
|
||||
"PM10_PERCENT": 14.5,
|
||||
"CO_LIMIT": 4000,
|
||||
"CO_PERCENT": 4.06,
|
||||
"NO2_LIMIT": 25,
|
||||
"NO2_PERCENT": 64.17,
|
||||
"O3_LIMIT": 100,
|
||||
"O3_PERCENT": 41.52,
|
||||
"SO2_LIMIT": 40,
|
||||
"SO2_PERCENT": 34.93,
|
||||
"CAQI": 7.29,
|
||||
"LEVEL": "very low",
|
||||
"DESCRIPTION": "Great air here today!",
|
||||
"ADVICE": "Great air!"
|
||||
"ADVICE": "Catch your breath!"
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,7 @@ async def test_async_setup_entry(hass, aioclient_mock):
|
||||
state = hass.states.get("sensor.home_pm2_5")
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "14"
|
||||
assert state.state == "4"
|
||||
|
||||
|
||||
async def test_config_not_ready(hass, aioclient_mock):
|
||||
|
@ -35,7 +35,7 @@ async def test_sensor(hass, aioclient_mock):
|
||||
|
||||
state = hass.states.get("sensor.home_caqi")
|
||||
assert state
|
||||
assert state.state == "23"
|
||||
assert state.state == "7"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "CAQI"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.AQI
|
||||
@ -46,7 +46,7 @@ async def test_sensor(hass, aioclient_mock):
|
||||
|
||||
state = hass.states.get("sensor.home_humidity")
|
||||
assert state
|
||||
assert state.state == "92.8"
|
||||
assert state.state == "68.3"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.HUMIDITY
|
||||
@ -58,7 +58,7 @@ async def test_sensor(hass, aioclient_mock):
|
||||
|
||||
state = hass.states.get("sensor.home_pm1")
|
||||
assert state
|
||||
assert state.state == "9"
|
||||
assert state.state == "3"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
@ -73,7 +73,7 @@ async def test_sensor(hass, aioclient_mock):
|
||||
|
||||
state = hass.states.get("sensor.home_pm2_5")
|
||||
assert state
|
||||
assert state.state == "14"
|
||||
assert state.state == "4"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
@ -88,7 +88,7 @@ async def test_sensor(hass, aioclient_mock):
|
||||
|
||||
state = hass.states.get("sensor.home_pm10")
|
||||
assert state
|
||||
assert state.state == "19"
|
||||
assert state.state == "6"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
@ -101,9 +101,69 @@ async def test_sensor(hass, aioclient_mock):
|
||||
assert entry
|
||||
assert entry.unique_id == "123-456-pm10"
|
||||
|
||||
state = hass.states.get("sensor.home_co")
|
||||
assert state
|
||||
assert state.state == "162"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.CO
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = registry.async_get("sensor.home_co")
|
||||
assert entry
|
||||
assert entry.unique_id == "123-456-co"
|
||||
|
||||
state = hass.states.get("sensor.home_no2")
|
||||
assert state
|
||||
assert state.state == "16"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.NITROGEN_DIOXIDE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = registry.async_get("sensor.home_no2")
|
||||
assert entry
|
||||
assert entry.unique_id == "123-456-no2"
|
||||
|
||||
state = hass.states.get("sensor.home_o3")
|
||||
assert state
|
||||
assert state.state == "42"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.OZONE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = registry.async_get("sensor.home_o3")
|
||||
assert entry
|
||||
assert entry.unique_id == "123-456-o3"
|
||||
|
||||
state = hass.states.get("sensor.home_so2")
|
||||
assert state
|
||||
assert state.state == "14"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
)
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.SULPHUR_DIOXIDE
|
||||
assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT
|
||||
|
||||
entry = registry.async_get("sensor.home_so2")
|
||||
assert entry
|
||||
assert entry.unique_id == "123-456-so2"
|
||||
|
||||
state = hass.states.get("sensor.home_pressure")
|
||||
assert state
|
||||
assert state.state == "1001"
|
||||
assert state.state == "1020"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_HPA
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE
|
||||
@ -115,7 +175,7 @@ async def test_sensor(hass, aioclient_mock):
|
||||
|
||||
state = hass.states.get("sensor.home_temperature")
|
||||
assert state
|
||||
assert state.state == "14.2"
|
||||
assert state.state == "14.4"
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
|
||||
@ -133,7 +193,7 @@ async def test_availability(hass, aioclient_mock):
|
||||
state = hass.states.get("sensor.home_humidity")
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "92.8"
|
||||
assert state.state == "68.3"
|
||||
|
||||
aioclient_mock.clear_requests()
|
||||
aioclient_mock.get(API_POINT_URL, exc=ConnectionError())
|
||||
@ -154,7 +214,7 @@ async def test_availability(hass, aioclient_mock):
|
||||
state = hass.states.get("sensor.home_humidity")
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "92.8"
|
||||
assert state.state == "68.3"
|
||||
|
||||
|
||||
async def test_manual_update_entity(hass, aioclient_mock):
|
||||
|
Loading…
x
Reference in New Issue
Block a user