Add Aidoo sensors to Airzone Cloud (#93541)
This commit is contained in:
parent
8b662dc94f
commit
6aa01e1441
7 changed files with 159 additions and 14 deletions
|
@ -4,7 +4,13 @@ from __future__ import annotations
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
from aioairzone_cloud.const import AZD_NAME, AZD_SYSTEM_ID, AZD_ZONES
|
||||
from aioairzone_cloud.const import (
|
||||
AZD_AIDOOS,
|
||||
AZD_NAME,
|
||||
AZD_SYSTEM_ID,
|
||||
AZD_WEBSERVER,
|
||||
AZD_ZONES,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
|
@ -22,6 +28,36 @@ class AirzoneEntity(CoordinatorEntity[AirzoneUpdateCoordinator], ABC):
|
|||
"""Return Airzone Cloud entity value by key."""
|
||||
|
||||
|
||||
class AirzoneAidooEntity(AirzoneEntity):
|
||||
"""Define an Airzone Cloud Aidoo entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
aidoo_id: str,
|
||||
aidoo_data: dict[str, Any],
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self.aidoo_id = aidoo_id
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, f"{entry.unique_id}_{aidoo_id}")},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=aidoo_data[AZD_NAME],
|
||||
via_device=(DOMAIN, f"{entry.unique_id}_{aidoo_data[AZD_WEBSERVER]}"),
|
||||
)
|
||||
|
||||
def get_airzone_value(self, key: str) -> Any:
|
||||
"""Return Aidoo value by key."""
|
||||
value = None
|
||||
if aidoo := self.coordinator.data[AZD_AIDOOS].get(self.aidoo_id):
|
||||
value = aidoo.get(key)
|
||||
return value
|
||||
|
||||
|
||||
class AirzoneZoneEntity(AirzoneEntity):
|
||||
"""Define an Airzone Cloud Zone entity."""
|
||||
|
||||
|
@ -49,6 +85,5 @@ class AirzoneZoneEntity(AirzoneEntity):
|
|||
"""Return zone value by key."""
|
||||
value = None
|
||||
if zone := self.coordinator.data[AZD_ZONES].get(self.zone_id):
|
||||
if key in zone:
|
||||
value = zone[key]
|
||||
value = zone.get(key)
|
||||
return value
|
||||
|
|
|
@ -3,7 +3,13 @@ from __future__ import annotations
|
|||
|
||||
from typing import Any, Final
|
||||
|
||||
from aioairzone_cloud.const import AZD_HUMIDITY, AZD_NAME, AZD_TEMP, AZD_ZONES
|
||||
from aioairzone_cloud.const import (
|
||||
AZD_AIDOOS,
|
||||
AZD_HUMIDITY,
|
||||
AZD_NAME,
|
||||
AZD_TEMP,
|
||||
AZD_ZONES,
|
||||
)
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
|
@ -18,7 +24,17 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import AirzoneUpdateCoordinator
|
||||
from .entity import AirzoneEntity, AirzoneZoneEntity
|
||||
from .entity import AirzoneAidooEntity, AirzoneEntity, AirzoneZoneEntity
|
||||
|
||||
AIDOO_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
|
||||
SensorEntityDescription(
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
key=AZD_TEMP,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
)
|
||||
|
||||
ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
|
||||
SensorEntityDescription(
|
||||
|
@ -42,9 +58,25 @@ async def async_setup_entry(
|
|||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Add Airzone Cloud sensors from a config_entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
coordinator: AirzoneUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
sensors = []
|
||||
sensors: list[AirzoneSensor] = []
|
||||
|
||||
# Aidoos
|
||||
for aidoo_id, aidoo_data in coordinator.data.get(AZD_AIDOOS, {}).items():
|
||||
for description in AIDOO_SENSOR_TYPES:
|
||||
if description.key in aidoo_data:
|
||||
sensors.append(
|
||||
AirzoneAidooSensor(
|
||||
coordinator,
|
||||
description,
|
||||
entry,
|
||||
aidoo_id,
|
||||
aidoo_data,
|
||||
)
|
||||
)
|
||||
|
||||
# Zones
|
||||
for zone_id, zone_data in coordinator.data.get(AZD_ZONES, {}).items():
|
||||
for description in ZONE_SENSOR_TYPES:
|
||||
if description.key in zone_data:
|
||||
|
@ -76,6 +108,27 @@ class AirzoneSensor(AirzoneEntity, SensorEntity):
|
|||
self._attr_native_value = self.get_airzone_value(self.entity_description.key)
|
||||
|
||||
|
||||
class AirzoneAidooSensor(AirzoneAidooEntity, AirzoneSensor):
|
||||
"""Define an Airzone Cloud Aidoo sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AirzoneUpdateCoordinator,
|
||||
description: SensorEntityDescription,
|
||||
entry: ConfigEntry,
|
||||
aidoo_id: str,
|
||||
aidoo_data: dict[str, Any],
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator, entry, aidoo_id, aidoo_data)
|
||||
|
||||
self._attr_name = f"{aidoo_data[AZD_NAME]} {description.name}"
|
||||
self._attr_unique_id = f"{entry.unique_id}_{aidoo_id}_{description.key}"
|
||||
self.entity_description = description
|
||||
|
||||
self._async_update_attrs()
|
||||
|
||||
|
||||
class AirzoneZoneSensor(AirzoneZoneEntity, AirzoneSensor):
|
||||
"""Define an Airzone Cloud Zone sensor."""
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ from .util import (
|
|||
CONFIG,
|
||||
GET_INSTALLATION_MOCK,
|
||||
GET_INSTALLATIONS_MOCK,
|
||||
GET_WEBSERVER_MOCK,
|
||||
WS_ID,
|
||||
mock_get_device_status,
|
||||
mock_get_webserver,
|
||||
)
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||
return_value=GET_INSTALLATIONS_MOCK,
|
||||
), patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||
return_value=GET_WEBSERVER_MOCK,
|
||||
side_effect=mock_get_webserver,
|
||||
), patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||
return_value=None,
|
||||
|
@ -98,7 +98,7 @@ async def test_installations_list_error(hass: HomeAssistant) -> None:
|
|||
side_effect=AirzoneCloudError,
|
||||
), patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||
return_value=GET_WEBSERVER_MOCK,
|
||||
side_effect=mock_get_webserver,
|
||||
), patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||
return_value=None,
|
||||
|
|
|
@ -14,8 +14,8 @@ from .util import (
|
|||
CONFIG,
|
||||
GET_INSTALLATION_MOCK,
|
||||
GET_INSTALLATIONS_MOCK,
|
||||
GET_WEBSERVER_MOCK,
|
||||
mock_get_device_status,
|
||||
mock_get_webserver,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
@ -42,7 +42,7 @@ async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None:
|
|||
return_value=GET_INSTALLATIONS_MOCK,
|
||||
) as mock_installations, patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||
return_value=GET_WEBSERVER_MOCK,
|
||||
side_effect=mock_get_webserver,
|
||||
) as mock_webserver, patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||
return_value=None,
|
||||
|
@ -53,7 +53,7 @@ async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None:
|
|||
mock_device_status.assert_called()
|
||||
mock_installation.assert_awaited_once()
|
||||
mock_installations.assert_called_once()
|
||||
mock_webserver.assert_called_once()
|
||||
mock_webserver.assert_called()
|
||||
|
||||
mock_device_status.reset_mock()
|
||||
mock_installation.reset_mock()
|
||||
|
|
|
@ -7,6 +7,7 @@ from aioairzone_cloud.const import (
|
|||
API_DEVICES,
|
||||
API_GROUPS,
|
||||
API_WS_ID,
|
||||
AZD_AIDOOS,
|
||||
AZD_INSTALLATIONS,
|
||||
AZD_SYSTEMS,
|
||||
AZD_WEBSERVERS,
|
||||
|
@ -109,6 +110,7 @@ async def test_config_entry_diagnostics(
|
|||
)
|
||||
|
||||
assert list(diag["coord_data"]) >= [
|
||||
AZD_AIDOOS,
|
||||
AZD_INSTALLATIONS,
|
||||
AZD_SYSTEMS,
|
||||
AZD_WEBSERVERS,
|
||||
|
|
|
@ -12,6 +12,10 @@ async def test_airzone_create_sensors(
|
|||
|
||||
await async_init_integration(hass)
|
||||
|
||||
# Aidoos
|
||||
state = hass.states.get("sensor.bron_temperature")
|
||||
assert state.state == "21.0"
|
||||
|
||||
# Zones
|
||||
state = hass.states.get("sensor.dormitorio_temperature")
|
||||
assert state.state == "25.0"
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import Any
|
|||
from unittest.mock import patch
|
||||
|
||||
from aioairzone_cloud.const import (
|
||||
API_AZ_AIDOO,
|
||||
API_AZ_SYSTEM,
|
||||
API_AZ_ZONE,
|
||||
API_CELSIUS,
|
||||
|
@ -38,6 +39,7 @@ from aioairzone_cloud.const import (
|
|||
API_ZONE_NUMBER,
|
||||
)
|
||||
from aioairzone_cloud.device import Device
|
||||
from aioairzone_cloud.webserver import WebServer
|
||||
|
||||
from homeassistant.components.airzone_cloud import DOMAIN
|
||||
from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME
|
||||
|
@ -46,6 +48,7 @@ from homeassistant.core import HomeAssistant
|
|||
from tests.common import MockConfigEntry
|
||||
|
||||
WS_ID = "11:22:33:44:55:66"
|
||||
WS_ID_AIDOO = "11:22:33:44:55:67"
|
||||
|
||||
CONFIG = {
|
||||
CONF_ID: "inst1",
|
||||
|
@ -88,6 +91,17 @@ GET_INSTALLATION_MOCK = {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
API_NAME: "Aidoo Group",
|
||||
API_DEVICES: [
|
||||
{
|
||||
API_DEVICE_ID: "aidoo1",
|
||||
API_NAME: "Bron",
|
||||
API_TYPE: API_AZ_AIDOO,
|
||||
API_WS_ID: WS_ID_AIDOO,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -98,6 +112,7 @@ GET_INSTALLATIONS_MOCK = {
|
|||
API_NAME: "House",
|
||||
API_WS_IDS: [
|
||||
WS_ID,
|
||||
WS_ID_AIDOO,
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -120,10 +135,37 @@ GET_WEBSERVER_MOCK = {
|
|||
},
|
||||
}
|
||||
|
||||
GET_WEBSERVER_MOCK_AIDOO = {
|
||||
API_WS_TYPE: "ws_aidoo",
|
||||
API_CONFIG: {
|
||||
API_WS_FW: "3.13",
|
||||
API_STAT_SSID: "Wifi",
|
||||
API_STAT_CHANNEL: 1,
|
||||
API_STAT_AP_MAC: "00:00:00:00:00:01",
|
||||
},
|
||||
API_STATUS: {
|
||||
API_IS_CONNECTED: True,
|
||||
API_STAT_QUALITY: 4,
|
||||
API_STAT_RSSI: -77,
|
||||
API_CONNECTION_DATE: "2023-05-24 17:00:52 +0200",
|
||||
API_DISCONNECTION_DATE: "2023-05-24 17:00:25 +0200",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def mock_get_device_status(device: Device) -> dict[str, Any]:
|
||||
"""Mock API device status."""
|
||||
|
||||
if device.get_id() == "aidoo1":
|
||||
return {
|
||||
API_ERRORS: [],
|
||||
API_IS_CONNECTED: True,
|
||||
API_LOCAL_TEMP: {
|
||||
API_CELSIUS: 21,
|
||||
API_FAH: 70,
|
||||
},
|
||||
API_WARNINGS: [],
|
||||
}
|
||||
if device.get_id() == "system1":
|
||||
return {
|
||||
API_ERRORS: [],
|
||||
|
@ -151,6 +193,15 @@ def mock_get_device_status(device: Device) -> dict[str, Any]:
|
|||
}
|
||||
|
||||
|
||||
def mock_get_webserver(webserver: WebServer, devices: bool) -> dict[str, Any]:
|
||||
"""Mock API get webserver."""
|
||||
|
||||
if webserver.get_id() == WS_ID_AIDOO:
|
||||
return GET_WEBSERVER_MOCK_AIDOO
|
||||
|
||||
return GET_WEBSERVER_MOCK
|
||||
|
||||
|
||||
async def async_init_integration(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
|
@ -174,7 +225,7 @@ async def async_init_integration(
|
|||
return_value=GET_INSTALLATIONS_MOCK,
|
||||
), patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||
return_value=GET_WEBSERVER_MOCK,
|
||||
side_effect=mock_get_webserver,
|
||||
), patch(
|
||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||
return_value=None,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue