From 2eae4bd8e01333ee0da1fa5e49c56eef35c67371 Mon Sep 17 00:00:00 2001 From: Vincent Wolsink Date: Thu, 24 Aug 2023 17:13:21 +0200 Subject: [PATCH] Add natural mode and energy saving for Piazetta stoves (and maybe others) --- custom_components/aguaiot/climate.py | 8 +-- custom_components/aguaiot/const.py | 38 ++++++++++- custom_components/aguaiot/manifest.json | 4 +- custom_components/aguaiot/number.py | 75 ++++++++++++++++++++++ custom_components/aguaiot/switch.py | 84 +++++++++++++++++++++++++ 5 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 custom_components/aguaiot/number.py create mode 100644 custom_components/aguaiot/switch.py diff --git a/custom_components/aguaiot/climate.py b/custom_components/aguaiot/climate.py index 47f5c2c..b45e9a9 100644 --- a/custom_components/aguaiot/climate.py +++ b/custom_components/aguaiot/climate.py @@ -54,7 +54,7 @@ def __init__(self, coordinator, device): CoordinatorEntity.__init__(self, coordinator) self._device = device - if self._device.water_temperature and self._device.water_temperature > 0: + if self._device.water_temp and self._device.water_temp > 0: self._device_type = DEVICE_TYPE_WATER else: self._device_type = DEVICE_TYPE_AIR @@ -121,12 +121,12 @@ def max_temp(self): @property def current_temperature(self): """Return the current temperature.""" - return getattr(self._device, f"{self._device_type}_temperature") + return getattr(self._device, f"{self._device_type}_temp") @property def target_temperature(self): """Return the temperature we try to reach.""" - return getattr(self._device, f"set_{self._device_type}_temperature") + return getattr(self._device, f"set_{self._device_type}_temp") @property def hvac_mode(self): @@ -186,7 +186,7 @@ async def async_set_temperature(self, **kwargs): await self.hass.async_add_executor_job( setattr, self._device, - f"set_{self._device_type}_temperature", + f"set_{self._device_type}_temp", temperature, ) await self.coordinator.async_request_refresh() diff --git a/custom_components/aguaiot/const.py b/custom_components/aguaiot/const.py index dbd55c3..eba1b49 100644 --- a/custom_components/aguaiot/const.py +++ b/custom_components/aguaiot/const.py @@ -6,6 +6,14 @@ SensorEntityDescription, SensorStateClass, ) +from homeassistant.components.switch import ( + SwitchDeviceClass, + SwitchEntityDescription, +) +from homeassistant.components.number import ( + NumberDeviceClass, + NumberEntityDescription, +) DOMAIN = "aguaiot" @@ -38,8 +46,10 @@ DEVICE_TYPE_WATER = "water" PLATFORMS = [ - Platform.SENSOR, Platform.CLIMATE, + Platform.SENSOR, + Platform.SWITCH, + Platform.NUMBER, ] UPDATE_INTERVAL = 60 @@ -61,3 +71,29 @@ device_class=None, ), ) + +SWITCHES = ( + SwitchEntityDescription( + key="natural_mode", + name="Natural Mode", + icon="mdi:fan-off", + device_class=SwitchDeviceClass.SWITCH, + ), +) + +NUMBERS = ( + NumberEntityDescription( + key="energy_saving_air_start", + name="Energy Saving Start", + native_step=1, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=NumberDeviceClass.TEMPERATURE, + ), + NumberEntityDescription( + key="energy_saving_air_stop", + name="Energy Saving Stop", + native_step=1, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + device_class=NumberDeviceClass.TEMPERATURE, + ), +) diff --git a/custom_components/aguaiot/manifest.json b/custom_components/aguaiot/manifest.json index 9c9bbef..4095113 100644 --- a/custom_components/aguaiot/manifest.json +++ b/custom_components/aguaiot/manifest.json @@ -9,6 +9,6 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/vincentwolsink/home_assistant_micronova_agua_iot/issues", "loggers": ["py_agua_iot"], - "requirements": ["py-agua-iot==0.0.18"], - "version": "0.1.7" + "requirements": ["py-agua-iot==0.0.19"], + "version": "0.1.8" } diff --git a/custom_components/aguaiot/number.py b/custom_components/aguaiot/number.py new file mode 100644 index 0000000..67abc28 --- /dev/null +++ b/custom_components/aguaiot/number.py @@ -0,0 +1,75 @@ +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) +from homeassistant.components.number import NumberEntity +from homeassistant.helpers.entity import DeviceInfo +from .const import NUMBERS, DOMAIN + + +async def async_setup_entry(hass, entry, async_add_entities): + coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ + "coordinator" + ] + agua = hass.data[DOMAIN][entry.entry_id]["agua"] + + numbers = [] + for device in agua.devices: + for number in NUMBERS: + if getattr(device, number.key, None) is not None: + numbers.append(AguaIOTHeatingNumber(coordinator, device, number)) + + async_add_entities(numbers, True) + + +class AguaIOTHeatingNumber(CoordinatorEntity, NumberEntity): + def __init__(self, coordinator, device, description): + """Initialize the thermostat.""" + CoordinatorEntity.__init__(self, coordinator) + self._device = device + self.entity_description = description + + @property + def unique_id(self): + """Return a unique ID.""" + return f"{self._device.id_device}_{self.entity_description.key}" + + @property + def name(self): + """Return the name of the device, if any.""" + return f"{self._device.name} {self.entity_description.name}" + + @property + def device_info(self): + """Return the device info.""" + return DeviceInfo( + identifiers={(DOMAIN, self._device.id_device)}, + name=self._device.name, + manufacturer="Micronova", + model=self._device.name_product, + ) + + @property + def native_value(self): + """Return the state of the sensor.""" + return getattr(self._device, self.entity_description.key) + + @property + def native_min_value(self): + return getattr(self._device, f"min_{self.entity_description.key}") + + @property + def native_max_value(self): + return getattr(self._device, f"max_{self.entity_description.key}") + + async def async_set_native_value(self, value): + try: + await self.hass.async_add_executor_job( + setattr, + self._device, + self.entity_description.key, + value, + ) + await self.coordinator.async_request_refresh() + except (ValueError, AguaIOTError) as err: + _LOGGER.error("Failed to set value, error: %s", err) diff --git a/custom_components/aguaiot/switch.py b/custom_components/aguaiot/switch.py new file mode 100644 index 0000000..20ab364 --- /dev/null +++ b/custom_components/aguaiot/switch.py @@ -0,0 +1,84 @@ +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) +from homeassistant.components.switch import SwitchEntity +from homeassistant.helpers.entity import DeviceInfo +from .const import SWITCHES, DOMAIN +from py_agua_iot import Error as AguaIOTError + + +async def async_setup_entry(hass, entry, async_add_entities): + coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ + "coordinator" + ] + agua = hass.data[DOMAIN][entry.entry_id]["agua"] + + switches = [] + for device in agua.devices: + for switch in SWITCHES: + if getattr(device, switch.key, None) is not None: + switches.append(AguaIOTHeatingSwitch(coordinator, device, switch)) + + async_add_entities(switches, True) + + +class AguaIOTHeatingSwitch(CoordinatorEntity, SwitchEntity): + def __init__(self, coordinator, device, description): + """Initialize the thermostat.""" + CoordinatorEntity.__init__(self, coordinator) + self._device = device + self.entity_description = description + + @property + def unique_id(self): + """Return a unique ID.""" + return f"{self._device.id_device}_{self.entity_description.key}" + + @property + def name(self): + """Return the name of the device, if any.""" + return f"{self._device.name} {self.entity_description.name}" + + @property + def device_info(self): + """Return the device info.""" + return DeviceInfo( + identifiers={(DOMAIN, self._device.id_device)}, + name=self._device.name, + manufacturer="Micronova", + model=self._device.name_product, + ) + + @property + def is_on(self): + """Return the state of the sensor.""" + return bool(getattr(self._device, self.entity_description.key)) + + async def async_turn_off(self): + """Turn device off.""" + try: + await self.hass.async_add_executor_job( + setattr(self._device, self.entity_description.key, False) + ) + await self.coordinator.async_request_refresh() + except AguaIOTError as err: + _LOGGER.error( + "Failed to turn off '%s', error: %s", + f"{self._device.name} {self.entity_description.name}", + err, + ) + + async def async_turn_on(self): + """Turn device on.""" + try: + await self.hass.async_add_executor_job( + setattr(self._device, self.entity_description.key, True) + ) + await self.coordinator.async_request_refresh() + except AguaIOTError as err: + _LOGGER.error( + "Failed to turn on '%s', error: %s", + f"{self._device.name} {self.entity_description.name}", + err, + )