Skip to content

Commit 87b9c31

Browse files
authored
Add sensor entities to Airobot integration (home-assistant#157938)
1 parent 061c38d commit 87b9c31

File tree

8 files changed

+439
-7
lines changed

8 files changed

+439
-7
lines changed

homeassistant/components/airobot/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from .coordinator import AirobotConfigEntry, AirobotDataUpdateCoordinator
99

10-
PLATFORMS: list[Platform] = [Platform.CLIMATE]
10+
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SENSOR]
1111

1212

1313
async def async_setup_entry(hass: HomeAssistant, entry: AirobotConfigEntry) -> bool:

homeassistant/components/airobot/quality_scale.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ rules:
4444
discovery: done
4545
docs-data-update: done
4646
docs-examples: todo
47-
docs-known-limitations: todo
47+
docs-known-limitations: done
4848
docs-supported-devices: done
4949
docs-supported-functions: done
5050
docs-troubleshooting: done
@@ -54,7 +54,7 @@ rules:
5454
comment: Single device integration, no dynamic device discovery needed.
5555
entity-category: done
5656
entity-device-class: done
57-
entity-disabled-by-default: todo
57+
entity-disabled-by-default: done
5858
entity-translations: todo
5959
exception-translations: done
6060
icon-translations: todo
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""Sensor platform for Airobot thermostat."""
2+
3+
from __future__ import annotations
4+
5+
from collections.abc import Callable
6+
from dataclasses import dataclass
7+
8+
from pyairobotrest.models import ThermostatStatus
9+
10+
from homeassistant.components.sensor import (
11+
SensorDeviceClass,
12+
SensorEntity,
13+
SensorEntityDescription,
14+
SensorStateClass,
15+
)
16+
from homeassistant.const import (
17+
CONCENTRATION_PARTS_PER_MILLION,
18+
PERCENTAGE,
19+
EntityCategory,
20+
UnitOfTemperature,
21+
UnitOfTime,
22+
)
23+
from homeassistant.core import HomeAssistant
24+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
25+
from homeassistant.helpers.typing import StateType
26+
27+
from . import AirobotConfigEntry
28+
from .entity import AirobotEntity
29+
30+
PARALLEL_UPDATES = 0
31+
32+
33+
@dataclass(frozen=True, kw_only=True)
34+
class AirobotSensorEntityDescription(SensorEntityDescription):
35+
"""Describes Airobot sensor entity."""
36+
37+
value_fn: Callable[[ThermostatStatus], StateType]
38+
supported_fn: Callable[[ThermostatStatus], bool] = lambda _: True
39+
40+
41+
SENSOR_TYPES: tuple[AirobotSensorEntityDescription, ...] = (
42+
AirobotSensorEntityDescription(
43+
key="air_temperature",
44+
translation_key="air_temperature",
45+
device_class=SensorDeviceClass.TEMPERATURE,
46+
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
47+
state_class=SensorStateClass.MEASUREMENT,
48+
value_fn=lambda status: status.temp_air,
49+
),
50+
AirobotSensorEntityDescription(
51+
key="humidity",
52+
device_class=SensorDeviceClass.HUMIDITY,
53+
native_unit_of_measurement=PERCENTAGE,
54+
state_class=SensorStateClass.MEASUREMENT,
55+
value_fn=lambda status: status.hum_air,
56+
),
57+
AirobotSensorEntityDescription(
58+
key="floor_temperature",
59+
translation_key="floor_temperature",
60+
device_class=SensorDeviceClass.TEMPERATURE,
61+
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
62+
state_class=SensorStateClass.MEASUREMENT,
63+
value_fn=lambda status: status.temp_floor,
64+
supported_fn=lambda status: status.has_floor_sensor,
65+
),
66+
AirobotSensorEntityDescription(
67+
key="co2",
68+
device_class=SensorDeviceClass.CO2,
69+
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
70+
state_class=SensorStateClass.MEASUREMENT,
71+
value_fn=lambda status: status.co2,
72+
supported_fn=lambda status: status.has_co2_sensor,
73+
),
74+
AirobotSensorEntityDescription(
75+
key="air_quality_index",
76+
device_class=SensorDeviceClass.AQI,
77+
state_class=SensorStateClass.MEASUREMENT,
78+
value_fn=lambda status: status.aqi,
79+
supported_fn=lambda status: status.has_co2_sensor,
80+
),
81+
AirobotSensorEntityDescription(
82+
key="heating_uptime",
83+
translation_key="heating_uptime",
84+
device_class=SensorDeviceClass.DURATION,
85+
native_unit_of_measurement=UnitOfTime.SECONDS,
86+
suggested_unit_of_measurement=UnitOfTime.HOURS,
87+
state_class=SensorStateClass.TOTAL_INCREASING,
88+
entity_category=EntityCategory.DIAGNOSTIC,
89+
value_fn=lambda status: status.heating_uptime,
90+
entity_registry_enabled_default=False,
91+
),
92+
AirobotSensorEntityDescription(
93+
key="errors",
94+
translation_key="errors",
95+
state_class=SensorStateClass.MEASUREMENT,
96+
entity_category=EntityCategory.DIAGNOSTIC,
97+
value_fn=lambda status: status.errors,
98+
),
99+
)
100+
101+
102+
async def async_setup_entry(
103+
hass: HomeAssistant,
104+
entry: AirobotConfigEntry,
105+
async_add_entities: AddConfigEntryEntitiesCallback,
106+
) -> None:
107+
"""Set up Airobot sensor platform."""
108+
coordinator = entry.runtime_data
109+
async_add_entities(
110+
AirobotSensor(coordinator, description)
111+
for description in SENSOR_TYPES
112+
if description.supported_fn(coordinator.data.status)
113+
)
114+
115+
116+
class AirobotSensor(AirobotEntity, SensorEntity):
117+
"""Representation of an Airobot sensor."""
118+
119+
entity_description: AirobotSensorEntityDescription
120+
121+
def __init__(
122+
self,
123+
coordinator,
124+
description: AirobotSensorEntityDescription,
125+
) -> None:
126+
"""Initialize the sensor."""
127+
super().__init__(coordinator)
128+
self.entity_description = description
129+
self._attr_unique_id = f"{coordinator.data.status.device_id}_{description.key}"
130+
131+
@property
132+
def native_value(self) -> StateType:
133+
"""Return the state of the sensor."""
134+
return self.entity_description.value_fn(self.coordinator.data.status)

homeassistant/components/airobot/strings.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,25 @@
4343
}
4444
}
4545
},
46+
"entity": {
47+
"sensor": {
48+
"air_temperature": {
49+
"name": "Air temperature"
50+
},
51+
"device_uptime": {
52+
"name": "Device uptime"
53+
},
54+
"errors": {
55+
"name": "Error count"
56+
},
57+
"floor_temperature": {
58+
"name": "Floor temperature"
59+
},
60+
"heating_uptime": {
61+
"name": "Heating uptime"
62+
}
63+
}
64+
},
4665
"exceptions": {
4766
"authentication_failed": {
4867
"message": "Authentication failed, please reauthenticate."

tests/components/airobot/conftest.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212
import pytest
1313

1414
from homeassistant.components.airobot.const import DOMAIN
15-
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD, CONF_USERNAME
15+
from homeassistant.const import (
16+
CONF_HOST,
17+
CONF_MAC,
18+
CONF_PASSWORD,
19+
CONF_USERNAME,
20+
Platform,
21+
)
1622
from homeassistant.core import HomeAssistant
1723

1824
from tests.common import MockConfigEntry
@@ -105,16 +111,24 @@ def mock_config_entry() -> MockConfigEntry:
105111
)
106112

107113

114+
@pytest.fixture
115+
def platforms() -> list[Platform]:
116+
"""Fixture to specify platforms to test."""
117+
return [Platform.CLIMATE, Platform.SENSOR]
118+
119+
108120
@pytest.fixture
109121
async def init_integration(
110122
hass: HomeAssistant,
111123
mock_config_entry: MockConfigEntry,
112124
mock_airobot_client: AsyncMock,
125+
platforms: list[Platform],
113126
) -> MockConfigEntry:
114127
"""Set up the Airobot integration for testing."""
115128
mock_config_entry.add_to_hass(hass)
116129

117-
await hass.config_entries.async_setup(mock_config_entry.entry_id)
118-
await hass.async_block_till_done()
130+
with patch("homeassistant.components.airobot.PLATFORMS", platforms):
131+
await hass.config_entries.async_setup(mock_config_entry.entry_id)
132+
await hass.async_block_till_done()
119133

120134
return mock_config_entry

0 commit comments

Comments
 (0)