Code Quality improvements for Advantage Air integration (#41861)

* Readability improvements

* Code quality and more tests

* Created a parent entity

* Apply parent entity to cover platform

* Update _zone property

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Remove device_state_attributes

* Correct attribute tests

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Brett 2020-10-16 14:30:29 +10:00 committed by GitHub
parent 1e0663e986
commit 731d617c5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 107 deletions

View File

@ -8,7 +8,11 @@ from advantage_air import ApiError, advantage_air
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
UpdateFailed,
)
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
@ -72,3 +76,35 @@ async def async_setup_entry(hass, config_entry):
)
return True
class AdvantageAirEntity(CoordinatorEntity):
"""Parent class for Advantage Air Entities."""
def __init__(self, instance, ac_key, zone_key=None):
"""Initialize common aspects of an Advantage Air sensor."""
super().__init__(instance["coordinator"])
self.async_change = instance["async_change"]
self.ac_key = ac_key
self.zone_key = zone_key
@property
def _ac(self):
return self.coordinator.data["aircons"][self.ac_key]["info"]
@property
def _zone(self):
if not self.zone_key:
return None
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key]
@property
def device_info(self):
"""Return parent device information."""
return {
"identifiers": {(DOMAIN, self.coordinator.data["system"]["rid"])},
"name": self.coordinator.data["system"]["name"],
"manufacturer": "Advantage Air",
"model": self.coordinator.data["system"]["sysType"],
"sw_version": self.coordinator.data["system"]["myAppRev"],
}

View File

