From e8a8d352898bd025f0fd57f5c574b5f6ef4c0309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sat, 14 May 2022 02:23:18 +0200 Subject: [PATCH] Add Sensors for Airzone WebServer (#69748) --- homeassistant/components/airzone/entity.py | 67 +++++++++++++++------- homeassistant/components/airzone/sensor.py | 61 +++++++++++++++++++- tests/components/airzone/test_sensor.py | 11 +++- tests/components/airzone/util.py | 3 +- 4 files changed, 116 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/airzone/entity.py b/homeassistant/components/airzone/entity.py index 687d7873ece..f697a364bc8 100644 --- a/homeassistant/components/airzone/entity.py +++ b/homeassistant/components/airzone/entity.py @@ -7,16 +7,19 @@ from aioairzone.const import ( AZD_FIRMWARE, AZD_FULL_NAME, AZD_ID, + AZD_MAC, AZD_MODEL, AZD_NAME, AZD_SYSTEM, AZD_SYSTEMS, AZD_THERMOSTAT_FW, AZD_THERMOSTAT_MODEL, + AZD_WEBSERVER, AZD_ZONES, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -46,17 +49,15 @@ class AirzoneSystemEntity(AirzoneEntity): self.system_id = system_data[AZD_ID] - self._attr_device_info: DeviceInfo = { - "identifiers": {(DOMAIN, f"{entry.entry_id}_{self.system_id}")}, - "manufacturer": MANUFACTURER, - "model": self.get_airzone_value(AZD_MODEL), - "name": self.get_airzone_value(AZD_FULL_NAME), - "sw_version": self.get_airzone_value(AZD_FIRMWARE), - "via_device": (DOMAIN, f"{entry.entry_id}_ws"), - } - self._attr_unique_id = ( - entry.entry_id if entry.unique_id is None else entry.unique_id + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, f"{entry.entry_id}_{self.system_id}")}, + manufacturer=MANUFACTURER, + model=self.get_airzone_value(AZD_MODEL), + name=self.get_airzone_value(AZD_FULL_NAME), + sw_version=self.get_airzone_value(AZD_FIRMWARE), + via_device=(DOMAIN, f"{entry.entry_id}_ws"), ) + self._attr_unique_id = entry.unique_id or entry.entry_id def get_airzone_value(self, key: str) -> Any: """Return system value by key.""" @@ -67,6 +68,34 @@ class AirzoneSystemEntity(AirzoneEntity): return value +class AirzoneWebServerEntity(AirzoneEntity): + """Define an Airzone WebServer entity.""" + + def __init__( + self, + coordinator: AirzoneUpdateCoordinator, + entry: ConfigEntry, + ) -> None: + """Initialize.""" + super().__init__(coordinator) + + mac = self.get_airzone_value(AZD_MAC) + + self._attr_device_info = DeviceInfo( + connections={(dr.CONNECTION_NETWORK_MAC, mac)}, + identifiers={(DOMAIN, f"{entry.entry_id}_ws")}, + manufacturer=MANUFACTURER, + model=self.get_airzone_value(AZD_MODEL), + name=self.get_airzone_value(AZD_FULL_NAME), + sw_version=self.get_airzone_value(AZD_FIRMWARE), + ) + self._attr_unique_id = entry.unique_id or entry.entry_id + + def get_airzone_value(self, key: str) -> Any: + """Return system value by key.""" + return self.coordinator.data[AZD_WEBSERVER].get(key) + + class AirzoneZoneEntity(AirzoneEntity): """Define an Airzone Zone entity.""" @@ -84,17 +113,15 @@ class AirzoneZoneEntity(AirzoneEntity): self.system_zone_id = system_zone_id self.zone_id = zone_data[AZD_ID] - self._attr_device_info: DeviceInfo = { - "identifiers": {(DOMAIN, f"{entry.entry_id}_{system_zone_id}")}, - "manufacturer": MANUFACTURER, - "model": self.get_airzone_value(AZD_THERMOSTAT_MODEL), - "name": f"Airzone [{system_zone_id}] {zone_data[AZD_NAME]}", - "sw_version": self.get_airzone_value(AZD_THERMOSTAT_FW), - "via_device": (DOMAIN, f"{entry.entry_id}_{self.system_id}"), - } - self._attr_unique_id = ( - entry.entry_id if entry.unique_id is None else entry.unique_id + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, f"{entry.entry_id}_{system_zone_id}")}, + manufacturer=MANUFACTURER, + model=self.get_airzone_value(AZD_THERMOSTAT_MODEL), + name=f"Airzone [{system_zone_id}] {zone_data[AZD_NAME]}", + sw_version=self.get_airzone_value(AZD_THERMOSTAT_FW), + via_device=(DOMAIN, f"{entry.entry_id}_{self.system_id}"), ) + self._attr_unique_id = entry.unique_id or entry.entry_id def get_airzone_value(self, key: str) -> Any: """Return zone value by key.""" diff --git a/homeassistant/components/airzone/sensor.py b/homeassistant/components/airzone/sensor.py index 3e81f5df094..1671eb919a7 100644 --- a/homeassistant/components/airzone/sensor.py +++ b/homeassistant/components/airzone/sensor.py @@ -3,7 +3,15 @@ from __future__ import annotations from typing import Any, Final -from aioairzone.const import AZD_HUMIDITY, AZD_NAME, AZD_TEMP, AZD_TEMP_UNIT, AZD_ZONES +from aioairzone.const import ( + AZD_HUMIDITY, + AZD_NAME, + AZD_TEMP, + AZD_TEMP_UNIT, + AZD_WEBSERVER, + AZD_WIFI_RSSI, + AZD_ZONES, +) from homeassistant.components.sensor import ( SensorDeviceClass, @@ -12,13 +20,30 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import ( + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + TEMP_CELSIUS, +) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, TEMP_UNIT_LIB_TO_HASS from .coordinator import AirzoneUpdateCoordinator -from .entity import AirzoneEntity, AirzoneZoneEntity +from .entity import AirzoneEntity, AirzoneWebServerEntity, AirzoneZoneEntity + +WEBSERVER_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = ( + SensorEntityDescription( + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + key=AZD_WIFI_RSSI, + name="RSSI", + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + state_class=SensorStateClass.MEASUREMENT, + ), +) ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = ( SensorEntityDescription( @@ -45,6 +70,19 @@ async def async_setup_entry( coordinator = hass.data[DOMAIN][entry.entry_id] sensors: list[AirzoneSensor] = [] + + if AZD_WEBSERVER in coordinator.data: + ws_data = coordinator.data[AZD_WEBSERVER] + for description in WEBSERVER_SENSOR_TYPES: + if description.key in ws_data: + sensors.append( + AirzoneWebServerSensor( + coordinator, + description, + entry, + ) + ) + for system_zone_id, zone_data in coordinator.data[AZD_ZONES].items(): for description in ZONE_SENSOR_TYPES: if description.key in zone_data: @@ -70,6 +108,23 @@ class AirzoneSensor(AirzoneEntity, SensorEntity): self._attr_native_value = self.get_airzone_value(self.entity_description.key) +class AirzoneWebServerSensor(AirzoneWebServerEntity, AirzoneSensor): + """Define an Airzone WebServer sensor.""" + + def __init__( + self, + coordinator: AirzoneUpdateCoordinator, + description: SensorEntityDescription, + entry: ConfigEntry, + ) -> None: + """Initialize.""" + super().__init__(coordinator, entry) + self._attr_name = f"WebServer {description.name}" + self._attr_unique_id = f"{self._attr_unique_id}_ws_{description.key}" + self.entity_description = description + self._async_update_attrs() + + class AirzoneZoneSensor(AirzoneZoneEntity, AirzoneSensor): """Define an Airzone Zone sensor.""" diff --git a/tests/components/airzone/test_sensor.py b/tests/components/airzone/test_sensor.py index c68be2abbab..bd57129cae0 100644 --- a/tests/components/airzone/test_sensor.py +++ b/tests/components/airzone/test_sensor.py @@ -1,15 +1,24 @@ """The sensor tests for the Airzone platform.""" +from unittest.mock import AsyncMock + from homeassistant.core import HomeAssistant from .util import async_init_integration -async def test_airzone_create_sensors(hass: HomeAssistant) -> None: +async def test_airzone_create_sensors( + hass: HomeAssistant, entity_registry_enabled_by_default: AsyncMock +) -> None: """Test creation of sensors.""" await async_init_integration(hass) + # WebServer + state = hass.states.get("sensor.webserver_rssi") + assert state.state == "-42" + + # Zones state = hass.states.get("sensor.despacho_temperature") assert state.state == "21.2" diff --git a/tests/components/airzone/util.py b/tests/components/airzone/util.py index ede4bcc3b53..6b81c493eb6 100644 --- a/tests/components/airzone/util.py +++ b/tests/components/airzone/util.py @@ -34,7 +34,6 @@ from aioairzone.const import ( API_WIFI_RSSI, API_ZONE_ID, ) -from aioairzone.exceptions import InvalidMethod from homeassistant.components.airzone import DOMAIN from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT @@ -219,7 +218,7 @@ async def async_init_integration( return_value=HVAC_SYSTEMS_MOCK, ), patch( "homeassistant.components.airzone.AirzoneLocalApi.get_webserver", - side_effect=InvalidMethod, + return_value=HVAC_WEBSERVER_MOCK, ): await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done()