diff --git a/custom_components/aquanta/coordinator.py b/custom_components/aquanta/coordinator.py index d189a43..8f1c866 100644 --- a/custom_components/aquanta/coordinator.py +++ b/custom_components/aquanta/coordinator.py @@ -5,6 +5,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 ( @@ -21,10 +22,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, diff --git a/custom_components/aquanta/sensor.py b/custom_components/aquanta/sensor.py index 6b96d19..61ab490 100644 --- a/custom_components/aquanta/sensor.py +++ b/custom_components/aquanta/sensor.py @@ -9,7 +9,7 @@ SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -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 @@ -87,6 +87,21 @@ "timer", ], }, + { + "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": 0, + "options": None, + }, ) diff --git a/custom_components/aquanta/water_heater.py b/custom_components/aquanta/water_heater.py index 6e9240a..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, ) @@ -17,6 +14,12 @@ 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, @@ -35,14 +38,26 @@ async def async_setup_entry( async_add_entities(entities) + 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_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.""" @@ -52,41 +67,49 @@ 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 - + 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"][ "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 + ] + ] + LOGGER.debug( + "Aquanta API reports current mode: %s with records of type: %s.", + mode_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 "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 %s.", 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"