diff --git a/custom_components/smartthinq_sensors/switch.py b/custom_components/smartthinq_sensors/switch.py index 21f4d1e0..98d8e3a3 100644 --- a/custom_components/smartthinq_sensors/switch.py +++ b/custom_components/smartthinq_sensors/switch.py @@ -91,6 +91,14 @@ class ThinQSwitchEntityDescription(SwitchEntityDescription): ), ) AC_SWITCH: tuple[ThinQSwitchEntityDescription, ...] = ( + ThinQSwitchEntityDescription( + key=AirConditionerFeatures.POWER, + name="Power", + icon="mdi:power", + turn_off_fn=lambda x: x.device.power(False), + turn_on_fn=lambda x: x.device.power(True), + value_fn=lambda x: x.is_power_on, + ), ThinQSwitchEntityDescription( key=AirConditionerFeatures.MODE_AIRCLEAN, name="Ionizer", @@ -98,6 +106,13 @@ class ThinQSwitchEntityDescription(SwitchEntityDescription): turn_off_fn=lambda x: x.device.set_mode_airclean(False), turn_on_fn=lambda x: x.device.set_mode_airclean(True), ), + ThinQSwitchEntityDescription( + key=AirConditionerFeatures.UVNANO, + name="UVnano", + icon="mdi:flash", + turn_off_fn=lambda x: x.device.set_uv_nano(False), + turn_on_fn=lambda x: x.device.set_uv_nano(True), + ), ThinQSwitchEntityDescription( key=AirConditionerFeatures.MODE_JET, name="Jet mode", @@ -161,6 +176,11 @@ def _switch_exist( if feature in lge_device.available_features: return True + # Special case for UVnano - check if device supports it directly + if feature == AirConditionerFeatures.UVNANO: + if hasattr(lge_device.device, 'is_uv_nano_supported'): + return lge_device.device.is_uv_nano_supported + return False diff --git a/custom_components/smartthinq_sensors/wideq/const.py b/custom_components/smartthinq_sensors/wideq/const.py index 5ff64fd9..bd0217d3 100644 --- a/custom_components/smartthinq_sensors/wideq/const.py +++ b/custom_components/smartthinq_sensors/wideq/const.py @@ -44,8 +44,10 @@ class AirConditionerFeatures(StrEnum): PM1 = "pm1" PM10 = "pm10" PM25 = "pm25" + POWER = "power" RESERVATION_SLEEP_TIME = "reservation_sleep_time" ROOM_TEMP = "room_temperature" + UVNANO = "uvnano" WATER_IN_TEMP = "water_in_temperature" WATER_OUT_TEMP = "water_out_temperature" diff --git a/custom_components/smartthinq_sensors/wideq/devices/ac.py b/custom_components/smartthinq_sensors/wideq/devices/ac.py index 8f3ad828..a4ef7fd3 100644 --- a/custom_components/smartthinq_sensors/wideq/devices/ac.py +++ b/custom_components/smartthinq_sensors/wideq/devices/ac.py @@ -35,6 +35,13 @@ SUPPORT_JET_COOL = [SUPPORT_RAC_SUBMODE, "@AC_MAIN_WIND_MODE_COOL_JET_W"] SUPPORT_JET_HEAT = [SUPPORT_RAC_SUBMODE, "@AC_MAIN_WIND_MODE_HEAT_JET_W"] SUPPORT_AIRCLEAN = [SUPPORT_RAC_MODE, "@AIRCLEAN"] +# Try different possible support keys for UVnano +# SUPPORT_UVNANO = [SUPPORT_RAC_MODE, "@UVNANO"] +# SUPPORT_UVNANO = [SUPPORT_RAC_MODE, "@UV_NANO"] +# SUPPORT_UVNANO = [SUPPORT_RAC_SUBMODE, "@UV_NANO"] +# Alternative based on miscFuncState location: +SUPPORT_UVNANO = ["SupportMiscFuncState", "support.miscFuncState"] +# SUPPORT_UVNANO = ["SupportUVnano", "support.uvnano"] SUPPORT_HOT_WATER = [SUPPORT_PAC_MODE, ["@HOTWATER", "@HOTWATER_ONLY"]] SUPPORT_LIGHT_SWITCH = [SUPPORT_LIGHT, "@RAC_88_DISPLAY_CONTROL"] SUPPORT_LIGHT_INV_SWITCH = [SUPPORT_LIGHT, "@BRIGHTNESS_CONTROL"] @@ -73,6 +80,7 @@ STATE_HUMIDITY = ["SensorHumidity", "airState.humidity.current"] STATE_MODE_AIRCLEAN = ["AirClean", "airState.wMode.airClean"] STATE_MODE_JET = ["Jet", "airState.wMode.jet"] +STATE_UV_NANO = ["UVNano", "airState.miscFuncState.Uvnano"] STATE_LIGHTING_DISPLAY = ["DisplayControl", "airState.lightingState.displayControl"] STATE_AIRSENSORMON = ["SensorMon", "airState.quality.sensorMon"] STATE_PM1 = ["SensorPM1", "airState.quality.PM1"] @@ -104,6 +112,7 @@ CMD_STATE_DUCT_ZONES = [CTRL_MISC, "Set", [DUCT_ZONE_V1, "airState.ductZone.control"]] CMD_STATE_MODE_AIRCLEAN = [CTRL_BASIC, "Set", STATE_MODE_AIRCLEAN] CMD_STATE_MODE_JET = [CTRL_BASIC, "Set", STATE_MODE_JET] +CMD_STATE_UV_NANO = [CTRL_BASIC, "Set", STATE_UV_NANO] CMD_STATE_LIGHTING_DISPLAY = [CTRL_BASIC, "Set", STATE_LIGHTING_DISPLAY] CMD_RESERVATION_SLEEP_TIME = [CTRL_BASIC, "Set", STATE_RESERVATION_SLEEP_TIME] @@ -152,6 +161,9 @@ MODE_AIRCLEAN_OFF = "@AC_MAIN_AIRCLEAN_OFF_W" MODE_AIRCLEAN_ON = "@AC_MAIN_AIRCLEAN_ON_W" +UV_NANO_OFF = "@OFF" +UV_NANO_ON = "@ON" + AWHP_MODE_AIR = "@AIR" AWHP_MODE_WATER = "@WATER" @@ -589,6 +601,23 @@ def is_mode_airclean_supported(self): """Return if AirClean mode is supported.""" return self._is_mode_supported(SUPPORT_AIRCLEAN) + @cached_property + def is_uv_nano_supported(self): + """Return if UVnano mode is supported.""" + # First try the traditional support check + supported = self._is_mode_supported(SUPPORT_UVNANO) + _LOGGER.debug("UVnano support check via model: %s (support key: %s)", supported, SUPPORT_UVNANO) + + # If traditional check fails, check if the data is present in the device status + if not supported and self._status: + key = self._get_state_key(STATE_UV_NANO) + data_present = key in self._status._data if hasattr(self._status, '_data') else False + _LOGGER.debug("UVnano support check via data presence: %s (key: %s)", data_present, key) + supported = data_present + + _LOGGER.debug("UVnano final support decision: %s", supported) + return supported + @cached_property def supported_ligth_modes(self): """Return light switch modes supported.""" @@ -750,6 +779,23 @@ async def set_mode_airclean(self, status: bool): mode = self.model_info.enum_value(keys[2], mode_key) await self.set(keys[0], keys[1], key=keys[2], value=mode) + async def set_uv_nano(self, status: bool): + """Set the UVnano mode on or off.""" + if not self.is_uv_nano_supported: + raise ValueError("UVnano mode not supported") + + keys = self._get_cmd_keys(CMD_STATE_UV_NANO) + mode_key = UV_NANO_ON if status else UV_NANO_OFF + mode = self.model_info.enum_value(keys[2], mode_key) + + _LOGGER.debug("UVnano set command - keys: %s, mode_key: %s, final_mode: %s", keys, mode_key, mode) + + if mode is None: + _LOGGER.error("UVnano mode value not found in model info for key: %s", mode_key) + raise ValueError(f"UVnano mode value not found for key: {mode_key}") + + await self.set(keys[0], keys[1], key=keys[2], value=mode) + async def set_mode_jet(self, status: bool): """Set the Jet mode on or off.""" if self.supported_mode_jet == JetModeSupport.NONE: @@ -1188,6 +1234,25 @@ def mode_airclean(self): status = value == MODE_AIRCLEAN_ON return self._update_feature(AirConditionerFeatures.MODE_AIRCLEAN, status, False) + @property + def uv_nano(self): + """Return UVnano Mode status.""" + if not self._device.is_uv_nano_supported: + return None + key = self._get_state_key(STATE_UV_NANO) + value = self.lookup_enum(key, True) + + _LOGGER.debug("UVnano status - key: %s, raw_value: %s", key, value) + + if value is None: + return None + + status = value == UV_NANO_ON + + _LOGGER.debug("UVnano status - processed: %s (raw: %s, expected_on: %s)", status, value, UV_NANO_ON) + + return self._update_feature(AirConditionerFeatures.UVNANO, status, False) + @property def mode_jet(self): """Return Jet Mode status.""" @@ -1387,6 +1452,12 @@ def reservation_sleep_time(self): AirConditionerFeatures.RESERVATION_SLEEP_TIME, value, False ) + @property + def power(self): + """Return power status.""" + status = self.is_on + return self._update_feature(AirConditionerFeatures.POWER, status, False) + def _update_features(self): _ = [ self.room_temp, @@ -1397,6 +1468,7 @@ def _update_features(self): self.pm25, self.pm1, self.mode_airclean, + self.uv_nano, self.mode_jet, self.lighting_display, self.water_in_current_temp, @@ -1404,4 +1476,5 @@ def _update_features(self): self.mode_awhp_silent, self.hot_water_current_temp, self.reservation_sleep_time, + self.power, ]