From 8fe900885ab767409ce834817cae237f5e9e2049 Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Mon, 17 Apr 2023 10:56:57 +1000 Subject: [PATCH] Bump Advantage Air to 0.4.4 (#91147) --- .../components/advantage_air/__init__.py | 23 +------ .../components/advantage_air/climate.py | 68 ++++--------------- .../components/advantage_air/cover.py | 38 ++--------- .../components/advantage_air/entity.py | 36 ++++++++-- .../components/advantage_air/light.py | 38 +++++------ .../components/advantage_air/manifest.json | 2 +- .../components/advantage_air/select.py | 4 +- .../components/advantage_air/sensor.py | 2 +- .../components/advantage_air/switch.py | 8 +-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/advantage_air/test_light.py | 14 ++++ 12 files changed, 93 insertions(+), 144 deletions(-) diff --git a/homeassistant/components/advantage_air/__init__.py b/homeassistant/components/advantage_air/__init__.py index 3b5faaa1855..bffe2c7d06a 100644 --- a/homeassistant/components/advantage_air/__init__.py +++ b/homeassistant/components/advantage_air/__init__.py @@ -7,7 +7,6 @@ from advantage_air import ApiError, advantage_air from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -53,30 +52,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL), ) - def error_handle_factory(func): - """Return the provided API function wrapped. - - Adds an error handler and coordinator refresh. - """ - - async def error_handle(param): - try: - if await func(param): - await coordinator.async_refresh() - except ApiError as err: - raise HomeAssistantError(err) from err - - return error_handle - await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][entry.entry_id] = { - "coordinator": coordinator, - "aircon": error_handle_factory(api.aircon.async_set), - "lights": error_handle_factory(api.lights.async_set), - "things": error_handle_factory(api.things.async_set), - } + hass.data[DOMAIN][entry.entry_id] = {"coordinator": coordinator, "api": api} await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/homeassistant/components/advantage_air/climate.py b/homeassistant/components/advantage_air/climate.py index a8f0e9c884f..05adc37b7f3 100644 --- a/homeassistant/components/advantage_air/climate.py +++ b/homeassistant/components/advantage_air/climate.py @@ -161,67 +161,41 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity): async def async_turn_on(self) -> None: """Set the HVAC State to on.""" - await self.aircon( - { - self.ac_key: { - "info": { - "state": ADVANTAGE_AIR_STATE_ON, - } - } - } - ) + await self.async_update_ac({"state": ADVANTAGE_AIR_STATE_ON}) async def async_turn_off(self) -> None: """Set the HVAC State to off.""" - await self.aircon( + await self.async_update_ac( { - self.ac_key: { - "info": { - "state": ADVANTAGE_AIR_STATE_OFF, - } - } + "state": ADVANTAGE_AIR_STATE_OFF, } ) async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the HVAC Mode and State.""" if hvac_mode == HVACMode.OFF: - await self.aircon( - {self.ac_key: {"info": {"state": ADVANTAGE_AIR_STATE_OFF}}} - ) + await self.async_update_ac({"state": ADVANTAGE_AIR_STATE_OFF}) else: - await self.aircon( + await self.async_update_ac( { - self.ac_key: { - "info": { - "state": ADVANTAGE_AIR_STATE_ON, - "mode": HASS_HVAC_MODES.get(hvac_mode), - } - } + "state": ADVANTAGE_AIR_STATE_ON, + "mode": HASS_HVAC_MODES.get(hvac_mode), } ) async def async_set_fan_mode(self, fan_mode: str) -> None: """Set the Fan Mode.""" - await self.aircon( - {self.ac_key: {"info": {"fan": HASS_FAN_MODES.get(fan_mode)}}} - ) + await self.async_update_ac({"fan": HASS_FAN_MODES.get(fan_mode)}) async def async_set_temperature(self, **kwargs: Any) -> None: """Set the Temperature.""" if ATTR_TEMPERATURE in kwargs: - await self.aircon( - {self.ac_key: {"info": {"setTemp": kwargs[ATTR_TEMPERATURE]}}} - ) + await self.async_update_ac({"setTemp": kwargs[ATTR_TEMPERATURE]}) if ATTR_TARGET_TEMP_LOW in kwargs and ATTR_TARGET_TEMP_HIGH in kwargs: - await self.aircon( + await self.async_update_ac( { - self.ac_key: { - "info": { - ADVANTAGE_AIR_COOL_TARGET: kwargs[ATTR_TARGET_TEMP_HIGH], - ADVANTAGE_AIR_HEAT_TARGET: kwargs[ATTR_TARGET_TEMP_LOW], - } - } + ADVANTAGE_AIR_COOL_TARGET: kwargs[ATTR_TARGET_TEMP_HIGH], + ADVANTAGE_AIR_HEAT_TARGET: kwargs[ATTR_TARGET_TEMP_LOW], } ) @@ -260,23 +234,11 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity): async def async_turn_on(self) -> None: """Set the HVAC State to on.""" - await self.aircon( - { - self.ac_key: { - "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_OPEN}} - } - } - ) + await self.async_update_zone({"state": ADVANTAGE_AIR_STATE_OPEN}) async def async_turn_off(self) -> None: """Set the HVAC State to off.""" - await self.aircon( - { - self.ac_key: { - "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}} - } - } - ) + await self.async_update_zone({"state": ADVANTAGE_AIR_STATE_CLOSE}) async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the HVAC Mode and State.""" @@ -288,4 +250,4 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity): async def async_set_temperature(self, **kwargs: Any) -> None: """Set the Temperature.""" temp = kwargs.get(ATTR_TEMPERATURE) - await self.aircon({self.ac_key: {"zones": {self.zone_key: {"setTemp": temp}}}}) + await self.async_update_zone({"setTemp": temp}) diff --git a/homeassistant/components/advantage_air/cover.py b/homeassistant/components/advantage_air/cover.py index fe725256643..ea744609415 100644 --- a/homeassistant/components/advantage_air/cover.py +++ b/homeassistant/components/advantage_air/cover.py @@ -79,48 +79,24 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity): async def async_open_cover(self, **kwargs: Any) -> None: """Fully open zone vent.""" - await self.aircon( - { - self.ac_key: { - "zones": { - self.zone_key: {"state": ADVANTAGE_AIR_STATE_OPEN, "value": 100} - } - } - } + await self.async_update_zone( + {"state": ADVANTAGE_AIR_STATE_OPEN, "value": 100}, ) async def async_close_cover(self, **kwargs: Any) -> None: """Fully close zone vent.""" - await self.aircon( - { - self.ac_key: { - "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}} - } - } - ) + await self.async_update_zone({"state": ADVANTAGE_AIR_STATE_CLOSE}) async def async_set_cover_position(self, **kwargs: Any) -> None: """Change vent position.""" position = round(kwargs[ATTR_POSITION] / 5) * 5 if position == 0: - await self.aircon( - { - self.ac_key: { - "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}} - } - } - ) + await self.async_update_zone({"state": ADVANTAGE_AIR_STATE_CLOSE}) else: - await self.aircon( + await self.async_update_zone( { - self.ac_key: { - "zones": { - self.zone_key: { - "state": ADVANTAGE_AIR_STATE_OPEN, - "value": position, - } - } - } + "state": ADVANTAGE_AIR_STATE_OPEN, + "value": position, } ) diff --git a/homeassistant/components/advantage_air/entity.py b/homeassistant/components/advantage_air/entity.py index 41e7a499caf..418281af54c 100644 --- a/homeassistant/components/advantage_air/entity.py +++ b/homeassistant/components/advantage_air/entity.py @@ -1,6 +1,9 @@ """Advantage Air parent entity class.""" from typing import Any +from advantage_air import ApiError + +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -17,6 +20,21 @@ class AdvantageAirEntity(CoordinatorEntity): super().__init__(instance["coordinator"]) self._attr_unique_id: str = self.coordinator.data["system"]["rid"] + def update_handle_factory(self, func, *keys): + """Return the provided API function wrapped. + + Adds an error handler and coordinator refresh, and presets keys. + """ + + async def update_handle(*values): + try: + if await func(*keys, *values): + await self.coordinator.async_refresh() + except ApiError as err: + raise HomeAssistantError(err) from err + + return update_handle + class AdvantageAirAcEntity(AdvantageAirEntity): """Parent class for Advantage Air AC Entities.""" @@ -24,7 +42,7 @@ class AdvantageAirAcEntity(AdvantageAirEntity): def __init__(self, instance: dict[str, Any], ac_key: str) -> None: """Initialize common aspects of an Advantage Air ac entity.""" super().__init__(instance) - self.aircon = instance["aircon"] + self.ac_key: str = ac_key self._attr_unique_id += f"-{ac_key}" @@ -35,6 +53,9 @@ class AdvantageAirAcEntity(AdvantageAirEntity): model=self.coordinator.data["system"]["sysType"], name=self.coordinator.data["aircons"][self.ac_key]["info"]["name"], ) + self.async_update_ac = self.update_handle_factory( + instance["api"].aircon.async_update_ac, self.ac_key + ) @property def _ac(self) -> dict[str, Any]: @@ -47,8 +68,12 @@ class AdvantageAirZoneEntity(AdvantageAirAcEntity): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None: """Initialize common aspects of an Advantage Air zone entity.""" super().__init__(instance, ac_key) + self.zone_key: str = zone_key self._attr_unique_id += f"-{zone_key}" + self.async_update_zone = self.update_handle_factory( + instance["api"].aircon.async_update_zone, self.ac_key, self.zone_key + ) @property def _zone(self) -> dict[str, Any]: @@ -61,7 +86,7 @@ class AdvantageAirThingEntity(AdvantageAirEntity): def __init__(self, instance: dict[str, Any], thing: dict[str, Any]) -> None: """Initialize common aspects of an Advantage Air Things entity.""" super().__init__(instance) - self.set = instance["things"] + self._id = thing["id"] self._attr_unique_id += f"-{self._id}" @@ -72,6 +97,9 @@ class AdvantageAirThingEntity(AdvantageAirEntity): model="MyPlace", name=thing["name"], ) + self.async_update_value = self.update_handle_factory( + instance["api"].things.async_update_value, self._id + ) @property def _data(self) -> dict: @@ -85,8 +113,8 @@ class AdvantageAirThingEntity(AdvantageAirEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the thing on.""" - await self.set({self._id: {"id": self._id, "value": 100}}) + await self.async_update_value(True) async def async_turn_off(self, **kwargs: Any) -> None: """Turn the thing off.""" - await self.set({self._id: {"id": self._id, "value": 0}}) + await self.async_update_value(False) diff --git a/homeassistant/components/advantage_air/light.py b/homeassistant/components/advantage_air/light.py index c88bceea510..7dd79f93892 100644 --- a/homeassistant/components/advantage_air/light.py +++ b/homeassistant/components/advantage_air/light.py @@ -7,11 +7,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ( - ADVANTAGE_AIR_STATE_OFF, - ADVANTAGE_AIR_STATE_ON, - DOMAIN as ADVANTAGE_AIR_DOMAIN, -) +from .const import ADVANTAGE_AIR_STATE_ON, DOMAIN as ADVANTAGE_AIR_DOMAIN from .entity import AdvantageAirEntity, AdvantageAirThingEntity @@ -48,7 +44,7 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity): def __init__(self, instance: dict[str, Any], light: dict[str, Any]) -> None: """Initialize an Advantage Air Light.""" super().__init__(instance) - self.set = instance["lights"] + self._id: str = light["id"] self._attr_unique_id += f"-{self._id}" self._attr_device_info = DeviceInfo( @@ -58,6 +54,9 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity): model=light.get("moduleType"), name=light["name"], ) + self.async_update_state = self.update_handle_factory( + instance["api"].lights.async_update_state, self._id + ) @property def _data(self) -> dict[str, Any]: @@ -71,11 +70,11 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the light on.""" - await self.set({self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON}}) + await self.async_update_state(True) async def async_turn_off(self, **kwargs: Any) -> None: """Turn the light off.""" - await self.set({self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF}}) + await self.async_update_state(False) class AdvantageAirLightDimmable(AdvantageAirLight): @@ -83,6 +82,13 @@ class AdvantageAirLightDimmable(AdvantageAirLight): _attr_supported_color_modes = {ColorMode.ONOFF, ColorMode.BRIGHTNESS} + def __init__(self, instance: dict[str, Any], light: dict[str, Any]) -> None: + """Initialize an Advantage Air Dimmable Light.""" + super().__init__(instance, light) + self.async_update_value = self.update_handle_factory( + instance["api"].lights.async_update_value, self._id + ) + @property def brightness(self) -> int: """Return the brightness of this light between 0..255.""" @@ -90,12 +96,9 @@ class AdvantageAirLightDimmable(AdvantageAirLight): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the light on and optionally set the brightness.""" - data: dict[str, Any] = { - self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON} - } if ATTR_BRIGHTNESS in kwargs: - data[self._id]["value"] = round(kwargs[ATTR_BRIGHTNESS] * 100 / 255) - await self.set(data) + return await self.async_update_value(round(kwargs[ATTR_BRIGHTNESS] / 2.55)) + return await self.async_update_state(True) class AdvantageAirThingLight(AdvantageAirThingEntity, LightEntity): @@ -116,11 +119,4 @@ class AdvantageAirThingLightDimmable(AdvantageAirThingEntity, LightEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the light on by setting the brightness.""" - await self.set( - { - self._id: { - "id": self._id, - "value": round(kwargs.get(ATTR_BRIGHTNESS, 255) * 100 / 255), - } - } - ) + await self.async_update_value(round(kwargs.get(ATTR_BRIGHTNESS, 255) / 2.55)) diff --git a/homeassistant/components/advantage_air/manifest.json b/homeassistant/components/advantage_air/manifest.json index 86bbbe3e57d..ed9d3bff989 100644 --- a/homeassistant/components/advantage_air/manifest.json +++ b/homeassistant/components/advantage_air/manifest.json @@ -7,5 +7,5 @@ "iot_class": "local_polling", "loggers": ["advantage_air"], "quality_scale": "platinum", - "requirements": ["advantage_air==0.4.2"] + "requirements": ["advantage_air==0.4.4"] } diff --git a/homeassistant/components/advantage_air/select.py b/homeassistant/components/advantage_air/select.py index 6d28549ca41..b78cd7f99d3 100644 --- a/homeassistant/components/advantage_air/select.py +++ b/homeassistant/components/advantage_air/select.py @@ -58,6 +58,4 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity): async def async_select_option(self, option: str) -> None: """Set the MyZone.""" - await self.aircon( - {self.ac_key: {"info": {"myZone": self._name_to_number[option]}}} - ) + await self.async_update_ac({"myZone": self._name_to_number[option]}) diff --git a/homeassistant/components/advantage_air/sensor.py b/homeassistant/components/advantage_air/sensor.py index 04b3802f643..e4701ee4668 100644 --- a/homeassistant/components/advantage_air/sensor.py +++ b/homeassistant/components/advantage_air/sensor.py @@ -88,7 +88,7 @@ class AdvantageAirTimeTo(AdvantageAirAcEntity, SensorEntity): async def set_time_to(self, **kwargs: Any) -> None: """Set the timer value.""" value = min(720, max(0, int(kwargs[ADVANTAGE_AIR_SET_COUNTDOWN_VALUE]))) - await self.aircon({self.ac_key: {"info": {self._time_key: value}}}) + await self.async_update_ac({self._time_key: value}) class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity): diff --git a/homeassistant/components/advantage_air/switch.py b/homeassistant/components/advantage_air/switch.py index 3ef3dbbb74d..ee6d1499331 100644 --- a/homeassistant/components/advantage_air/switch.py +++ b/homeassistant/components/advantage_air/switch.py @@ -54,15 +54,11 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn fresh air on.""" - await self.aircon( - {self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_ON}}} - ) + await self.async_update_ac({"freshAirStatus": ADVANTAGE_AIR_STATE_ON}) async def async_turn_off(self, **kwargs: Any) -> None: """Turn fresh air off.""" - await self.aircon( - {self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}} - ) + await self.async_update_ac({"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}) class AdvantageAirRelay(AdvantageAirThingEntity, SwitchEntity): diff --git a/requirements_all.txt b/requirements_all.txt index 6d9e9f62381..0ce40c3ccca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -86,7 +86,7 @@ adext==0.4.2 adguardhome==0.6.1 # homeassistant.components.advantage_air -advantage_air==0.4.2 +advantage_air==0.4.4 # homeassistant.components.frontier_silicon afsapi==0.2.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a0cb8a00236..96b5520297d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -76,7 +76,7 @@ adext==0.4.2 adguardhome==0.6.1 # homeassistant.components.advantage_air -advantage_air==0.4.2 +advantage_air==0.4.4 # homeassistant.components.frontier_silicon afsapi==0.2.7 diff --git a/tests/components/advantage_air/test_light.py b/tests/components/advantage_air/test_light.py index 80b28581d5b..a1d38857116 100644 --- a/tests/components/advantage_air/test_light.py +++ b/tests/components/advantage_air/test_light.py @@ -90,6 +90,20 @@ async def test_light(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) - assert entry assert entry.unique_id == f"uniqueid-{light_id}" + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: [entity_id]}, + blocking=True, + ) + assert aioclient_mock.mock_calls[-2][0] == "GET" + assert aioclient_mock.mock_calls[-2][1].path == "/setLights" + data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(light_id) + assert data["id"] == light_id + assert data["state"] == ADVANTAGE_AIR_STATE_ON + assert aioclient_mock.mock_calls[-1][0] == "GET" + assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData" + await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON,