From 7ec66c2581605e7fe7f6011a46933156a9ad055b Mon Sep 17 00:00:00 2001 From: cdnninja Date: Thu, 16 Jan 2025 01:31:07 +0000 Subject: [PATCH 1/9] Refactor Fan --- homeassistant/components/vesync/common.py | 8 +- homeassistant/components/vesync/const.py | 28 ++++- homeassistant/components/vesync/fan.py | 118 ++++++++++------------ 3 files changed, 87 insertions(+), 67 deletions(-) diff --git a/homeassistant/components/vesync/common.py b/homeassistant/components/vesync/common.py index c51b6a913d3c1..cf99c36a9d641 100644 --- a/homeassistant/components/vesync/common.py +++ b/homeassistant/components/vesync/common.py @@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant -from .const import VeSyncHumidifierDevice +from .const import VeSyncFanDevice, VeSyncHumidifierDevice _LOGGER = logging.getLogger(__name__) @@ -32,3 +32,9 @@ def is_humidifier(device: VeSyncBaseDevice) -> bool: """Check if the device represents a humidifier.""" return isinstance(device, VeSyncHumidifierDevice) + + +def is_fan(device: VeSyncBaseDevice) -> bool: + """Check if the device represents a fan.""" + + return isinstance(device, VeSyncFanDevice) diff --git a/homeassistant/components/vesync/const.py b/homeassistant/components/vesync/const.py index 841185e4308f9..414ed63e0fea5 100644 --- a/homeassistant/components/vesync/const.py +++ b/homeassistant/components/vesync/const.py @@ -1,6 +1,12 @@ """Constants for VeSync Component.""" -from pyvesync.vesyncfan import VeSyncHumid200300S, VeSyncSuperior6000S +from pyvesync.vesyncfan import ( + VeSyncAir131, + VeSyncAirBaseV2, + VeSyncAirBypass, + VeSyncHumid200300S, + VeSyncSuperior6000S, +) DOMAIN = "vesync" VS_DISCOVERY = "vesync_discovery_{}" @@ -29,9 +35,29 @@ VS_HUMIDIFIER_MODE_MANUAL = "manual" VS_HUMIDIFIER_MODE_SLEEP = "sleep" +VS_FAN_MODE_AUTO = "auto" +VS_FAN_MODE_SLEEP = "sleep" +VS_FAN_MODE_ADVANCED_SLEEP = "advancedSleep" +VS_FAN_MODE_TURBO = "turbo" +VS_FAN_MODE_PET = "pet" +VS_FAN_MODE_MANUAL = "manual" + +# not a full list as manual is used as speed not present +VS_FAN_MODE_PRESET_LIST_HA = [ + VS_FAN_MODE_AUTO, + VS_FAN_MODE_SLEEP, + VS_FAN_MODE_ADVANCED_SLEEP, + VS_FAN_MODE_TURBO, + VS_FAN_MODE_PET, +] + VeSyncHumidifierDevice = VeSyncHumid200300S | VeSyncSuperior6000S """Humidifier device types""" +VeSyncFanDevice = VeSyncAirBypass | VeSyncAirBypass | VeSyncAirBaseV2 | VeSyncAir131 +"""Fan device types""" + + DEV_TYPE_TO_HA = { "wifi-switch-1.3": "outlet", "ESW03-USA": "outlet", diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index ba1880f249295..12f60b3c804fc 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -19,34 +19,26 @@ ) from homeassistant.util.scaling import int_states_in_range +from .common import is_fan from .const import ( - DEV_TYPE_TO_HA, DOMAIN, SKU_TO_BASE_DEVICE, VS_COORDINATOR, VS_DEVICES, VS_DISCOVERY, + VS_FAN_MODE_ADVANCED_SLEEP, + VS_FAN_MODE_AUTO, + VS_FAN_MODE_MANUAL, + VS_FAN_MODE_PET, + VS_FAN_MODE_PRESET_LIST_HA, + VS_FAN_MODE_SLEEP, + VS_FAN_MODE_TURBO, ) from .coordinator import VeSyncDataCoordinator from .entity import VeSyncBaseEntity _LOGGER = logging.getLogger(__name__) -FAN_MODE_AUTO = "auto" -FAN_MODE_SLEEP = "sleep" -FAN_MODE_PET = "pet" -FAN_MODE_TURBO = "turbo" - -PRESET_MODES = { - "LV-PUR131S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], - "Core200S": [FAN_MODE_SLEEP], - "Core300S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], - "Core400S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], - "Core600S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], - "EverestAir": [FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_TURBO], - "Vital200S": [FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_PET], - "Vital100S": [FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_PET], -} SPEED_RANGE = { # off is not included "LV-PUR131S": (1, 3), "Core200S": (1, 3), @@ -87,13 +79,8 @@ def _setup_entities( coordinator: VeSyncDataCoordinator, ): """Check if device is fan and add entity.""" - entities = [ - VeSyncFanHA(dev, coordinator) - for dev in devices - if DEV_TYPE_TO_HA.get(SKU_TO_BASE_DEVICE.get(dev.device_type, "")) == "fan" - ] - async_add_entities(entities, update_before_add=True) + async_add_entities(VeSyncFanHA(dev, coordinator) for dev in devices if is_fan(dev)) class VeSyncFanHA(VeSyncBaseEntity, FanEntity): @@ -108,13 +95,6 @@ class VeSyncFanHA(VeSyncBaseEntity, FanEntity): _attr_name = None _attr_translation_key = "vesync" - def __init__( - self, fan: VeSyncBaseDevice, coordinator: VeSyncDataCoordinator - ) -> None: - """Initialize the VeSync fan device.""" - super().__init__(fan, coordinator) - self.smartfan = fan - @property def is_on(self) -> bool: """Return True if device is on.""" @@ -124,8 +104,8 @@ def is_on(self) -> bool: def percentage(self) -> int | None: """Return the current speed.""" if ( - self.smartfan.mode == "manual" - and (current_level := self.smartfan.fan_level) is not None + self.device.mode == VS_FAN_MODE_MANUAL + and (current_level := self.device.fan_level) is not None ): return ranged_value_to_percentage( SPEED_RANGE[SKU_TO_BASE_DEVICE[self.device.device_type]], current_level @@ -142,53 +122,59 @@ def speed_count(self) -> int: @property def preset_modes(self) -> list[str]: """Get the list of available preset modes.""" - return PRESET_MODES[SKU_TO_BASE_DEVICE[self.device.device_type]] + if hasattr(self.device, "modes"): + return sorted( + [ + mode + for mode in self.device.modes + if mode in VS_FAN_MODE_PRESET_LIST_HA + ] + ) + return [] @property def preset_mode(self) -> str | None: """Get the current preset mode.""" - if self.smartfan.mode in (FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_TURBO): - return self.smartfan.mode + if self.device.mode in VS_FAN_MODE_PRESET_LIST_HA: + return self.device.mode return None @property def unique_info(self): """Return the ID of this fan.""" - return self.smartfan.uuid + return self.device.uuid @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the fan.""" attr = {} - if hasattr(self.smartfan, "active_time"): - attr["active_time"] = self.smartfan.active_time + if hasattr(self.device, "active_time"): + attr["active_time"] = self.device.active_time - if hasattr(self.smartfan, "screen_status"): - attr["screen_status"] = self.smartfan.screen_status + if hasattr(self.device, "screen_status"): + attr["screen_status"] = self.device.screen_status - if hasattr(self.smartfan, "child_lock"): - attr["child_lock"] = self.smartfan.child_lock + if hasattr(self.device, "child_lock"): + attr["child_lock"] = self.device.child_lock - if hasattr(self.smartfan, "night_light"): - attr["night_light"] = self.smartfan.night_light + if hasattr(self.device, "night_light"): + attr["night_light"] = self.device.night_light - if hasattr(self.smartfan, "mode"): - attr["mode"] = self.smartfan.mode + if hasattr(self.device, "mode"): + attr["mode"] = self.device.mode return attr def set_percentage(self, percentage: int) -> None: """Set the speed of the device.""" if percentage == 0: - self.smartfan.turn_off() - return - - if not self.smartfan.is_on: - self.smartfan.turn_on() + self.device.turn_off() + elif not self.device.is_on: + self.device.turn_on() - self.smartfan.manual_mode() - self.smartfan.change_fan_speed( + self.device.manual_mode() + self.device.change_fan_speed( math.ceil( percentage_to_ranged_value( SPEED_RANGE[SKU_TO_BASE_DEVICE[self.device.device_type]], percentage @@ -199,23 +185,25 @@ def set_percentage(self, percentage: int) -> None: def set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode of device.""" - if preset_mode not in self.preset_modes: + if preset_mode not in VS_FAN_MODE_PRESET_LIST_HA: raise ValueError( f"{preset_mode} is not one of the valid preset modes: " - f"{self.preset_modes}" + f"{VS_FAN_MODE_PRESET_LIST_HA}" ) - if not self.smartfan.is_on: - self.smartfan.turn_on() - - if preset_mode == FAN_MODE_AUTO: - self.smartfan.auto_mode() - elif preset_mode == FAN_MODE_SLEEP: - self.smartfan.sleep_mode() - elif preset_mode == FAN_MODE_PET: - self.smartfan.pet_mode() - elif preset_mode == FAN_MODE_TURBO: - self.smartfan.turbo_mode() + if not self.device.is_on: + self.device.turn_on() + + if preset_mode == VS_FAN_MODE_AUTO: + self.device.auto_mode() + elif preset_mode == VS_FAN_MODE_SLEEP: + self.device.sleep_mode() + elif preset_mode == VS_FAN_MODE_PET: + self.device.pet_mode() + elif preset_mode == VS_FAN_MODE_TURBO: + self.device.turbo_mode() + elif preset_mode == VS_FAN_MODE_ADVANCED_SLEEP: + self.device.advanced_sleep_mode() self.schedule_update_ha_state() From 1aa2e4a0e2d29d9e613669629c1a36bdeb9fa43d Mon Sep 17 00:00:00 2001 From: cdnninja Date: Thu, 16 Jan 2025 02:42:10 +0000 Subject: [PATCH 2/9] Add tower fan tests and mode --- homeassistant/components/vesync/const.py | 6 ++ homeassistant/components/vesync/fan.py | 1 + homeassistant/components/vesync/icons.json | 1 + homeassistant/components/vesync/strings.json | 1 + tests/components/vesync/common.py | 3 + .../vesync/fixtures/SmartTowerFan-detail.json | 37 +++++++ .../vesync/fixtures/vesync-devices.json | 9 ++ .../components/vesync/snapshots/test_fan.ambr | 101 ++++++++++++++++++ .../vesync/snapshots/test_light.ambr | 38 +++++++ .../vesync/snapshots/test_sensor.ambr | 38 +++++++ .../vesync/snapshots/test_switch.ambr | 38 +++++++ 11 files changed, 273 insertions(+) create mode 100644 tests/components/vesync/fixtures/SmartTowerFan-detail.json diff --git a/homeassistant/components/vesync/const.py b/homeassistant/components/vesync/const.py index 414ed63e0fea5..f294c81ed2edb 100644 --- a/homeassistant/components/vesync/const.py +++ b/homeassistant/components/vesync/const.py @@ -74,6 +74,7 @@ "EverestAir": "fan", "Vital200S": "fan", "Vital100S": "fan", + "SmartTowerFan": "fan", "ESD16": "walldimmer", "ESWD16": "walldimmer", "ESL100": "bulb-dimmable", @@ -117,4 +118,9 @@ "LAP-EL551S-AEUR": "EverestAir", # Alt ID Model EverestAir "LAP-EL551S-WEU": "EverestAir", # Alt ID Model EverestAir "LAP-EL551S-WUS": "EverestAir", # Alt ID Model EverestAir + "SmartTowerFan": "SmartTowerFan", + "LTF-F422S-KEU": "SmartTowerFan", # Alt ID Model SmartTowerFan + "LTF-F422S-WUSR": "SmartTowerFan", # Alt ID Model SmartTowerFan + "LTF-F422_WJP": "SmartTowerFan", # Alt ID Model SmartTowerFan + "LTF-F422S-WUS": "SmartTowerFan", # Alt ID Model SmartTowerFan } diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index 12f60b3c804fc..9705002052430 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -48,6 +48,7 @@ "EverestAir": (1, 3), "Vital200S": (1, 4), "Vital100S": (1, 4), + "SmartTowerFan": (1, 13), } diff --git a/homeassistant/components/vesync/icons.json b/homeassistant/components/vesync/icons.json index e4769acc9a514..c11bd00204961 100644 --- a/homeassistant/components/vesync/icons.json +++ b/homeassistant/components/vesync/icons.json @@ -7,6 +7,7 @@ "state": { "auto": "mdi:fan-auto", "sleep": "mdi:sleep", + "advanced_sleep": "mdi:sleep", "pet": "mdi:paw", "turbo": "mdi:weather-tornado" } diff --git a/homeassistant/components/vesync/strings.json b/homeassistant/components/vesync/strings.json index a23fe7936e717..87a8ea8746e3f 100644 --- a/homeassistant/components/vesync/strings.json +++ b/homeassistant/components/vesync/strings.json @@ -55,6 +55,7 @@ "state": { "auto": "Auto", "sleep": "Sleep", + "advanced_sleep": "Advanced sleep", "pet": "Pet", "turbo": "Turbo" } diff --git a/tests/components/vesync/common.py b/tests/components/vesync/common.py index ead3ecdc17382..ee9f9b94052ab 100644 --- a/tests/components/vesync/common.py +++ b/tests/components/vesync/common.py @@ -51,6 +51,9 @@ ("post", "/inwallswitch/v1/device/devicedetail", "device-detail.json") ], "Dimmer Switch": [("post", "/dimmer/v1/device/devicedetail", "dimmer-detail.json")], + "SmartTowerFan": [ + ("post", "/cloud/v2/deviceManaged/bypassV2", "SmartTowerFan-detail.json") + ], } diff --git a/tests/components/vesync/fixtures/SmartTowerFan-detail.json b/tests/components/vesync/fixtures/SmartTowerFan-detail.json new file mode 100644 index 0000000000000..061dcb5b0d059 --- /dev/null +++ b/tests/components/vesync/fixtures/SmartTowerFan-detail.json @@ -0,0 +1,37 @@ +{ + "traceId": "0000000000", + "code": 0, + "msg": "request success", + "module": null, + "stacktrace": null, + "result": { + "traceId": "0000000000", + "code": 0, + "result": { + "powerSwitch": 0, + "workMode": "normal", + "manualSpeedLevel": 1, + "fanSpeedLevel": 0, + "screenState": 0, + "screenSwitch": 0, + "oscillationSwitch": 1, + "oscillationState": 1, + "muteSwitch": 1, + "muteState": 1, + "timerRemain": 0, + "temperature": 717, + "humidity": 40, + "thermalComfort": 65, + "errorCode": 0, + "sleepPreference": { + "sleepPreferenceType": "default", + "oscillationSwitch": 0, + "initFanSpeedLevel": 0, + "fallAsleepRemain": 0, + "autoChangeFanLevelSwitch": 0 + }, + "scheduleCount": 0, + "displayingType": 0 + } + } +} diff --git a/tests/components/vesync/fixtures/vesync-devices.json b/tests/components/vesync/fixtures/vesync-devices.json index eac2bf9f5fa70..bb32bae0435cc 100644 --- a/tests/components/vesync/fixtures/vesync-devices.json +++ b/tests/components/vesync/fixtures/vesync-devices.json @@ -108,6 +108,15 @@ "deviceStatus": "on", "connectionStatus": "online", "configModule": "configModule" + }, + { + "cid": "smarttowerfan", + "deviceType": "LTF-F422S-KEU", + "deviceName": "SmartTowerFan", + "subDeviceNo": null, + "deviceStatus": "on", + "connectionStatus": "online", + "configModule": "configModule" } ] } diff --git a/tests/components/vesync/snapshots/test_fan.ambr b/tests/components/vesync/snapshots/test_fan.ambr index 1dea5f28f2ccc..17e75f985f524 100644 --- a/tests/components/vesync/snapshots/test_fan.ambr +++ b/tests/components/vesync/snapshots/test_fan.ambr @@ -576,6 +576,107 @@ list([ ]) # --- +# name: test_fan_state[SmartTowerFan][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'smarttowerfan', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LTF-F422S-KEU', + 'model_id': None, + 'name': 'SmartTowerFan', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'suggested_area': None, + 'sw_version': None, + 'via_device_id': None, + }), + ]) +# --- +# name: test_fan_state[SmartTowerFan][entities] + list([ + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'preset_modes': list([ + 'advancedSleep', + 'auto', + 'turbo', + ]), + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'fan', + 'entity_category': None, + 'entity_id': 'fan.smarttowerfan', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'vesync', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': 'vesync', + 'unique_id': 'smarttowerfan', + 'unit_of_measurement': None, + }), + ]) +# --- +# name: test_fan_state[SmartTowerFan][fan.smarttowerfan] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'child_lock': False, + 'friendly_name': 'SmartTowerFan', + 'mode': 'normal', + 'night_light': 'off', + 'percentage': None, + 'percentage_step': 7.6923076923076925, + 'preset_mode': None, + 'preset_modes': list([ + 'advancedSleep', + 'auto', + 'turbo', + ]), + 'screen_status': False, + 'supported_features': , + }), + 'context': , + 'entity_id': 'fan.smarttowerfan', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- # name: test_fan_state[Temperature Light][devices] list([ DeviceRegistryEntrySnapshot({ diff --git a/tests/components/vesync/snapshots/test_light.ambr b/tests/components/vesync/snapshots/test_light.ambr index ba6c7ab51b992..74f63ce72a14c 100644 --- a/tests/components/vesync/snapshots/test_light.ambr +++ b/tests/components/vesync/snapshots/test_light.ambr @@ -447,6 +447,44 @@ list([ ]) # --- +# name: test_light_state[SmartTowerFan][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'smarttowerfan', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LTF-F422S-KEU', + 'model_id': None, + 'name': 'SmartTowerFan', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'suggested_area': None, + 'sw_version': None, + 'via_device_id': None, + }), + ]) +# --- +# name: test_light_state[SmartTowerFan][entities] + list([ + ]) +# --- # name: test_light_state[Temperature Light][devices] list([ DeviceRegistryEntrySnapshot({ diff --git a/tests/components/vesync/snapshots/test_sensor.ambr b/tests/components/vesync/snapshots/test_sensor.ambr index 50bee417a2877..2525dcd642eb2 100644 --- a/tests/components/vesync/snapshots/test_sensor.ambr +++ b/tests/components/vesync/snapshots/test_sensor.ambr @@ -1155,6 +1155,44 @@ 'state': '0', }) # --- +# name: test_sensor_state[SmartTowerFan][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'smarttowerfan', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LTF-F422S-KEU', + 'model_id': None, + 'name': 'SmartTowerFan', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'suggested_area': None, + 'sw_version': None, + 'via_device_id': None, + }), + ]) +# --- +# name: test_sensor_state[SmartTowerFan][entities] + list([ + ]) +# --- # name: test_sensor_state[Temperature Light][devices] list([ DeviceRegistryEntrySnapshot({ diff --git a/tests/components/vesync/snapshots/test_switch.ambr b/tests/components/vesync/snapshots/test_switch.ambr index 596aa0c94ada6..0a72bb3ca47c4 100644 --- a/tests/components/vesync/snapshots/test_switch.ambr +++ b/tests/components/vesync/snapshots/test_switch.ambr @@ -385,6 +385,44 @@ 'state': 'on', }) # --- +# name: test_switch_state[SmartTowerFan][devices] + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'vesync', + 'smarttowerfan', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'VeSync', + 'model': 'LTF-F422S-KEU', + 'model_id': None, + 'name': 'SmartTowerFan', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'suggested_area': None, + 'sw_version': None, + 'via_device_id': None, + }), + ]) +# --- +# name: test_switch_state[SmartTowerFan][entities] + list([ + ]) +# --- # name: test_switch_state[Temperature Light][devices] list([ DeviceRegistryEntrySnapshot({ From fa951af5fc47be3fa3922359121266975d4e9ec4 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sat, 18 Jan 2025 23:08:37 +0000 Subject: [PATCH 3/9] Schedule update after turn off --- homeassistant/components/vesync/fan.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index 9705002052430..e83407048843f 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -225,3 +225,4 @@ def turn_on( def turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" self.device.turn_off() + self.schedule_update_ha_state() From b462624ac3fd56bbd3821020545160d7be9fe3dd Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sat, 18 Jan 2025 23:44:39 +0000 Subject: [PATCH 4/9] Adjust updates to refresh library --- homeassistant/components/vesync/fan.py | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index e83407048843f..7e390d2ed4251 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -167,12 +167,12 @@ def extra_state_attributes(self) -> dict[str, Any]: return attr - def set_percentage(self, percentage: int) -> None: + async def async_set_percentage(self, percentage: int) -> None: """Set the speed of the device.""" if percentage == 0: - self.device.turn_off() + await self.hass.async_add_executor_job(self.device.turn_off) elif not self.device.is_on: - self.device.turn_on() + await self.hass.async_add_executor_job(self.device.turn_on) self.device.manual_mode() self.device.change_fan_speed( @@ -182,9 +182,9 @@ def set_percentage(self, percentage: int) -> None: ) ) ) - self.schedule_update_ha_state() + await self.coordinator.async_request_refresh() - def set_preset_mode(self, preset_mode: str) -> None: + async def async_set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode of device.""" if preset_mode not in VS_FAN_MODE_PRESET_LIST_HA: raise ValueError( @@ -193,22 +193,22 @@ def set_preset_mode(self, preset_mode: str) -> None: ) if not self.device.is_on: - self.device.turn_on() + await self.hass.async_add_executor_job(self.device.turn_on) if preset_mode == VS_FAN_MODE_AUTO: - self.device.auto_mode() + await self.hass.async_add_executor_job(self.device.auto_mode) elif preset_mode == VS_FAN_MODE_SLEEP: - self.device.sleep_mode() + await self.hass.async_add_executor_job(self.device.sleep_mode) elif preset_mode == VS_FAN_MODE_PET: - self.device.pet_mode() + await self.hass.async_add_executor_job(self.device.pet_mode) elif preset_mode == VS_FAN_MODE_TURBO: - self.device.turbo_mode() + await self.hass.async_add_executor_job(self.device.turbo_mode) elif preset_mode == VS_FAN_MODE_ADVANCED_SLEEP: - self.device.advanced_sleep_mode() + await self.hass.async_add_executor_job(self.device.advanced_sleep_mode) - self.schedule_update_ha_state() + await self.coordinator.async_request_refresh() - def turn_on( + async def async_turn_on( self, percentage: int | None = None, preset_mode: str | None = None, @@ -216,13 +216,13 @@ def turn_on( ) -> None: """Turn the device on.""" if preset_mode: - self.set_preset_mode(preset_mode) + await self.async_set_preset_mode(preset_mode) return if percentage is None: percentage = 50 - self.set_percentage(percentage) + await self.async_set_percentage(percentage) - def turn_off(self, **kwargs: Any) -> None: + async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" self.device.turn_off() - self.schedule_update_ha_state() + await self.coordinator.async_request_refresh() From 7e0a34f6ae2fb42d8c1c5e585c2eb145be58390a Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sat, 18 Jan 2025 16:53:25 -0700 Subject: [PATCH 5/9] correct off command --- homeassistant/components/vesync/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index 7e390d2ed4251..b820548ad6bd5 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -224,5 +224,5 @@ async def async_turn_on( async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" - self.device.turn_off() + await self.hass.async_add_executor_job(self.device.turn_off) await self.coordinator.async_request_refresh() From 4a3956fd39825301c0eef01a2fdee3ae0e54fece Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sun, 19 Jan 2025 00:49:47 +0000 Subject: [PATCH 6/9] Revert changes --- homeassistant/components/vesync/fan.py | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index b820548ad6bd5..e83407048843f 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -167,12 +167,12 @@ def extra_state_attributes(self) -> dict[str, Any]: return attr - async def async_set_percentage(self, percentage: int) -> None: + def set_percentage(self, percentage: int) -> None: """Set the speed of the device.""" if percentage == 0: - await self.hass.async_add_executor_job(self.device.turn_off) + self.device.turn_off() elif not self.device.is_on: - await self.hass.async_add_executor_job(self.device.turn_on) + self.device.turn_on() self.device.manual_mode() self.device.change_fan_speed( @@ -182,9 +182,9 @@ async def async_set_percentage(self, percentage: int) -> None: ) ) ) - await self.coordinator.async_request_refresh() + self.schedule_update_ha_state() - async def async_set_preset_mode(self, preset_mode: str) -> None: + def set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode of device.""" if preset_mode not in VS_FAN_MODE_PRESET_LIST_HA: raise ValueError( @@ -193,22 +193,22 @@ async def async_set_preset_mode(self, preset_mode: str) -> None: ) if not self.device.is_on: - await self.hass.async_add_executor_job(self.device.turn_on) + self.device.turn_on() if preset_mode == VS_FAN_MODE_AUTO: - await self.hass.async_add_executor_job(self.device.auto_mode) + self.device.auto_mode() elif preset_mode == VS_FAN_MODE_SLEEP: - await self.hass.async_add_executor_job(self.device.sleep_mode) + self.device.sleep_mode() elif preset_mode == VS_FAN_MODE_PET: - await self.hass.async_add_executor_job(self.device.pet_mode) + self.device.pet_mode() elif preset_mode == VS_FAN_MODE_TURBO: - await self.hass.async_add_executor_job(self.device.turbo_mode) + self.device.turbo_mode() elif preset_mode == VS_FAN_MODE_ADVANCED_SLEEP: - await self.hass.async_add_executor_job(self.device.advanced_sleep_mode) + self.device.advanced_sleep_mode() - await self.coordinator.async_request_refresh() + self.schedule_update_ha_state() - async def async_turn_on( + def turn_on( self, percentage: int | None = None, preset_mode: str | None = None, @@ -216,13 +216,13 @@ async def async_turn_on( ) -> None: """Turn the device on.""" if preset_mode: - await self.async_set_preset_mode(preset_mode) + self.set_preset_mode(preset_mode) return if percentage is None: percentage = 50 - await self.async_set_percentage(percentage) + self.set_percentage(percentage) - async def async_turn_off(self, **kwargs: Any) -> None: + def turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" - await self.hass.async_add_executor_job(self.device.turn_off) - await self.coordinator.async_request_refresh() + self.device.turn_off() + self.schedule_update_ha_state() From 1d9355c056baf7cdf8c009ef3fbd1b15e29b0b3d Mon Sep 17 00:00:00 2001 From: cdnninja Date: Fri, 31 Jan 2025 05:20:52 +0000 Subject: [PATCH 7/9] Merge corrections --- homeassistant/components/vesync/const.py | 2 ++ homeassistant/components/vesync/fan.py | 25 ++++++++++--------- .../components/vesync/snapshots/test_fan.ambr | 6 ++--- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/vesync/const.py b/homeassistant/components/vesync/const.py index f294c81ed2edb..0dd0c706ef368 100644 --- a/homeassistant/components/vesync/const.py +++ b/homeassistant/components/vesync/const.py @@ -41,6 +41,7 @@ VS_FAN_MODE_TURBO = "turbo" VS_FAN_MODE_PET = "pet" VS_FAN_MODE_MANUAL = "manual" +VS_FAN_MODE_NORMAL = "normal" # not a full list as manual is used as speed not present VS_FAN_MODE_PRESET_LIST_HA = [ @@ -49,6 +50,7 @@ VS_FAN_MODE_ADVANCED_SLEEP, VS_FAN_MODE_TURBO, VS_FAN_MODE_PET, + VS_FAN_MODE_NORMAL, ] VeSyncHumidifierDevice = VeSyncHumid200300S | VeSyncSuperior6000S diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index 370b43b0b7c28..279579f53191b 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -29,6 +29,7 @@ VS_FAN_MODE_ADVANCED_SLEEP, VS_FAN_MODE_AUTO, VS_FAN_MODE_MANUAL, + VS_FAN_MODE_NORMAL, VS_FAN_MODE_PET, VS_FAN_MODE_PRESET_LIST_HA, VS_FAN_MODE_SLEEP, @@ -195,18 +196,18 @@ def set_preset_mode(self, preset_mode: str) -> None: if not self.device.is_on: self.device.turn_on() - if preset_mode == FAN_MODE_AUTO: - self.smartfan.auto_mode() - elif preset_mode == FAN_MODE_SLEEP: - self.smartfan.sleep_mode() - elif preset_mode == FAN_MODE_ADVANCED_SLEEP: - self.smartfan.advanced_sleep_mode() - elif preset_mode == FAN_MODE_PET: - self.smartfan.pet_mode() - elif preset_mode == FAN_MODE_TURBO: - self.smartfan.turbo_mode() - elif preset_mode == FAN_MODE_NORMAL: - self.smartfan.normal_mode() + if preset_mode == VS_FAN_MODE_AUTO: + self.device.auto_mode() + elif preset_mode == VS_FAN_MODE_SLEEP: + self.device.sleep_mode() + elif preset_mode == VS_FAN_MODE_ADVANCED_SLEEP: + self.device.advanced_sleep_mode() + elif preset_mode == VS_FAN_MODE_PET: + self.device.pet_mode() + elif preset_mode == VS_FAN_MODE_TURBO: + self.device.turbo_mode() + elif preset_mode == VS_FAN_MODE_NORMAL: + self.device.normal_mode() self.schedule_update_ha_state() diff --git a/tests/components/vesync/snapshots/test_fan.ambr b/tests/components/vesync/snapshots/test_fan.ambr index fddc75630d215..86b6dfdece80c 100644 --- a/tests/components/vesync/snapshots/test_fan.ambr +++ b/tests/components/vesync/snapshots/test_fan.ambr @@ -620,8 +620,8 @@ 'preset_modes': list([ 'advancedSleep', 'auto', - 'turbo', 'normal', + 'turbo', ]), }), 'config_entry_id': , @@ -661,12 +661,12 @@ 'night_light': 'off', 'percentage': None, 'percentage_step': 7.6923076923076925, - 'preset_mode': None, + 'preset_mode': 'normal', 'preset_modes': list([ 'advancedSleep', 'auto', - 'turbo', 'normal', + 'turbo', ]), 'screen_status': False, 'supported_features': , From 5ba0b7e07f9c02d9481b0b3c31a50bd57dc1fbd6 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Wed, 5 Feb 2025 17:29:52 -0700 Subject: [PATCH 8/9] Remove unused code to increase test coverage --- homeassistant/components/vesync/fan.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index 279579f53191b..0865c80dd62a7 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -141,11 +141,6 @@ def preset_mode(self) -> str | None: return self.device.mode return None - @property - def unique_info(self): - """Return the ID of this fan.""" - return self.device.uuid - @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the fan.""" From 9225b44fe1ac64555ac6e254a351e3ea37532067 Mon Sep 17 00:00:00 2001 From: cdnnninja Date: Thu, 6 Feb 2025 00:37:22 +0000 Subject: [PATCH 9/9] Ruff --- homeassistant/components/vesync/common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/vesync/common.py b/homeassistant/components/vesync/common.py index 92a9ed5c00874..6dda6800c6273 100644 --- a/homeassistant/components/vesync/common.py +++ b/homeassistant/components/vesync/common.py @@ -57,16 +57,19 @@ def is_humidifier(device: VeSyncBaseDevice) -> bool: return isinstance(device, VeSyncHumidifierDevice) + def is_fan(device: VeSyncBaseDevice) -> bool: """Check if the device represents a fan.""" return isinstance(device, VeSyncFanDevice) - + + def is_outlet(device: VeSyncBaseDevice) -> bool: """Check if the device represents an outlet.""" return isinstance(device, VeSyncOutlet) + def is_wall_switch(device: VeSyncBaseDevice) -> bool: """Check if the device represents a wall switch, note this doessn't include dimming switches."""