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.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
@ -60,7 +61,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if await func(param):
await coordinator.async_refresh()
except ApiError as err:
_LOGGER.warning(err)
raise HomeAssistantError(err) from err
return error_handle
@ -69,8 +70,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator,
"async_change": error_handle_factory(api.aircon.async_set),
"async_set_light": error_handle_factory(api.lights.async_set),
"aircon": error_handle_factory(api.aircon.async_set),
"lights": error_handle_factory(api.lights.async_set),
}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

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

View File

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

View File

@ -1,9 +1,14 @@
"""Config Flow for Advantage Air integration."""
from __future__ import annotations
from typing import Any
from advantage_air import ApiError, advantage_air
import voluptuous as vol
from homeassistant import config_entries
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 .const import ADVANTAGE_AIR_RETRY, DOMAIN
@ -25,7 +30,9 @@ class AdvantageAirConfigFlow(config_entries.ConfigFlow, 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."""
errors = {}
if user_input:

View File

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

View File

@ -1,5 +1,7 @@
"""Advantage Air parent entity class."""
from typing import Any
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -11,20 +13,20 @@ class AdvantageAirEntity(CoordinatorEntity):
_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."""
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):
"""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."""
super().__init__(instance)
self.async_change = instance["async_change"]
self.ac_key = ac_key
self.aircon = instance["aircon"]
self.ac_key: str = ac_key
self._attr_unique_id += f"-{ac_key}"
self._attr_device_info = DeviceInfo(
@ -36,19 +38,19 @@ class AdvantageAirAcEntity(AdvantageAirEntity):
)
@property
def _ac(self):
def _ac(self) -> dict[str, Any]:
return self.coordinator.data["aircons"][self.ac_key]["info"]
class AdvantageAirZoneEntity(AdvantageAirAcEntity):
"""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."""
super().__init__(instance, ac_key)
self.zone_key = zone_key
self.zone_key: str = zone_key
self._attr_unique_id += f"-{zone_key}"
@property
def _zone(self):
def _zone(self) -> dict[str, Any]:
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]
entities = []
if "myLights" in instance["coordinator"].data:
for light in instance["coordinator"].data["myLights"]["lights"].values():
entities: list[LightEntity] = []
if my_lights := instance["coordinator"].data.get("myLights"):
for light in my_lights["lights"].values():
if light.get("relay"):
entities.append(AdvantageAirLight(instance, light))
else:
@ -39,11 +39,11 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
_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."""
super().__init__(instance)
self.async_set_light = instance["async_set_light"]
self._id = light["id"]
self.lights = instance["lights"]
self._id: str = light["id"]
self._attr_unique_id += f"-{self._id}"
self._attr_device_info = DeviceInfo(
identifiers={(ADVANTAGE_AIR_DOMAIN, self._attr_unique_id)},
@ -54,7 +54,7 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
)
@property
def _light(self):
def _light(self) -> dict[str, Any]:
"""Return the light object."""
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:
"""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:
"""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):
@ -84,7 +84,7 @@ class AdvantageAirLightDimmable(AdvantageAirLight):
async def async_turn_on(self, **kwargs: Any) -> None:
"""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:
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."""
from typing import Any
from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -19,9 +21,10 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = []
for ac_key in instance["coordinator"].data["aircons"]:
entities.append(AdvantageAirMyZone(instance, ac_key))
entities: list[SelectEntity] = []
if aircons := instance["coordinator"].data.get("aircons"):
for ac_key in aircons:
entities.append(AdvantageAirMyZone(instance, ac_key))
async_add_entities(entities)
@ -31,7 +34,7 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
_attr_icon = "mdi:home-thermometer"
_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."""
super().__init__(instance, ac_key)
self._attr_unique_id += "-myzone"
@ -52,6 +55,6 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
async def async_select_option(self, option: str) -> None:
"""Set the MyZone."""
await self.async_change(
await self.aircon(
{self.ac_key: {"info": {"myZone": self._name_to_number[option]}}}
)

View File

@ -1,6 +1,9 @@
"""Sensor platform for Advantage Air integration."""
from __future__ import annotations
from decimal import Decimal
from typing import Any
import voluptuous as vol
from homeassistant.components.sensor import (
@ -35,17 +38,18 @@ async def async_setup_entry(
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities: list[SensorEntity] = []
for ac_key, ac_device in instance["coordinator"].data["aircons"].items():
entities.append(AdvantageAirTimeTo(instance, ac_key, "On"))
entities.append(AdvantageAirTimeTo(instance, ac_key, "Off"))
for zone_key, zone in ac_device["zones"].items():
# Only show damper and temp sensors when zone is in temperature control
if zone["type"] != 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key))
entities.append(AdvantageAirZoneTemp(instance, ac_key, zone_key))
# Only show wireless signal strength sensors when using wireless sensors
if zone["rssi"] > 0:
entities.append(AdvantageAirZoneSignal(instance, ac_key, zone_key))
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, "Off"))
for zone_key, zone in ac_device["zones"].items():
# Only show damper and temp sensors when zone is in temperature control
if zone["type"] != 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key))
entities.append(AdvantageAirZoneTemp(instance, ac_key, zone_key))
# Only show wireless signal strength sensors when using wireless sensors
if zone["rssi"] > 0:
entities.append(AdvantageAirZoneSignal(instance, ac_key, zone_key))
async_add_entities(entities)
platform = entity_platform.async_get_current_platform()
@ -62,7 +66,7 @@ class AdvantageAirTimeTo(AdvantageAirAcEntity, SensorEntity):
_attr_native_unit_of_measurement = ADVANTAGE_AIR_SET_COUNTDOWN_UNIT
_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."""
super().__init__(instance, ac_key)
self.action = action
@ -71,21 +75,21 @@ class AdvantageAirTimeTo(AdvantageAirAcEntity, SensorEntity):
self._attr_unique_id += f"-timeto{action}"
@property
def native_value(self):
def native_value(self) -> Decimal:
"""Return the current value."""
return self._ac[self._time_key]
@property
def icon(self):
def icon(self) -> str:
"""Return a representative icon of the timer."""
if self._ac[self._time_key] > 0:
return "mdi:timer-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."""
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):
@ -95,21 +99,21 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, SensorEntity):
_attr_state_class = SensorStateClass.MEASUREMENT
_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."""
super().__init__(instance, ac_key, zone_key=zone_key)
self._attr_name = f'{self._zone["name"]} vent'
self._attr_unique_id += "-vent"
@property
def native_value(self):
def native_value(self) -> Decimal:
"""Return the current value of the air vent."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return self._zone["value"]
return 0
return Decimal(0)
@property
def icon(self):
def icon(self) -> str:
"""Return a representative icon."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return "mdi:fan"
@ -123,19 +127,19 @@ class AdvantageAirZoneSignal(AdvantageAirZoneEntity, SensorEntity):
_attr_state_class = SensorStateClass.MEASUREMENT
_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."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} signal'
self._attr_unique_id += "-signal"
@property
def native_value(self):
def native_value(self) -> Decimal:
"""Return the current value of the wireless signal."""
return self._zone["rssi"]
@property
def icon(self):
def icon(self) -> str:
"""Return a representative icon."""
if self._zone["rssi"] >= 80:
return "mdi:wifi-strength-4"
@ -157,13 +161,13 @@ class AdvantageAirZoneTemp(AdvantageAirZoneEntity, SensorEntity):
_attr_entity_registry_enabled_default = False
_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."""
super().__init__(instance, ac_key, zone_key)
self._attr_name = f'{self._zone["name"]} temperature'
self._attr_unique_id += "-temp"
@property
def native_value(self):
def native_value(self) -> Decimal:
"""Return the current value of the measured temperature."""
return self._zone["measuredTemp"]

View File

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

View File

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

View File

@ -1,6 +1,8 @@
"""Test the Advantage Air Climate Platform."""
from json import loads
import pytest
from homeassistant.components.advantage_air.climate import (
HASS_FAN_MODES,
HASS_HVAC_MODES,
@ -20,9 +22,10 @@ from homeassistant.components.climate.const import (
HVACMode,
)
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from tests.components.advantage_air import (
from . import (
TEST_SET_RESPONSE,
TEST_SET_URL,
TEST_SYSTEM_DATA,
@ -184,12 +187,13 @@ async def test_climate_async_failed_update(hass, aioclient_mock):
assert len(aioclient_mock.mock_calls) == 1
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: ["climate.ac_one"], ATTR_TEMPERATURE: 25},
blocking=True,
)
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: ["climate.ac_one"], ATTR_TEMPERATURE: 25},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 2
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/setAircon"