diff --git a/homeassistant/components/advantage_air/__init__.py b/homeassistant/components/advantage_air/__init__.py index a6efc479715..7eccc4d05c5 100644 --- a/homeassistant/components/advantage_air/__init__.py +++ b/homeassistant/components/advantage_air/__init__.py @@ -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"], + } diff --git a/homeassistant/components/advantage_air/climate.py b/homeassistant/components/advantage_air/climate.py index dfb30b2b782..c829824c94f 100644 --- a/homeassistant/components/advantage_air/climate.py +++ b/homeassistant/components/advantage_air/climate.py @@ -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.""" diff --git a/homeassistant/components/advantage_air/cover.py b/homeassistant/components/advantage_air/cover.py index 3c9d7540212..a238a9cdca8 100644 --- a/homeassistant/components/advantage_air/cover.py +++ b/homeassistant/components/advantage_air/cover.py @@ -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( diff --git a/tests/components/advantage_air/test_climate.py b/tests/components/advantage_air/test_climate.py index 517750aa98b..7eb1729a5db 100644 --- a/tests/components/advantage_air/test_climate.py +++ b/tests/components/advantage_air/test_climate.py @@ -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