From 010365591bfe2e4cb20f3f8e2fd42c9a80b91bad Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Wed, 26 Jun 2024 22:01:58 -0500 Subject: [PATCH 01/11] MAI: Migrate to new unit of measure constant Homeassistant Core has deprecated the TEMP_CELSIUS constant. The new constant is an ENUM which collects all temp units together. Closes https://github.com/bmcclure/ha-aquanta/issues/66 --- custom_components/aquanta/sensor.py | 6 +++--- custom_components/aquanta/water_heater.py | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/custom_components/aquanta/sensor.py b/custom_components/aquanta/sensor.py index f6b468c..fbbd86c 100644 --- a/custom_components/aquanta/sensor.py +++ b/custom_components/aquanta/sensor.py @@ -8,7 +8,7 @@ SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, PERCENTAGE +from homeassistant.const import UnitOfTemperature, PERCENTAGE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -23,7 +23,7 @@ name="Temperature", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:water-thermometer", ), "native_value": lambda entity: entity.coordinator.data["devices"][ @@ -38,7 +38,7 @@ name="Set point", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - native_unit_of_measurement=TEMP_CELSIUS, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, icon="mdi:thermometer-water", ), "native_value": lambda entity: entity.coordinator.data["devices"][ diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index e9bd814..05ac990 100644 --- a/custom_components/aquanta/water_heater.py +++ b/custom_components/aquanta/water_heater.py @@ -9,9 +9,7 @@ WaterHeaterEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - TEMP_CELSIUS, -) +from homeassistant.const import UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -41,7 +39,7 @@ class AquantaWaterHeater(AquantaEntity, WaterHeaterEntity): _attr_has_entity_name = True _attr_supported_features = WaterHeaterEntityFeature.AWAY_MODE - _attr_temperature_unit = TEMP_CELSIUS + _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_operation_list = [STATE_ECO, STATE_PERFORMANCE, STATE_OFF] _attr_name = "Water heater" From 56de89a65953f778f86fc566cc3b84dcc3ac9931 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Wed, 26 Jun 2024 23:08:27 -0500 Subject: [PATCH 02/11] NEW: Add Intelligence mode efficiency level sensor The API returns the Aquanta Intelligence settings "less efficient", "efficient" and "most efficient" as a fraction of 1. --- custom_components/aquanta/sensor.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/custom_components/aquanta/sensor.py b/custom_components/aquanta/sensor.py index fbbd86c..da38a7a 100644 --- a/custom_components/aquanta/sensor.py +++ b/custom_components/aquanta/sensor.py @@ -8,6 +8,7 @@ SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import EntityCategory from homeassistant.const import UnitOfTemperature, PERCENTAGE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -85,6 +86,21 @@ "off", ], }, + { + "desc": SensorEntityDescription( + key="efficiency_level", + name="Efficiency level", + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, + ), + "native_value": lambda entity: ( + entity.coordinator.data["devices"][entity.aquanta_id]["advanced"]["efficiencySelection"] + * 100 + ), + "suggested_precision": 1, + "options": None, + }, ) From cb048302dd0e99ead55fe45daf8eb3268a49aef1 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Thu, 4 Jul 2024 19:01:52 -0500 Subject: [PATCH 03/11] DOC: Add type hints --- custom_components/aquanta/coordinator.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/custom_components/aquanta/coordinator.py b/custom_components/aquanta/coordinator.py index 8465480..4deebbd 100644 --- a/custom_components/aquanta/coordinator.py +++ b/custom_components/aquanta/coordinator.py @@ -4,6 +4,7 @@ from datetime import timedelta import async_timeout +from aquanta import Aquanta from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import ( @@ -20,10 +21,10 @@ class AquantaCoordinator(DataUpdateCoordinator): config_entry: ConfigEntry - def __init__(self, hass: HomeAssistant, aquanta, account_id) -> None: + def __init__(self, hass: HomeAssistant, aquanta: Aquanta, account_id: str) -> None: """Initialize the coordinator.""" - self.aquanta = aquanta - self.account_id = account_id + self.aquanta: Aquanta = aquanta + self.account_id: str = account_id super().__init__( hass=hass, logger=LOGGER, From 66587d91a1f85685944274aadb13cde20f289698 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Thu, 4 Jul 2024 19:02:34 -0500 Subject: [PATCH 04/11] API: Remap operation states --- custom_components/aquanta/water_heater.py | 73 ++++++++++++++--------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index 05ac990..8612758 100644 --- a/custom_components/aquanta/water_heater.py +++ b/custom_components/aquanta/water_heater.py @@ -16,6 +16,11 @@ from .entity import AquantaEntity from .const import DOMAIN, LOGGER +STATE_INTELLIGENCE = "aquanta_intelligence" +STATE_SETPOINT = "setpoint" +STATE_TIME_OF_USE = "time_of_use" +STATE_TIMER = "manual_timer" +# Away is a special state in homeassistant async def async_setup_entry( hass: HomeAssistant, @@ -38,10 +43,23 @@ class AquantaWaterHeater(AquantaEntity, WaterHeaterEntity): """Representation of an Aquanta water heater controller.""" _attr_has_entity_name = True + # TODO: Enable setting operation mode from homeassistant _attr_supported_features = WaterHeaterEntityFeature.AWAY_MODE _attr_temperature_unit = UnitOfTemperature.CELSIUS - _attr_operation_list = [STATE_ECO, STATE_PERFORMANCE, STATE_OFF] + _attr_operation_list: list[str] = [ + STATE_INTELLIGENCE, + STATE_OFF, + STATE_PERFORMANCE, + STATE_SETPOINT, + STATE_TIME_OF_USE, + STATE_TIMER, + ] _attr_name = "Water heater" + # The settable temp range (in celsius) according to Aquanta App + _attr_max_temp: float = 110.0 + _attr_min_temp: float = 10.0 + _attr_target_temperature_high: None = None + _attr_target_temperature_low: None = None def __init__(self, coordinator, aquanta_id) -> None: """Initialize the water heater.""" @@ -51,41 +69,40 @@ def __init__(self, coordinator, aquanta_id) -> None: LOGGER.debug("Created water heater with unique ID %s", self._attr_unique_id) @property - def current_temperature(self): + def current_temperature(self) -> float | None: """Return the current temperature.""" return self.coordinator.data["devices"][self.aquanta_id]["water"]["temperature"] @property - def current_operation(self): + def current_operation(self) -> str: """Return current operation ie. eco, performance, off.""" - operation = STATE_OFF - - if ( - self.coordinator.data["devices"][self.aquanta_id]["info"]["currentMode"][ - "type" - ] - != "off" - ): - found = False - - for record in self.coordinator.data["devices"][self.aquanta_id]["info"][ - "records" - ]: - if record["type"] == "boost" and record["state"] == "ongoing": - operation = STATE_PERFORMANCE - found = True - elif record["type"] == "away" and record["state"] == "ongoing": - operation = STATE_OFF - found = True - break - - if not found: - operation = STATE_ECO - + mode_type = self.coordinator.data["devices"][self.aquanta_id]["info"]["currentMode"]["type"] + # Since and boost/away modes are special temporary states which preempt + # other operations states, we need to sometimes look at the records + record_types = [record['type'] for record in self.coordinator.data["devices"][self.aquanta_id]["info"]['record']] + LOGGER.debug("Aquanta API reports current mode: {mode} with records of type: {record_types}.") + + operation: str = STATE_SETPOINT + + if mode_type == 'off': + # Turning Aquanta "off" actually reverts to the non-smart + # controller; it doesn't actually disable the water heater. "Away" + # is the closest state to "off" that Aquanta can provide. + operation = STATE_SETPOINT + elif mode_type == 'boost': + operation = STATE_PERFORMANCE + elif "intel" in record_types: + operation = STATE_INTELLIGENCE + elif "timer" in record_types: + operation = STATE_TIMER + elif "tou" in record_types: + operation = STATE_TIME_OF_USE + + LOGGER.debug("The resolved operation mode is {operation}.") return operation @property - def target_temperature(self): + def target_temperature(self) -> float | None: """Return the temperature we try to reach.""" if self.coordinator.data["devices"][self.aquanta_id]["advanced"][ "thermostatEnabled" From dad9af42b57024e0e79cd0c5a41e2edd6c0ffbed Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Thu, 4 Jul 2024 19:46:30 -0500 Subject: [PATCH 05/11] BUG: Fix incorrect key --- custom_components/aquanta/water_heater.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index 8612758..3a55056 100644 --- a/custom_components/aquanta/water_heater.py +++ b/custom_components/aquanta/water_heater.py @@ -1,4 +1,5 @@ """Aquanta water heater component.""" + from __future__ import annotations from homeassistant.components.water_heater import ( @@ -22,6 +23,7 @@ STATE_TIMER = "manual_timer" # Away is a special state in homeassistant + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -39,6 +41,7 @@ async def async_setup_entry( async_add_entities(entities) + class AquantaWaterHeater(AquantaEntity, WaterHeaterEntity): """Representation of an Aquanta water heater controller.""" @@ -76,20 +79,29 @@ def current_temperature(self) -> float | None: @property def current_operation(self) -> str: """Return current operation ie. eco, performance, off.""" - mode_type = self.coordinator.data["devices"][self.aquanta_id]["info"]["currentMode"]["type"] + mode_type = self.coordinator.data["devices"][self.aquanta_id]["info"][ + "currentMode" + ]["type"] # Since and boost/away modes are special temporary states which preempt # other operations states, we need to sometimes look at the records - record_types = [record['type'] for record in self.coordinator.data["devices"][self.aquanta_id]["info"]['record']] - LOGGER.debug("Aquanta API reports current mode: {mode} with records of type: {record_types}.") + record_types = [ + record["type"] + for record in self.coordinator.data["devices"][self.aquanta_id]["info"][ + "records" + ] + ] + LOGGER.debug( + "Aquanta API reports current mode: {mode} with records of type: {record_types}." + ) operation: str = STATE_SETPOINT - if mode_type == 'off': + if mode_type == "off": # Turning Aquanta "off" actually reverts to the non-smart # controller; it doesn't actually disable the water heater. "Away" # is the closest state to "off" that Aquanta can provide. operation = STATE_SETPOINT - elif mode_type == 'boost': + elif mode_type == "boost": operation = STATE_PERFORMANCE elif "intel" in record_types: operation = STATE_INTELLIGENCE From 84b862bca57bb3569a56cff919b596e32fe38327 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Thu, 4 Jul 2024 20:26:38 -0500 Subject: [PATCH 06/11] BUG: Fix log formatting --- custom_components/aquanta/water_heater.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index 3a55056..3ae38a5 100644 --- a/custom_components/aquanta/water_heater.py +++ b/custom_components/aquanta/water_heater.py @@ -91,7 +91,9 @@ def current_operation(self) -> str: ] ] LOGGER.debug( - "Aquanta API reports current mode: {mode} with records of type: {record_types}." + "Aquanta API reports current mode: %s with records of type: %s.", + mode_type, + record_types, ) operation: str = STATE_SETPOINT From 50b463d23913769b73300ae325185c4a7b8e8245 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Thu, 4 Jul 2024 20:41:03 -0500 Subject: [PATCH 07/11] BUG: Fix incorrect logging statement --- custom_components/aquanta/water_heater.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index 3ae38a5..b78ee85 100644 --- a/custom_components/aquanta/water_heater.py +++ b/custom_components/aquanta/water_heater.py @@ -112,7 +112,7 @@ def current_operation(self) -> str: elif "tou" in record_types: operation = STATE_TIME_OF_USE - LOGGER.debug("The resolved operation mode is {operation}.") + LOGGER.debug("The resolved operation mode is %s.", operation) return operation @property From 42d3c58808de26b0f3bddbbaba1b61f50e39c581 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Thu, 4 Jul 2024 20:41:35 -0500 Subject: [PATCH 08/11] DOC: Use pretty names for states --- custom_components/aquanta/water_heater.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index b78ee85..dab2c2e 100644 --- a/custom_components/aquanta/water_heater.py +++ b/custom_components/aquanta/water_heater.py @@ -17,10 +17,10 @@ from .entity import AquantaEntity from .const import DOMAIN, LOGGER -STATE_INTELLIGENCE = "aquanta_intelligence" -STATE_SETPOINT = "setpoint" -STATE_TIME_OF_USE = "time_of_use" -STATE_TIMER = "manual_timer" +STATE_INTELLIGENCE = "Aquanta Intelligence" +STATE_SETPOINT = "Setpoint" +STATE_TIME_OF_USE = "Time of Use" +STATE_TIMER = "Manual Timer" # Away is a special state in homeassistant From 0a8fb8b7e2ce246bff0574e17f1f58d79a087106 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Thu, 4 Jul 2024 20:59:39 -0500 Subject: [PATCH 09/11] REF: Remove redundant states --- custom_components/aquanta/water_heater.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index dab2c2e..e7353dd 100644 --- a/custom_components/aquanta/water_heater.py +++ b/custom_components/aquanta/water_heater.py @@ -3,9 +3,6 @@ from __future__ import annotations from homeassistant.components.water_heater import ( - STATE_ECO, - STATE_PERFORMANCE, - STATE_OFF, WaterHeaterEntity, WaterHeaterEntityFeature, ) @@ -51,8 +48,6 @@ class AquantaWaterHeater(AquantaEntity, WaterHeaterEntity): _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_operation_list: list[str] = [ STATE_INTELLIGENCE, - STATE_OFF, - STATE_PERFORMANCE, STATE_SETPOINT, STATE_TIME_OF_USE, STATE_TIMER, @@ -103,8 +98,6 @@ def current_operation(self) -> str: # controller; it doesn't actually disable the water heater. "Away" # is the closest state to "off" that Aquanta can provide. operation = STATE_SETPOINT - elif mode_type == "boost": - operation = STATE_PERFORMANCE elif "intel" in record_types: operation = STATE_INTELLIGENCE elif "timer" in record_types: From 9ac89da168f9aa4dac72c10c782bb70f416c4cc2 Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Sun, 7 Jul 2024 18:00:04 -0500 Subject: [PATCH 10/11] REF: Merge imports from same module --- custom_components/aquanta/sensor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/custom_components/aquanta/sensor.py b/custom_components/aquanta/sensor.py index da38a7a..39178f7 100644 --- a/custom_components/aquanta/sensor.py +++ b/custom_components/aquanta/sensor.py @@ -8,8 +8,7 @@ SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import EntityCategory -from homeassistant.const import UnitOfTemperature, PERCENTAGE +from homeassistant.const import UnitOfTemperature, PERCENTAGE, EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback From 9c2bb671dec80d02f6a87e9c6feebce13126ba3b Mon Sep 17 00:00:00 2001 From: Daniel Ching Date: Sun, 7 Jul 2024 18:15:20 -0500 Subject: [PATCH 11/11] DOC: Reduce suggested precision for efficiency level --- custom_components/aquanta/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/aquanta/sensor.py b/custom_components/aquanta/sensor.py index 5c0c3f7..61ab490 100644 --- a/custom_components/aquanta/sensor.py +++ b/custom_components/aquanta/sensor.py @@ -99,7 +99,7 @@ entity.coordinator.data["devices"][entity.aquanta_id]["advanced"]["efficiencySelection"] * 100 ), - "suggested_precision": 1, + "suggested_precision": 0, "options": None, }, )