mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Add Aidoo sensors to Airzone Cloud (#93541)
This commit is contained in:
parent
8b662dc94f
commit
6aa01e1441
@ -4,7 +4,13 @@ from __future__ import annotations
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Any
|
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.config_entries import ConfigEntry
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
@ -22,6 +28,36 @@ class AirzoneEntity(CoordinatorEntity[AirzoneUpdateCoordinator], ABC):
|
|||||||
"""Return Airzone Cloud entity value by key."""
|
"""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):
|
class AirzoneZoneEntity(AirzoneEntity):
|
||||||
"""Define an Airzone Cloud Zone entity."""
|
"""Define an Airzone Cloud Zone entity."""
|
||||||
|
|
||||||
@ -49,6 +85,5 @@ class AirzoneZoneEntity(AirzoneEntity):
|
|||||||
"""Return zone value by key."""
|
"""Return zone value by key."""
|
||||||
value = None
|
value = None
|
||||||
if zone := self.coordinator.data[AZD_ZONES].get(self.zone_id):
|
if zone := self.coordinator.data[AZD_ZONES].get(self.zone_id):
|
||||||
if key in zone:
|
value = zone.get(key)
|
||||||
value = zone[key]
|
|
||||||
return value
|
return value
|
||||||
|
@ -3,7 +3,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any, Final
|
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 (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
@ -18,7 +24,17 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import AirzoneUpdateCoordinator
|
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, ...]] = (
|
ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
@ -42,9 +58,25 @@ async def async_setup_entry(
|
|||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Add Airzone Cloud sensors from a config_entry."""
|
"""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 zone_id, zone_data in coordinator.data.get(AZD_ZONES, {}).items():
|
||||||
for description in ZONE_SENSOR_TYPES:
|
for description in ZONE_SENSOR_TYPES:
|
||||||
if description.key in zone_data:
|
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)
|
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):
|
class AirzoneZoneSensor(AirzoneZoneEntity, AirzoneSensor):
|
||||||
"""Define an Airzone Cloud Zone sensor."""
|
"""Define an Airzone Cloud Zone sensor."""
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ from .util import (
|
|||||||
CONFIG,
|
CONFIG,
|
||||||
GET_INSTALLATION_MOCK,
|
GET_INSTALLATION_MOCK,
|
||||||
GET_INSTALLATIONS_MOCK,
|
GET_INSTALLATIONS_MOCK,
|
||||||
GET_WEBSERVER_MOCK,
|
|
||||||
WS_ID,
|
WS_ID,
|
||||||
mock_get_device_status,
|
mock_get_device_status,
|
||||||
|
mock_get_webserver,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||||||
return_value=GET_INSTALLATIONS_MOCK,
|
return_value=GET_INSTALLATIONS_MOCK,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||||
return_value=GET_WEBSERVER_MOCK,
|
side_effect=mock_get_webserver,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
@ -98,7 +98,7 @@ async def test_installations_list_error(hass: HomeAssistant) -> None:
|
|||||||
side_effect=AirzoneCloudError,
|
side_effect=AirzoneCloudError,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||||
return_value=GET_WEBSERVER_MOCK,
|
side_effect=mock_get_webserver,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
|
@ -14,8 +14,8 @@ from .util import (
|
|||||||
CONFIG,
|
CONFIG,
|
||||||
GET_INSTALLATION_MOCK,
|
GET_INSTALLATION_MOCK,
|
||||||
GET_INSTALLATIONS_MOCK,
|
GET_INSTALLATIONS_MOCK,
|
||||||
GET_WEBSERVER_MOCK,
|
|
||||||
mock_get_device_status,
|
mock_get_device_status,
|
||||||
|
mock_get_webserver,
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
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,
|
return_value=GET_INSTALLATIONS_MOCK,
|
||||||
) as mock_installations, patch(
|
) as mock_installations, patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||||
return_value=GET_WEBSERVER_MOCK,
|
side_effect=mock_get_webserver,
|
||||||
) as mock_webserver, patch(
|
) as mock_webserver, patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
@ -53,7 +53,7 @@ async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None:
|
|||||||
mock_device_status.assert_called()
|
mock_device_status.assert_called()
|
||||||
mock_installation.assert_awaited_once()
|
mock_installation.assert_awaited_once()
|
||||||
mock_installations.assert_called_once()
|
mock_installations.assert_called_once()
|
||||||
mock_webserver.assert_called_once()
|
mock_webserver.assert_called()
|
||||||
|
|
||||||
mock_device_status.reset_mock()
|
mock_device_status.reset_mock()
|
||||||
mock_installation.reset_mock()
|
mock_installation.reset_mock()
|
||||||
|
@ -7,6 +7,7 @@ from aioairzone_cloud.const import (
|
|||||||
API_DEVICES,
|
API_DEVICES,
|
||||||
API_GROUPS,
|
API_GROUPS,
|
||||||
API_WS_ID,
|
API_WS_ID,
|
||||||
|
AZD_AIDOOS,
|
||||||
AZD_INSTALLATIONS,
|
AZD_INSTALLATIONS,
|
||||||
AZD_SYSTEMS,
|
AZD_SYSTEMS,
|
||||||
AZD_WEBSERVERS,
|
AZD_WEBSERVERS,
|
||||||
@ -109,6 +110,7 @@ async def test_config_entry_diagnostics(
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert list(diag["coord_data"]) >= [
|
assert list(diag["coord_data"]) >= [
|
||||||
|
AZD_AIDOOS,
|
||||||
AZD_INSTALLATIONS,
|
AZD_INSTALLATIONS,
|
||||||
AZD_SYSTEMS,
|
AZD_SYSTEMS,
|
||||||
AZD_WEBSERVERS,
|
AZD_WEBSERVERS,
|
||||||
|
@ -12,6 +12,10 @@ async def test_airzone_create_sensors(
|
|||||||
|
|
||||||
await async_init_integration(hass)
|
await async_init_integration(hass)
|
||||||
|
|
||||||
|
# Aidoos
|
||||||
|
state = hass.states.get("sensor.bron_temperature")
|
||||||
|
assert state.state == "21.0"
|
||||||
|
|
||||||
# Zones
|
# Zones
|
||||||
state = hass.states.get("sensor.dormitorio_temperature")
|
state = hass.states.get("sensor.dormitorio_temperature")
|
||||||
assert state.state == "25.0"
|
assert state.state == "25.0"
|
||||||
|
@ -4,6 +4,7 @@ from typing import Any
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from aioairzone_cloud.const import (
|
from aioairzone_cloud.const import (
|
||||||
|
API_AZ_AIDOO,
|
||||||
API_AZ_SYSTEM,
|
API_AZ_SYSTEM,
|
||||||
API_AZ_ZONE,
|
API_AZ_ZONE,
|
||||||
API_CELSIUS,
|
API_CELSIUS,
|
||||||
@ -38,6 +39,7 @@ from aioairzone_cloud.const import (
|
|||||||
API_ZONE_NUMBER,
|
API_ZONE_NUMBER,
|
||||||
)
|
)
|
||||||
from aioairzone_cloud.device import Device
|
from aioairzone_cloud.device import Device
|
||||||
|
from aioairzone_cloud.webserver import WebServer
|
||||||
|
|
||||||
from homeassistant.components.airzone_cloud import DOMAIN
|
from homeassistant.components.airzone_cloud import DOMAIN
|
||||||
from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME
|
||||||
@ -46,6 +48,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
WS_ID = "11:22:33:44:55:66"
|
WS_ID = "11:22:33:44:55:66"
|
||||||
|
WS_ID_AIDOO = "11:22:33:44:55:67"
|
||||||
|
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
CONF_ID: "inst1",
|
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_NAME: "House",
|
||||||
API_WS_IDS: [
|
API_WS_IDS: [
|
||||||
WS_ID,
|
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]:
|
def mock_get_device_status(device: Device) -> dict[str, Any]:
|
||||||
"""Mock API device status."""
|
"""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":
|
if device.get_id() == "system1":
|
||||||
return {
|
return {
|
||||||
API_ERRORS: [],
|
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(
|
async def async_init_integration(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -174,7 +225,7 @@ async def async_init_integration(
|
|||||||
return_value=GET_INSTALLATIONS_MOCK,
|
return_value=GET_INSTALLATIONS_MOCK,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.api_get_webserver",
|
||||||
return_value=GET_WEBSERVER_MOCK,
|
side_effect=mock_get_webserver,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
"homeassistant.components.airzone_cloud.AirzoneCloudApi.login",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user