@ -1,13 +1,12 @@
"""Climate platform for Advantage Air integration."""
import logging
from homeassistant.components.advantage_air import AdvantageAirEntity
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
FAN_MEDIUM,
FAN_OFF,
HVAC_MODE_COOL,
HVAC_MODE_DRY,
HVAC_MODE_FAN_ONLY,
@ -17,7 +16,6 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
ADVANTAGE_AIR_STATE_CLOSE,
@ -53,8 +51,6 @@ AC_HVAC_MODES = [
]
ZONE_HVAC_MODES = [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY]
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up AdvantageAir climate platform."""
@ -62,28 +58,18 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
instance = hass.data[DOMAIN][config_entry.entry_id]
entities = []
for ac_key in instance["coordinator"].data["aircons"]:
for ac_key, ac_device in instance["coordinator"].data["aircons"].items():
entities.append(AdvantageAirAC(instance, ac_key))
for zone_key in instance["coordinator"].data["aircons"][ac_key]["zones"]:
for zone_key, zone in ac_device["zones"].items():
# Only add zone climate control when zone is in temperature control
if (
instance["coordinator"].data["aircons"][ac_key]["zones"][zone_key][
"type"
]
!= 0
):
if zone["type"] != 0:
entities.append(AdvantageAirZone(instance, ac_key, zone_key))
async_add_entities(entities)
class AdvantageAirClimateEntity(CoordinatorEntity, ClimateEntity):
class AdvantageAirClimateEntity(AdvantageAirEntity, ClimateEntity):
"""AdvantageAir Climate class."""
def __init__(self, instance):
"""Initialize the base Advantage Air climate entity."""
super().__init__(instance["coordinator"])
self.async_change = instance["async_change"]
@property
def temperature_unit(self):
"""Return the temperature unit."""
@ -104,30 +90,14 @@ class AdvantageAirClimateEntity(CoordinatorEntity, ClimateEntity):
"""Return the minimum supported temperature."""
return 16
@property
def device_info(self):
"""Return parent device information."""
return {
"identifiers": {(DOMAIN, self.coordinator.data["system"]["rid"])},
"name": self.coordinator.data["system"]["name"],
"manufacturer": "Advantage Air",
"model": self.coordinator.data["system"]["sysType"],
"sw_version": self.coordinator.data["system"]["myAppRev"],
}
class AdvantageAirAC(AdvantageAirClimateEntity):
"""AdvantageAir AC unit."""
def __init__(self, instance, ac_key):
"""Initialize the Advantage Air AC climate entity."""
super().__init__(instance)
self.ac_key = ac_key
@property
def name(self):
"""Return the name."""
return self.coordinator.data["aircons"][self.ac_key]["info"]["name"]
return self._ac["name"]
@property
def unique_id(self):
@ -137,18 +107,13 @@ class AdvantageAirAC(AdvantageAirClimateEntity):
@property
def target_temperature(self):
"""Return the current target temperature."""
return self.coordinator.data["aircons"][self.ac_key]["info"]["setTemp"]
return self._ac["setTemp"]
@property
def hvac_mode(self):
"""Return the current HVAC modes."""
if (
self.coordinator.data["aircons"][self.ac_key]["info"]["state"]
== ADVANTAGE_AIR_STATE_ON
):
return ADVANTAGE_AIR_HVAC_MODES.get(
self.coordinator.data["aircons"][self.ac_key]["info"]["mode"]
)
if self._ac["state"] == ADVANTAGE_AIR_STATE_ON:
return ADVANTAGE_AIR_HVAC_MODES.get(self._ac["mode"])
return HVAC_MODE_OFF
@property
@ -159,9 +124,7 @@ class AdvantageAirAC(AdvantageAirClimateEntity):
@property
def fan_mode(self):
"""Return the current fan modes."""
return ADVANTAGE_AIR_FAN_MODES.get(
self.coordinator.data["aircons"][self.ac_key]["info"]["fan"], FAN_OFF
)
return ADVANTAGE_AIR_FAN_MODES.get(self._ac["fan"])
@property
def fan_modes(self):
@ -173,11 +136,6 @@ class AdvantageAirAC(AdvantageAirClimateEntity):
"""Return the supported features."""
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
@property
def device_state_attributes(self):
"""Return additional attributes about AC unit."""
return self.coordinator.data["aircons"][self.ac_key]["info"]
async def async_set_hvac_mode(self, hvac_mode):
"""Set the HVAC Mode and State."""
if hvac_mode == HVAC_MODE_OFF:
@ -211,18 +169,10 @@ class AdvantageAirAC(AdvantageAirClimateEntity):
class AdvantageAirZone(AdvantageAirClimateEntity):
"""AdvantageAir Zone control."""
def __init__(self, instance, ac_key, zone_key):
"""Initialize the Advantage Air Zone climate entity."""
super().__init__(instance)
self.ac_key = ac_key
self.zone_key = zone_key
@property
def name(self):
"""Return the name."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"name"
]
return self._zone["name"]
@property
def unique_id(self):
@ -232,26 +182,17 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
@property
def current_temperature(self):
"""Return the current temperature."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"measuredTemp"
]
return self._zone["measuredTemp"]
@property
def target_temperature(self):
"""Return the target temperature."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"setTemp"
]
return self._zone["setTemp"]
@property
def hvac_mode(self):
"""Return the current HVAC modes."""
if (
self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"state"
]
== ADVANTAGE_AIR_STATE_OPEN
):
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return HVAC_MODE_FAN_ONLY
return HVAC_MODE_OFF
@ -260,11 +201,6 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
"""Return supported HVAC modes."""
return ZONE_HVAC_MODES
@property
def device_state_attributes(self):
"""Return additional attributes about Zone."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key]
@property
def supported_features(self):
"""Return the supported features."""

View File

@ -1,5 +1,6 @@
"""Cover platform for Advantage Air integration."""
from homeassistant.components.advantage_air import AdvantageAirEntity
from homeassistant.components.cover import (
ATTR_POSITION,
DEVICE_CLASS_DAMPER,
@ -8,7 +9,6 @@ from homeassistant.components.cover import (
SUPPORT_SET_POSITION,
CoverEntity,
)
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import ADVANTAGE_AIR_STATE_CLOSE, ADVANTAGE_AIR_STATE_OPEN, DOMAIN
@ -27,20 +27,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_entities(entities)
class AdvantageAirZoneVent(CoordinatorEntity, CoverEntity):
class AdvantageAirZoneVent(AdvantageAirEntity, CoverEntity):
"""Advantage Air Cover Class."""
def __init__(self, instance, ac_key, zone_key):
"""Initialize the Advantage Air Zone Vent cover entity."""
super().__init__(instance["coordinator"])
self.async_change = instance["async_change"]
self.ac_key = ac_key
self.zone_key = zone_key
@property
def _zone(self):
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key]
@property
def name(self):
"""Return the name."""
@ -73,17 +62,6 @@ class AdvantageAirZoneVent(CoordinatorEntity, CoverEntity):
return self._zone["value"]
return 0
@property
def device_info(self):
"""Return parent device information."""
return {
"identifiers": {(DOMAIN, self.coordinator.data["system"]["rid"])},
"name": self.coordinator.data["system"]["name"],
"manufacturer": "Advantage Air",
"model": self.coordinator.data["system"]["sysType"],
"sw_version": self.coordinator.data["system"]["myAppRev"],
}
async def async_open_cover(self, **kwargs):
"""Fully open zone vent."""
await self.async_change(

View File

@ -1,10 +1,20 @@
"""Test the Advantage Air Climate Platform."""
from json import loads
from homeassistant.components.advantage_air.climate import (
HASS_FAN_MODES,
HASS_HVAC_MODES,
)
from homeassistant.components.advantage_air.const import (
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
)
from homeassistant.components.climate.const import (
ATTR_FAN_MODE,
ATTR_HVAC_MODE,
DOMAIN as CLIMATE_DOMAIN,
FAN_OFF,
FAN_LOW,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_OFF,
SERVICE_SET_FAN_MODE,
@ -46,6 +56,7 @@ async def test_climate_async_setup_entry(hass, aioclient_mock):
assert state.state == HVAC_MODE_FAN_ONLY
assert state.attributes.get("min_temp") == 16
assert state.attributes.get("max_temp") == 32
assert state.attributes.get("temperature") == 24
assert state.attributes.get("current_temperature") is None
entry = registry.async_get(entity_id)
@ -61,6 +72,9 @@ async def test_climate_async_setup_entry(hass, aioclient_mock):
assert len(aioclient_mock.mock_calls) == 3
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["state"] == ADVANTAGE_AIR_STATE_ON
assert data["ac1"]["info"]["mode"] == HASS_HVAC_MODES[HVAC_MODE_FAN_ONLY]
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
@ -73,18 +87,22 @@ async def test_climate_async_setup_entry(hass, aioclient_mock):
assert len(aioclient_mock.mock_calls) == 5
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["state"] == ADVANTAGE_AIR_STATE_OFF
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_FAN_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_FAN_MODE: FAN_OFF},
{ATTR_ENTITY_ID: [entity_id], ATTR_FAN_MODE: FAN_LOW},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 7
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["fan"] == HASS_FAN_MODES[FAN_LOW]
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
@ -97,6 +115,8 @@ async def test_climate_async_setup_entry(hass, aioclient_mock):
assert len(aioclient_mock.mock_calls) == 9
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["setTemp"] == 25
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
@ -106,8 +126,8 @@ async def test_climate_async_setup_entry(hass, aioclient_mock):
assert state
assert state.attributes.get("min_temp") == 16
assert state.attributes.get("max_temp") == 32
assert state.attributes.get("measuredTemp") == 25
assert state.attributes.get("setTemp") == 24
assert state.attributes.get("temperature") == 24
assert state.attributes.get("current_temperature") == 25
entry = registry.async_get(entity_id)
assert entry