Code Quality Improvements for Advantage Air (#77695)

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
This commit is contained in:
Brett Adams 2022-09-19 05:27:09 +10:00 committed by GitHub
parent 4fbf44cced
commit fa7f04c34b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 148 additions and 121 deletions

View File

@ -7,6 +7,7 @@ from advantage_air import ApiError, advantage_air
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, Platform from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@ -60,7 +61,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if await func(param): if await func(param):
await coordinator.async_refresh() await coordinator.async_refresh()
except ApiError as err: except ApiError as err:
_LOGGER.warning(err) raise HomeAssistantError(err) from err
return error_handle return error_handle
@ -69,8 +70,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = { hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator, "coordinator": coordinator,
"async_change": error_handle_factory(api.aircon.async_set), "aircon": error_handle_factory(api.aircon.async_set),
"async_set_light": error_handle_factory(api.lights.async_set), "lights": error_handle_factory(api.lights.async_set),
} }
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -1,6 +1,8 @@
"""Binary Sensor platform for Advantage Air integration.""" """Binary Sensor platform for Advantage Air integration."""
from __future__ import annotations from __future__ import annotations
from typing import Any
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
BinarySensorEntity, BinarySensorEntity,
@ -26,7 +28,8 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id] instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities: list[BinarySensorEntity] = [] entities: list[BinarySensorEntity] = []
for ac_key, ac_device in instance["coordinator"].data["aircons"].items(): if aircons := instance["coordinator"].data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirFilter(instance, ac_key)) entities.append(AdvantageAirFilter(instance, ac_key))
for zone_key, zone in ac_device["zones"].items(): for zone_key, zone in ac_device["zones"].items():
# Only add motion sensor when motion is enabled # Only add motion sensor when motion is enabled
@ -45,13 +48,13 @@ class AdvantageAirFilter(AdvantageAirAcEntity, BinarySensorEntity):
_attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
_attr_name = "Filter" _attr_name = "Filter"
def __init__(self, instance, ac_key): def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
"""Initialize an Advantage Air Filter sensor.""" """Initialize an Advantage Air Filter sensor."""
super().__init__(instance, ac_key) super().__init__(instance, ac_key)
self._attr_unique_id += "-filter" self._attr_unique_id += "-filter"
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return if filter needs cleaning.""" """Return if filter needs cleaning."""
return self._ac["filterCleanStatus"] return self._ac["filterCleanStatus"]
@ -61,14 +64,14 @@ class AdvantageAirZoneMotion(AdvantageAirZoneEntity, BinarySensorEntity):
_attr_device_class = BinarySensorDeviceClass.MOTION _attr_device_class = BinarySensorDeviceClass.MOTION
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Motion sensor.""" """Initialize an Advantage Air Zone Motion sensor."""
super().__init__(instance, ac_key, zone_key) super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} motion' self._attr_name = f'{self._zone["name"]} motion'
self._attr_unique_id += "-motion" self._attr_unique_id += "-motion"
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return if motion is detect.""" """Return if motion is detect."""
return self._zone["motion"] == 20 return self._zone["motion"] == 20
@ -79,13 +82,13 @@ class AdvantageAirZoneMyZone(AdvantageAirZoneEntity, BinarySensorEntity):
_attr_entity_registry_enabled_default = False _attr_entity_registry_enabled_default = False
_attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone MyZone sensor.""" """Initialize an Advantage Air Zone MyZone sensor."""
super().__init__(instance, ac_key, zone_key) super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} myZone' self._attr_name = f'{self._zone["name"]} myZone'
self._attr_unique_id += "-myzone" self._attr_unique_id += "-myzone"
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return if this zone is the myZone.""" """Return if this zone is the myZone."""
return self._zone["number"] == self._ac["myZone"] return self._zone["number"] == self._ac["myZone"]

View File

@ -70,7 +70,8 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id] instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities: list[ClimateEntity] = [] entities: list[ClimateEntity] = []
for ac_key, ac_device in instance["coordinator"].data["aircons"].items(): if aircons := instance["coordinator"].data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirAC(instance, ac_key)) entities.append(AdvantageAirAC(instance, ac_key))
for zone_key, zone in ac_device["zones"].items(): for zone_key, zone in ac_device["zones"].items():
# Only add zone climate control when zone is in temperature control # Only add zone climate control when zone is in temperature control
@ -92,37 +93,37 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
) )
def __init__(self, instance, ac_key): def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
"""Initialize an AdvantageAir AC unit.""" """Initialize an AdvantageAir AC unit."""
super().__init__(instance, ac_key) super().__init__(instance, ac_key)
if self._ac.get("myAutoModeEnabled"): if self._ac.get("myAutoModeEnabled"):
self._attr_hvac_modes = AC_HVAC_MODES + [HVACMode.AUTO] self._attr_hvac_modes = AC_HVAC_MODES + [HVACMode.AUTO]
@property @property
def target_temperature(self): def target_temperature(self) -> float:
"""Return the current target temperature.""" """Return the current target temperature."""
return self._ac["setTemp"] return self._ac["setTemp"]
@property @property
def hvac_mode(self): def hvac_mode(self) -> HVACMode | None:
"""Return the current HVAC modes.""" """Return the current HVAC modes."""
if self._ac["state"] == ADVANTAGE_AIR_STATE_ON: if self._ac["state"] == ADVANTAGE_AIR_STATE_ON:
return ADVANTAGE_AIR_HVAC_MODES.get(self._ac["mode"]) return ADVANTAGE_AIR_HVAC_MODES.get(self._ac["mode"])
return HVACMode.OFF return HVACMode.OFF
@property @property
def fan_mode(self): def fan_mode(self) -> str | None:
"""Return the current fan modes.""" """Return the current fan modes."""
return ADVANTAGE_AIR_FAN_MODES.get(self._ac["fan"]) return ADVANTAGE_AIR_FAN_MODES.get(self._ac["fan"])
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC Mode and State.""" """Set the HVAC Mode and State."""
if hvac_mode == HVACMode.OFF: if hvac_mode == HVACMode.OFF:
await self.async_change( await self.aircon(
{self.ac_key: {"info": {"state": ADVANTAGE_AIR_STATE_OFF}}} {self.ac_key: {"info": {"state": ADVANTAGE_AIR_STATE_OFF}}}
) )
else: else:
await self.async_change( await self.aircon(
{ {
self.ac_key: { self.ac_key: {
"info": { "info": {
@ -135,14 +136,14 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
async def async_set_fan_mode(self, fan_mode: str) -> None: async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set the Fan Mode.""" """Set the Fan Mode."""
await self.async_change( await self.aircon(
{self.ac_key: {"info": {"fan": HASS_FAN_MODES.get(fan_mode)}}} {self.ac_key: {"info": {"fan": HASS_FAN_MODES.get(fan_mode)}}}
) )
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the Temperature.""" """Set the Temperature."""
temp = kwargs.get(ATTR_TEMPERATURE) temp = kwargs.get(ATTR_TEMPERATURE)
await self.async_change({self.ac_key: {"info": {"setTemp": temp}}}) await self.aircon({self.ac_key: {"info": {"setTemp": temp}}})
class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity): class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
@ -155,7 +156,7 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
_attr_hvac_modes = ZONE_HVAC_MODES _attr_hvac_modes = ZONE_HVAC_MODES
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize an AdvantageAir Zone control.""" """Initialize an AdvantageAir Zone control."""
super().__init__(instance, ac_key, zone_key) super().__init__(instance, ac_key, zone_key)
self._attr_name = self._zone["name"] self._attr_name = self._zone["name"]
@ -164,26 +165,26 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
) )
@property @property
def hvac_mode(self): def hvac_mode(self) -> HVACMode:
"""Return the current state as HVAC mode.""" """Return the current state as HVAC mode."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN: if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return HVACMode.HEAT_COOL return HVACMode.HEAT_COOL
return HVACMode.OFF return HVACMode.OFF
@property @property
def current_temperature(self): def current_temperature(self) -> float:
"""Return the current temperature.""" """Return the current temperature."""
return self._zone["measuredTemp"] return self._zone["measuredTemp"]
@property @property
def target_temperature(self): def target_temperature(self) -> float:
"""Return the target temperature.""" """Return the target temperature."""
return self._zone["setTemp"] return self._zone["setTemp"]
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC Mode and State.""" """Set the HVAC Mode and State."""
if hvac_mode == HVACMode.OFF: if hvac_mode == HVACMode.OFF:
await self.async_change( await self.aircon(
{ {
self.ac_key: { self.ac_key: {
"zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}} "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}}
@ -191,7 +192,7 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
} }
) )
else: else:
await self.async_change( await self.aircon(
{ {
self.ac_key: { self.ac_key: {
"zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_OPEN}} "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_OPEN}}
@ -202,6 +203,4 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the Temperature.""" """Set the Temperature."""
temp = kwargs.get(ATTR_TEMPERATURE) temp = kwargs.get(ATTR_TEMPERATURE)
await self.async_change( await self.aircon({self.ac_key: {"zones": {self.zone_key: {"setTemp": temp}}}})
{self.ac_key: {"zones": {self.zone_key: {"setTemp": temp}}}}
)

View File

@ -1,9 +1,14 @@
"""Config Flow for Advantage Air integration.""" """Config Flow for Advantage Air integration."""
from __future__ import annotations
from typing import Any
from advantage_air import ApiError, advantage_air from advantage_air import ApiError, advantage_air
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import ADVANTAGE_AIR_RETRY, DOMAIN from .const import ADVANTAGE_AIR_RETRY, DOMAIN
@ -25,7 +30,9 @@ class AdvantageAirConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
DOMAIN = DOMAIN DOMAIN = DOMAIN
async def async_step_user(self, user_input=None): async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Get configuration from the user.""" """Get configuration from the user."""
errors = {} errors = {}
if user_input: if user_input:

View File

@ -30,8 +30,9 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id] instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = [] entities: list[CoverEntity] = []
for ac_key, ac_device in instance["coordinator"].data["aircons"].items(): if aircons := instance["coordinator"].data.get("aircons"):
for ac_key, ac_device in aircons.items():
for zone_key, zone in ac_device["zones"].items(): for zone_key, zone in ac_device["zones"].items():
# Only add zone vent controls when zone in vent control mode. # Only add zone vent controls when zone in vent control mode.
if zone["type"] == 0: if zone["type"] == 0:
@ -49,7 +50,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
| CoverEntityFeature.SET_POSITION | CoverEntityFeature.SET_POSITION
) )
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Vent.""" """Initialize an Advantage Air Zone Vent."""
super().__init__(instance, ac_key, zone_key) super().__init__(instance, ac_key, zone_key)
self._attr_name = self._zone["name"] self._attr_name = self._zone["name"]
@ -68,7 +69,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
async def async_open_cover(self, **kwargs: Any) -> None: async def async_open_cover(self, **kwargs: Any) -> None:
"""Fully open zone vent.""" """Fully open zone vent."""
await self.async_change( await self.aircon(
{ {
self.ac_key: { self.ac_key: {
"zones": { "zones": {
@ -80,7 +81,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
async def async_close_cover(self, **kwargs: Any) -> None: async def async_close_cover(self, **kwargs: Any) -> None:
"""Fully close zone vent.""" """Fully close zone vent."""
await self.async_change( await self.aircon(
{ {
self.ac_key: { self.ac_key: {
"zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}} "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}}
@ -92,7 +93,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
"""Change vent position.""" """Change vent position."""
position = round(kwargs[ATTR_POSITION] / 5) * 5 position = round(kwargs[ATTR_POSITION] / 5) * 5
if position == 0: if position == 0:
await self.async_change( await self.aircon(
{ {
self.ac_key: { self.ac_key: {
"zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}} "zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}}
@ -100,7 +101,7 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
} }
) )
else: else:
await self.async_change( await self.aircon(
{ {
self.ac_key: { self.ac_key: {
"zones": { "zones": {

View File

@ -1,5 +1,7 @@
"""Advantage Air parent entity class.""" """Advantage Air parent entity class."""
from typing import Any
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -11,20 +13,20 @@ class AdvantageAirEntity(CoordinatorEntity):
_attr_has_entity_name = True _attr_has_entity_name = True
def __init__(self, instance): def __init__(self, instance: dict[str, Any]) -> None:
"""Initialize common aspects of an Advantage Air entity.""" """Initialize common aspects of an Advantage Air entity."""
super().__init__(instance["coordinator"]) super().__init__(instance["coordinator"])
self._attr_unique_id = self.coordinator.data["system"]["rid"] self._attr_unique_id: str = self.coordinator.data["system"]["rid"]
class AdvantageAirAcEntity(AdvantageAirEntity): class AdvantageAirAcEntity(AdvantageAirEntity):
"""Parent class for Advantage Air AC Entities.""" """Parent class for Advantage Air AC Entities."""
def __init__(self, instance, ac_key): def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
"""Initialize common aspects of an Advantage Air ac entity.""" """Initialize common aspects of an Advantage Air ac entity."""
super().__init__(instance) super().__init__(instance)
self.async_change = instance["async_change"] self.aircon = instance["aircon"]
self.ac_key = ac_key self.ac_key: str = ac_key
self._attr_unique_id += f"-{ac_key}" self._attr_unique_id += f"-{ac_key}"
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
@ -36,19 +38,19 @@ class AdvantageAirAcEntity(AdvantageAirEntity):
) )
@property @property
def _ac(self): def _ac(self) -> dict[str, Any]:
return self.coordinator.data["aircons"][self.ac_key]["info"] return self.coordinator.data["aircons"][self.ac_key]["info"]
class AdvantageAirZoneEntity(AdvantageAirAcEntity): class AdvantageAirZoneEntity(AdvantageAirAcEntity):
"""Parent class for Advantage Air Zone Entities.""" """Parent class for Advantage Air Zone Entities."""
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize common aspects of an Advantage Air zone entity.""" """Initialize common aspects of an Advantage Air zone entity."""
super().__init__(instance, ac_key) super().__init__(instance, ac_key)
self.zone_key = zone_key self.zone_key: str = zone_key
self._attr_unique_id += f"-{zone_key}" self._attr_unique_id += f"-{zone_key}"
@property @property
def _zone(self): def _zone(self) -> dict[str, Any]:
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key] return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key]

View File

@ -24,9 +24,9 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id] instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = [] entities: list[LightEntity] = []
if "myLights" in instance["coordinator"].data: if my_lights := instance["coordinator"].data.get("myLights"):
for light in instance["coordinator"].data["myLights"]["lights"].values(): for light in my_lights["lights"].values():
if light.get("relay"): if light.get("relay"):
entities.append(AdvantageAirLight(instance, light)) entities.append(AdvantageAirLight(instance, light))
else: else:
@ -39,11 +39,11 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
_attr_supported_color_modes = {ColorMode.ONOFF} _attr_supported_color_modes = {ColorMode.ONOFF}
def __init__(self, instance, light): def __init__(self, instance: dict[str, Any], light: dict[str, Any]) -> None:
"""Initialize an Advantage Air Light.""" """Initialize an Advantage Air Light."""
super().__init__(instance) super().__init__(instance)
self.async_set_light = instance["async_set_light"] self.lights = instance["lights"]
self._id = light["id"] self._id: str = light["id"]
self._attr_unique_id += f"-{self._id}" self._attr_unique_id += f"-{self._id}"
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(ADVANTAGE_AIR_DOMAIN, self._attr_unique_id)}, identifiers={(ADVANTAGE_AIR_DOMAIN, self._attr_unique_id)},
@ -54,7 +54,7 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
) )
@property @property
def _light(self): def _light(self) -> dict[str, Any]:
"""Return the light object.""" """Return the light object."""
return self.coordinator.data["myLights"]["lights"][self._id] return self.coordinator.data["myLights"]["lights"][self._id]
@ -65,11 +65,11 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on.""" """Turn the light on."""
await self.async_set_light({"id": self._id, "state": ADVANTAGE_AIR_STATE_ON}) await self.lights({"id": self._id, "state": ADVANTAGE_AIR_STATE_ON})
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off.""" """Turn the light off."""
await self.async_set_light({"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF}) await self.lights({"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF})
class AdvantageAirLightDimmable(AdvantageAirLight): class AdvantageAirLightDimmable(AdvantageAirLight):
@ -84,7 +84,7 @@ class AdvantageAirLightDimmable(AdvantageAirLight):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on and optionally set the brightness.""" """Turn the light on and optionally set the brightness."""
data = {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON} data: dict[str, Any] = {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON}
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
data["value"] = round(kwargs[ATTR_BRIGHTNESS] * 100 / 255) data["value"] = round(kwargs[ATTR_BRIGHTNESS] * 100 / 255)
await self.async_set_light(data) await self.lights(data)

View File

@ -1,4 +1,6 @@
"""Select platform for Advantage Air integration.""" """Select platform for Advantage Air integration."""
from typing import Any
from homeassistant.components.select import SelectEntity from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -19,8 +21,9 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id] instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = [] entities: list[SelectEntity] = []
for ac_key in instance["coordinator"].data["aircons"]: if aircons := instance["coordinator"].data.get("aircons"):
for ac_key in aircons:
entities.append(AdvantageAirMyZone(instance, ac_key)) entities.append(AdvantageAirMyZone(instance, ac_key))
async_add_entities(entities) async_add_entities(entities)
@ -31,7 +34,7 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
_attr_icon = "mdi:home-thermometer" _attr_icon = "mdi:home-thermometer"
_attr_name = "MyZone" _attr_name = "MyZone"
def __init__(self, instance, ac_key): def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
"""Initialize an Advantage Air MyZone control.""" """Initialize an Advantage Air MyZone control."""
super().__init__(instance, ac_key) super().__init__(instance, ac_key)
self._attr_unique_id += "-myzone" self._attr_unique_id += "-myzone"
@ -52,6 +55,6 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
"""Set the MyZone.""" """Set the MyZone."""
await self.async_change( await self.aircon(
{self.ac_key: {"info": {"myZone": self._name_to_number[option]}}} {self.ac_key: {"info": {"myZone": self._name_to_number[option]}}}
) )

View File

@ -1,6 +1,9 @@
"""Sensor platform for Advantage Air integration.""" """Sensor platform for Advantage Air integration."""
from __future__ import annotations from __future__ import annotations
from decimal import Decimal
from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
@ -35,7 +38,8 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id] instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities: list[SensorEntity] = [] entities: list[SensorEntity] = []
for ac_key, ac_device in instance["coordinator"].data["aircons"].items(): if aircons := instance["coordinator"].data.get("aircons"):
for ac_key, ac_device in aircons.items():
entities.append(AdvantageAirTimeTo(instance, ac_key, "On")) entities.append(AdvantageAirTimeTo(instance, ac_key, "On"))
entities.append(AdvantageAirTimeTo(instance, ac_key, "Off")) entities.append(AdvantageAirTimeTo(instance, ac_key, "Off"))
for zone_key, zone in ac_device["zones"].items(): for zone_key, zone in ac_device["zones"].items():
@ -62,7 +66,7 @@ class AdvantageAirTimeTo(AdvantageAirAcEntity, SensorEntity):
_attr_native_unit_of_measurement = ADVANTAGE_AIR_SET_COUNTDOWN_UNIT _attr_native_unit_of_measurement = ADVANTAGE_AIR_SET_COUNTDOWN_UNIT
_attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance, ac_key, action): def __init__(self, instance: dict[str, Any], ac_key: str, action: str) -> None:
"""Initialize the Advantage Air timer control.""" """Initialize the Advantage Air timer control."""
super().__init__(instance, ac_key) super().__init__(instance, ac_key)
self.action = action self.action = action
@ -71,21 +75,21 @@ class AdvantageAirTimeTo(AdvantageAirAcEntity, SensorEntity):
self._attr_unique_id += f"-timeto{action}" self._attr_unique_id += f"-timeto{action}"
@property @property
def native_value(self): def native_value(self) -> Decimal:
"""Return the current value.""" """Return the current value."""
return self._ac[self._time_key] return self._ac[self._time_key]
@property @property
def icon(self): def icon(self) -> str:
"""Return a representative icon of the timer.""" """Return a representative icon of the timer."""
if self._ac[self._time_key] > 0: if self._ac[self._time_key] > 0:
return "mdi:timer-outline" return "mdi:timer-outline"
return "mdi:timer-off-outline" return "mdi:timer-off-outline"
async def set_time_to(self, **kwargs): async def set_time_to(self, **kwargs: Any) -> None:
"""Set the timer value.""" """Set the timer value."""
value = min(720, max(0, int(kwargs[ADVANTAGE_AIR_SET_COUNTDOWN_VALUE]))) value = min(720, max(0, int(kwargs[ADVANTAGE_AIR_SET_COUNTDOWN_VALUE])))
await self.async_change({self.ac_key: {"info": {self._time_key: value}}}) await self.aircon({self.ac_key: {"info": {self._time_key: value}}})
class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity): class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
@ -95,21 +99,21 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
_attr_state_class = SensorStateClass.MEASUREMENT _attr_state_class = SensorStateClass.MEASUREMENT
_attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Vent Sensor.""" """Initialize an Advantage Air Zone Vent Sensor."""
super().__init__(instance, ac_key, zone_key=zone_key) super().__init__(instance, ac_key, zone_key=zone_key)
self._attr_name = f'{self._zone["name"]} vent' self._attr_name = f'{self._zone["name"]} vent'
self._attr_unique_id += "-vent" self._attr_unique_id += "-vent"
@property @property
def native_value(self): def native_value(self) -> Decimal:
"""Return the current value of the air vent.""" """Return the current value of the air vent."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN: if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return self._zone["value"] return self._zone["value"]
return 0 return Decimal(0)
@property @property
def icon(self): def icon(self) -> str:
"""Return a representative icon.""" """Return a representative icon."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN: if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return "mdi:fan" return "mdi:fan"
@ -123,19 +127,19 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity):
_attr_state_class = SensorStateClass.MEASUREMENT _attr_state_class = SensorStateClass.MEASUREMENT
_attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone wireless signal sensor.""" """Initialize an Advantage Air Zone wireless signal sensor."""
super().__init__(instance, ac_key, zone_key) super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} signal' self._attr_name = f'{self._zone["name"]} signal'
self._attr_unique_id += "-signal" self._attr_unique_id += "-signal"
@property @property
def native_value(self): def native_value(self) -> Decimal:
"""Return the current value of the wireless signal.""" """Return the current value of the wireless signal."""
return self._zone["rssi"] return self._zone["rssi"]
@property @property
def icon(self): def icon(self) -> str:
"""Return a representative icon.""" """Return a representative icon."""
if self._zone["rssi"] >= 80: if self._zone["rssi"] >= 80:
return "mdi:wifi-strength-4" return "mdi:wifi-strength-4"
@ -157,13 +161,13 @@ class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity):
_attr_entity_registry_enabled_default = False _attr_entity_registry_enabled_default = False
_attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, instance, ac_key, zone_key): def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None:
"""Initialize an Advantage Air Zone Temp Sensor.""" """Initialize an Advantage Air Zone Temp Sensor."""
super().__init__(instance, ac_key, zone_key) super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} temperature' self._attr_name = f'{self._zone["name"]} temperature'
self._attr_unique_id += "-temp" self._attr_unique_id += "-temp"
@property @property
def native_value(self): def native_value(self) -> Decimal:
"""Return the current value of the measured temperature.""" """Return the current value of the measured temperature."""
return self._zone["measuredTemp"] return self._zone["measuredTemp"]

View File

@ -23,8 +23,9 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id] instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = [] entities: list[SwitchEntity] = []
for ac_key, ac_device in instance["coordinator"].data["aircons"].items(): if aircons := instance["coordinator"].data.get("aircons"):
for ac_key, ac_device in aircons.items():
if ac_device["info"]["freshAirStatus"] != "none": if ac_device["info"]["freshAirStatus"] != "none":
entities.append(AdvantageAirFreshAir(instance, ac_key)) entities.append(AdvantageAirFreshAir(instance, ac_key))
async_add_entities(entities) async_add_entities(entities)
@ -36,24 +37,24 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity):
_attr_icon = "mdi:air-filter" _attr_icon = "mdi:air-filter"
_attr_name = "Fresh air" _attr_name = "Fresh air"
def __init__(self, instance, ac_key): def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
"""Initialize an Advantage Air fresh air control.""" """Initialize an Advantage Air fresh air control."""
super().__init__(instance, ac_key) super().__init__(instance, ac_key)
self._attr_unique_id += "-freshair" self._attr_unique_id += "-freshair"
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return the fresh air status.""" """Return the fresh air status."""
return self._ac["freshAirStatus"] == ADVANTAGE_AIR_STATE_ON return self._ac["freshAirStatus"] == ADVANTAGE_AIR_STATE_ON
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn fresh air on.""" """Turn fresh air on."""
await self.async_change( await self.aircon(
{self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_ON}}} {self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_ON}}}
) )
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn fresh air off.""" """Turn fresh air off."""
await self.async_change( await self.aircon(
{self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}} {self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}}
) )

View File

@ -1,4 +1,6 @@
"""Advantage Air Update platform.""" """Advantage Air Update platform."""
from typing import Any
from homeassistant.components.update import UpdateEntity from homeassistant.components.update import UpdateEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -26,7 +28,7 @@ class AdvantageAirApp(AdvantageAirEntity, UpdateEntity):
_attr_name = "App" _attr_name = "App"
def __init__(self, instance): def __init__(self, instance: dict[str, Any]) -> None:
"""Initialize the Advantage Air App.""" """Initialize the Advantage Air App."""
super().__init__(instance) super().__init__(instance)
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
@ -40,12 +42,12 @@ class AdvantageAirApp(AdvantageAirEntity, UpdateEntity):
) )
@property @property
def installed_version(self): def installed_version(self) -> str:
"""Return the current app version.""" """Return the current app version."""
return self.coordinator.data["system"]["myAppRev"] return self.coordinator.data["system"]["myAppRev"]
@property @property
def latest_version(self): def latest_version(self) -> str:
"""Return if there is an update.""" """Return if there is an update."""
if self.coordinator.data["system"]["needsUpdate"]: if self.coordinator.data["system"]["needsUpdate"]:
return "Needs Update" return "Needs Update"

View File

@ -1,6 +1,8 @@
"""Test the Advantage Air Climate Platform.""" """Test the Advantage Air Climate Platform."""
from json import loads from json import loads
import pytest
from homeassistant.components.advantage_air.climate import ( from homeassistant.components.advantage_air.climate import (
HASS_FAN_MODES, HASS_FAN_MODES,
HASS_HVAC_MODES, HASS_HVAC_MODES,
@ -20,9 +22,10 @@ from homeassistant.components.climate.const import (
HVACMode, HVACMode,
) )
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from tests.components.advantage_air import ( from . import (
TEST_SET_RESPONSE, TEST_SET_RESPONSE,
TEST_SET_URL, TEST_SET_URL,
TEST_SYSTEM_DATA, TEST_SYSTEM_DATA,
@ -184,6 +187,7 @@ async def test_climate_async_failed_update(hass, aioclient_mock):
assert len(aioclient_mock.mock_calls) == 1 assert len(aioclient_mock.mock_calls) == 1
with pytest.raises(HomeAssistantError):
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,