diff --git a/custom_components/wundasmart/services.yaml b/custom_components/wundasmart/services.yaml index ad4d02c..31f0d35 100644 --- a/custom_components/wundasmart/services.yaml +++ b/custom_components/wundasmart/services.yaml @@ -13,3 +13,22 @@ hw_boost: advanced: false example: '00:30:00' default: '00:30:00' + selector: + time: +hw_off: + name: Turn off hot water + description: Turns the water heater off for a specific amount of time. + target: + entity: + integration: wundasmart + domain: water_heater + fields: + duration: + name: Duration + description: Time to turn the water heater off for. + required: true + advanced: false + example: '00:30:00' + default: '00:30:00' + selector: + time: diff --git a/custom_components/wundasmart/strings.json b/custom_components/wundasmart/strings.json index 481f596..bea83f4 100644 --- a/custom_components/wundasmart/strings.json +++ b/custom_components/wundasmart/strings.json @@ -31,6 +31,32 @@ } } } + }, + "water_heater": { + "wundasmart": { + "state": { + "auto": "Auto", + "auto_on": "On (Auto)", + "auto_off": "Off (Auto)", + "boost_on": "On", + "off": "Off" + }, + "state_attributes": { + "operation_mode": { + "state": { + "auto": "Auto", + "boost_30": "Boost (30 mins)", + "boost_60": "Boost (1 hour)", + "boost_90": "Boost (1.5 hours)", + "boost_120": "Boost (2 hours)", + "off_30": "Off (30 mins)", + "off_60": "Off (1 hour)", + "off_90": "Off (1.5 hours)", + "off_120": "Off (2 hours)" + } + } + } + } } } } diff --git a/custom_components/wundasmart/water_heater.py b/custom_components/wundasmart/water_heater.py index f732831..43fdda4 100644 --- a/custom_components/wundasmart/water_heater.py +++ b/custom_components/wundasmart/water_heater.py @@ -33,18 +33,49 @@ SUPPORTED_FEATURES = WaterHeaterEntityFeature.ON_OFF | WaterHeaterEntityFeature.OPERATION_MODE -STATE_AUTO_ON = "On (Auto)" -STATE_AUTO_OFF = "Off (Auto)" -STATE_BOOST_ON = "On (Boost)" -STATE_BOOST_OFF = "Off (Manual)" -STATE_AUTO = "Auto" - -HW_BOOST_TIME = 60 * 30 # boost for 30 minutes -HW_OFF_TIME = 60 * 60 # switch off for 1 hour - -OPERATION_SET_AUTO = "Auto" -OPERATION_BOOST_ON = "Boost (30 mins)" -OPERATION_BOOST_OFF = "Off (1 hour)" +STATE_AUTO_ON = "auto_on" +STATE_AUTO_OFF = "auto_off" +STATE_BOOST_ON = "boost_on" +STATE_OFF = "boost_off" +STATE_AUTO = "auto" + +OPERATION_SET_AUTO = "auto" +OPERATION_BOOST_30 = "boost_30" +OPERATION_BOOST_60 = "boost_60" +OPERATION_BOOST_90 = "boost_90" +OPERATION_BOOST_120 = "boost_120" +OPERATION_OFF_30 = "off_30" +OPERATION_OFF_60 = "off_60" +OPERATION_OFF_90 = "off_90" +OPERATION_OFF_120 = "off_90" + +HW_BOOST_OPERATIONS = { + OPERATION_BOOST_30, + OPERATION_BOOST_60, + OPERATION_BOOST_90, + OPERATION_BOOST_120 +} + +HW_OFF_OPERATIONS = { + OPERATION_OFF_30, + OPERATION_OFF_60, + OPERATION_OFF_90, + OPERATION_OFF_120 +} + +HW_BOOST_TIME = { + OPERATION_BOOST_30: 30 * 60, + OPERATION_BOOST_60: 60 * 60, + OPERATION_BOOST_90: 90 * 60, + OPERATION_BOOST_120: 120 * 60 +} + +HW_OFF_TIME = { + OPERATION_OFF_30: 30 * 60, + OPERATION_OFF_60: 60 * 60, + OPERATION_OFF_90: 90 * 60, + OPERATION_OFF_120: 120 * 60 +} async def async_setup_entry( @@ -80,15 +111,19 @@ async def async_setup_entry( "async_set_boost", ) + platform.async_register_entity_service( + "hw_off", + { + vol.Required("duration"): cv.positive_time_period + }, + "async_set_off", + ) + class Device(CoordinatorEntity[WundasmartDataUpdateCoordinator], WaterHeaterEntity): """Representation of an Wundasmart water heater.""" - _attr_operation_list = [ - OPERATION_SET_AUTO, - OPERATION_BOOST_ON, - OPERATION_BOOST_OFF - ] + _attr_operation_list = list({ OPERATION_SET_AUTO } | HW_BOOST_OPERATIONS | HW_OFF_OPERATIONS) _attr_supported_features = WaterHeaterEntityFeature.OPERATION_MODE _attr_temperature_unit = TEMP_CELSIUS _attr_translation_key = DOMAIN @@ -141,7 +176,7 @@ def __update_state(self): if hw_mode_state: self._attr_current_operation = STATE_BOOST_ON if hw_boost_state else STATE_AUTO_ON else: - self._attr_current_operation = STATE_BOOST_OFF if hw_boost_state else STATE_AUTO_OFF + self._attr_current_operation = STATE_OFF if hw_boost_state else STATE_AUTO_OFF @callback def _handle_coordinator_update(self) -> None: @@ -155,33 +190,45 @@ async def async_added_to_hass(self) -> None: self._handle_coordinator_update() async def async_set_operation_mode(self, operation_mode: str) -> None: - if operation_mode == OPERATION_BOOST_OFF: - await send_command(self._session, self._wunda_ip, self._wunda_user, self._wunda_pass, params={ - "cmd": 3, - "hw_off_time": HW_OFF_TIME - }) - elif operation_mode == OPERATION_BOOST_ON: - await send_command(self._session, self._wunda_ip, self._wunda_user, self._wunda_pass, params={ - "cmd": 3, - "hw_boost_time": HW_BOOST_TIME - }) - elif operation_mode == OPERATION_SET_AUTO: + if operation_mode: + if operation_mode in HW_OFF_OPERATIONS: + await send_command(self._session, self._wunda_ip, self._wunda_user, self._wunda_pass, params={ + "cmd": 3, + "hw_off_time": HW_OFF_TIME[operation_mode] + }) + elif operation_mode in HW_BOOST_OPERATIONS: + await send_command(self._session, self._wunda_ip, self._wunda_user, self._wunda_pass, params={ + "cmd": 3, + "hw_boost_time": HW_BOOST_TIME[operation_mode] + }) + elif operation_mode == OPERATION_SET_AUTO: + await send_command(self._session, self._wunda_ip, self._wunda_user, self._wunda_pass, params={ + "cmd": 3, + "hw_boost_time": 0 + }) + else: + raise NotImplementedError(f"Unsupported operation mode {operation_mode}") + + # Fetch the updated state + await self.coordinator.async_request_refresh() + + async def async_set_boost(self, duration: timedelta): + seconds = int((duration.days * 24 * 3600) + math.ceil(duration.seconds)) + if seconds > 0: await send_command(self._session, self._wunda_ip, self._wunda_user, self._wunda_pass, params={ "cmd": 3, - "hw_boost_time": 0 + "hw_boost_time": seconds }) - else: - raise NotImplementedError(f"Unsupported operation mode {operation_mode}") # Fetch the updated state await self.coordinator.async_request_refresh() - async def async_set_boost(self, duration: timedelta): + async def async_set_off(self, duration: timedelta): seconds = int((duration.days * 24 * 3600) + math.ceil(duration.seconds)) if seconds > 0: await send_command(self._session, self._wunda_ip, self._wunda_user, self._wunda_pass, params={ "cmd": 3, - "hw_boost_time": seconds + "hw_off_time": seconds }) # Fetch the updated state