diff --git a/custom_components/smartthinq_sensors/sensor.py b/custom_components/smartthinq_sensors/sensor.py index c51b7d5e..c1d22dc7 100644 --- a/custom_components/smartthinq_sensors/sensor.py +++ b/custom_components/smartthinq_sensors/sensor.py @@ -392,6 +392,50 @@ class ThinQSensorEntityDescription(SensorEntityDescription): device_class=SensorDeviceClass.TEMPERATURE, unit_fn=lambda x: x.oven_temp_unit, ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_UPPER_TIMER_TIME, + name="Oven upper timer", + icon="mdi:timer-outline", + device_class=SensorDeviceClass.TIMESTAMP, + ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_UPPER_TIMER_STATE, + name="Oven upper timer state", + icon="mdi:timer-cog-outline", + ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_UPPER_COOK_TIMER_TIME, + name="Oven upper cook time", + icon="mdi:timer-check-outline", + device_class=SensorDeviceClass.TIMESTAMP, + ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_UPPER_COOK_TIMER_STATE, + name="Oven upper cook time state", + icon="mdi:timer-cog-outline", + ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_LOWER_TIMER_TIME, + name="Oven lower timer", + icon="mdi:timer-outline", + device_class=SensorDeviceClass.TIMESTAMP, + ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_LOWER_TIMER_STATE, + name="Oven lower timer state", + icon="mdi:timer-cog-outline", + ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_LOWER_COOK_TIMER_TIME, + name="Oven lower cook time", + icon="mdi:timer-check-outline", + device_class=SensorDeviceClass.TIMESTAMP, + ), + ThinQSensorEntityDescription( + key=RangeFeatures.OVEN_LOWER_COOK_TIMER_STATE, + name="Oven lower cook time state", + icon="mdi:timer-cog-outline", + ), ) AIR_PURIFIER_SENSORS: tuple[ThinQSensorEntityDescription, ...] = ( ThinQSensorEntityDescription( diff --git a/custom_components/smartthinq_sensors/wideq/const.py b/custom_components/smartthinq_sensors/wideq/const.py index 5ff64fd9..2ea20338 100644 --- a/custom_components/smartthinq_sensors/wideq/const.py +++ b/custom_components/smartthinq_sensors/wideq/const.py @@ -96,6 +96,14 @@ class RangeFeatures(StrEnum): OVEN_UPPER_CURRENT_TEMP = "oven_upper_current_temp" OVEN_UPPER_MODE = "oven_upper_mode" OVEN_UPPER_STATE = "oven_upper_state" + OVEN_UPPER_TIMER_TIME = "oven_upper_timer" + OVEN_UPPER_COOK_TIMER_TIME = "oven_upper_cook_time" + OVEN_LOWER_TIMER_TIME = "oven_lower_timer" + OVEN_LOWER_COOK_TIMER_TIME = "oven_lower_cook_time" + OVEN_UPPER_TIMER_STATE = "oven_upper_timer_state" + OVEN_UPPER_COOK_TIMER_STATE = "oven_upper_cook_timer_state" + OVEN_LOWER_TIMER_STATE = "oven_lower_timer_state" + OVEN_LOWER_COOK_TIMER_STATE = "oven_lower_cook_timer_state" class RefrigeratorFeatures(StrEnum): diff --git a/custom_components/smartthinq_sensors/wideq/devices/range.py b/custom_components/smartthinq_sensors/wideq/devices/range.py index 719e09c7..99a5547d 100644 --- a/custom_components/smartthinq_sensors/wideq/devices/range.py +++ b/custom_components/smartthinq_sensors/wideq/devices/range.py @@ -2,6 +2,8 @@ from __future__ import annotations +from datetime import datetime, timedelta + from ..const import BIT_OFF, RangeFeatures, StateOptions, TemperatureUnit from ..core_async import ClientAsync from ..device import Device, DeviceStatus @@ -127,6 +129,111 @@ def _get_bit_target_temp(self, key: str): return target_temp + def _get_time_delta( + self, hour_key: str, min_key: str, sec_key: str + ) -> timedelta | None: + """Get the time remaining as a timedelta.""" + try: + hours = self.to_int_or_none(self._data.get(hour_key)) + mins = self.to_int_or_none(self._data.get(min_key)) + secs = self.to_int_or_none(self._data.get(sec_key)) + + if any(x is not None for x in (hours, mins, secs)): + return timedelta(hours=hours or 0, minutes=mins or 0, seconds=secs or 0) + except (ValueError, TypeError): + pass + return None + + def _get_finish_time(self, delta: timedelta | None) -> datetime | None: + """Get the finish time based on the time delta.""" + if delta is not None: + now = datetime.now().astimezone() + if delta.total_seconds() > 0: + return now + delta + # Return start of current day when timer is 0/off + return now.replace(hour=0, minute=0, second=0, microsecond=0).astimezone() + return None + + @property + def oven_upper_timer_time(self) -> datetime | None: + """Get the upper timer finish time.""" + delta = self._get_time_delta("UpperTimerHour", "UpperTimerMin", "UpperTimerSec") + return self._update_feature( + RangeFeatures.OVEN_UPPER_TIMER_TIME, self._get_finish_time(delta), False + ) + + @property + def oven_upper_cook_timer_time(self) -> datetime | None: + """Get the upper cook time finish time.""" + delta = self._get_time_delta( + "UpperCookTimeHour", "UpperCookTimeMin", "UpperCookTimeSec" + ) + return self._update_feature( + RangeFeatures.OVEN_UPPER_COOK_TIMER_TIME, + self._get_finish_time(delta), + False, + ) + + @property + def oven_lower_timer_time(self) -> datetime | None: + """Get the lower timer finish time.""" + delta = self._get_time_delta("LowerTimerHour", "LowerTimerMin", "LowerTimerSec") + return self._update_feature( + RangeFeatures.OVEN_LOWER_TIMER_TIME, self._get_finish_time(delta), False + ) + + @property + def oven_lower_cook_timer_time(self) -> datetime | None: + """Get the lower cook time finish time.""" + delta = self._get_time_delta( + "LowerCookTimeHour", "LowerCookTimeMin", "LowerCookTimeSec" + ) + return self._update_feature( + RangeFeatures.OVEN_LOWER_COOK_TIMER_TIME, + self._get_finish_time(delta), + False, + ) + + @property + def oven_upper_timer_state(self) -> str | None: + """Get the upper timer state.""" + delta = self._get_time_delta("UpperTimerHour", "UpperTimerMin", "UpperTimerSec") + state = ( + StateOptions.ON if delta and delta.total_seconds() > 0 else StateOptions.OFF + ) + return self._update_feature(RangeFeatures.OVEN_UPPER_TIMER_STATE, state) + + @property + def oven_upper_cook_timer_state(self) -> str | None: + """Get the upper cook time state.""" + delta = self._get_time_delta( + "UpperCookTimeHour", "UpperCookTimeMin", "UpperCookTimeSec" + ) + state = ( + StateOptions.ON if delta and delta.total_seconds() > 0 else StateOptions.OFF + ) + return self._update_feature(RangeFeatures.OVEN_UPPER_COOK_TIMER_STATE, state) + + @property + def oven_lower_timer_state(self) -> str | None: + """Get the lower timer state.""" + delta = self._get_time_delta("LowerTimerHour", "LowerTimerMin", "LowerTimerSec") + state = ( + StateOptions.ON if delta and delta.total_seconds() > 0 else StateOptions.OFF + ) + return self._update_feature(RangeFeatures.OVEN_LOWER_TIMER_STATE, state) + + @property + def oven_lower_cook_timer_state(self) -> str | None: + """Get the lower cook time state.""" + delta = self._get_time_delta( + "LowerCookTimeHour", "LowerCookTimeMin", "LowerCookTimeSec" + ) + state = ( + StateOptions.ON if delta and delta.total_seconds() > 0 else StateOptions.OFF + ) + return self._update_feature(RangeFeatures.OVEN_LOWER_COOK_TIMER_STATE, state) + @property def is_on(self): """Return if device is on.""" @@ -313,4 +420,12 @@ def _update_features(self): self.oven_upper_state, self.oven_upper_mode, self.oven_upper_current_temp, + self.oven_upper_timer_time, + self.oven_upper_cook_timer_time, + self.oven_lower_timer_time, + self.oven_lower_cook_timer_time, + self.oven_upper_timer_state, + self.oven_upper_cook_timer_state, + self.oven_lower_timer_state, + self.oven_lower_cook_timer_state, ]