From 7afe3abb23be0db11651fc95baf481259f689461 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Thu, 26 Dec 2024 13:55:09 +0800 Subject: [PATCH 1/7] fix: fan speed --- custom_components/xiaomi_home/fan.py | 58 ++++++++++++++++++---------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index 42947ce..aa98c8e 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -55,7 +55,9 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.util.percentage import ( percentage_to_ranged_value, - ranged_value_to_percentage + ranged_value_to_percentage, + ordered_list_item_to_percentage, + percentage_to_ordered_list_item ) from .miot.miot_spec import MIoTSpecProperty @@ -93,6 +95,8 @@ class Fan(MIoTServiceEntity, FanEntity): _speed_min: Optional[int] _speed_max: Optional[int] _speed_step: Optional[int] + _speed_names: Optional[list] + _speed_name_value: Optional[list] _mode_list: Optional[dict[Any, Any]] def __init__( @@ -110,6 +114,9 @@ class Fan(MIoTServiceEntity, FanEntity): self._speed_min = 65535 self._speed_max = 0 self._speed_step = 1 + self._speed_names = [] + self._speed_name_value = [] + self._mode_list = None # properties @@ -124,19 +131,16 @@ class Fan(MIoTServiceEntity, FanEntity): self._speed_min = prop.value_range['min'] self._speed_max = prop.value_range['max'] self._speed_step = prop.value_range['step'] - self._attr_speed_count = self._speed_max - self._speed_min+1 + self._attr_speed_count = (self._speed_max - self._speed_min + )/self._speed_step+1 self._attr_supported_features |= FanEntityFeature.SET_SPEED self._prop_fan_level = prop - elif ( - self._prop_fan_level is None - and isinstance(prop.value_list, list) - and prop.value_list - ): + elif isinstance(prop.value_list, list) and prop.value_list: # Fan level with value-list for item in prop.value_list: - self._speed_min = min(self._speed_min, item['value']) - self._speed_max = max(self._speed_max, item['value']) - self._attr_speed_count = self._speed_max - self._speed_min+1 + self._speed_names.append(item['description']) + self._speed_name_value.append(item['value']) + self._attr_speed_count = len(prop.value_list) self._attr_supported_features |= FanEntityFeature.SET_SPEED self._prop_fan_level = prop elif prop.name == 'mode': @@ -182,9 +186,19 @@ class Fan(MIoTServiceEntity, FanEntity): await self.set_property_async(prop=self._prop_on, value=True) # percentage if percentage: - await self.set_property_async( - prop=self._prop_fan_level, - value=int(percentage*self._attr_speed_count/100)) + if self._speed_names: + speed = percentage_to_ordered_list_item(self._speed_names, + percentage) + order = self._speed_names.index(speed) + await self.set_property_async(prop=self._prop_fan_level, + value=self._speed_name_value[order]) + else: + speed = int((self._speed_max - self._speed_min)*percentage/100 + )+self._speed_min + step_cnt = int((speed - self._speed_min)/self._speed_step) + speed = self._speed_min+step_cnt*self._speed_step + await self.set_property_async(prop=self._prop_fan_level, + value=speed) # preset_mode if preset_mode: await self.set_property_async( @@ -246,9 +260,16 @@ class Fan(MIoTServiceEntity, FanEntity): def percentage(self) -> Optional[int]: """Return the current percentage of the fan speed.""" fan_level = self.get_prop_value(prop=self._prop_fan_level) - return ranged_value_to_percentage( - low_high_range=(self._speed_min, self._speed_max), - value=fan_level) if fan_level else None + if fan_level is None: + return None + if self._speed_names: + order = self._speed_name_value.index(fan_level) + return ordered_list_item_to_percentage(self._speed_names, + self._speed_names[order]) + else: + return ranged_value_to_percentage( + low_high_range=(self._speed_min, self._speed_max), + value=fan_level) @property def oscillating(self) -> Optional[bool]: @@ -257,8 +278,3 @@ class Fan(MIoTServiceEntity, FanEntity): self.get_prop_value( prop=self._prop_horizontal_swing) if self._prop_horizontal_swing else None) - - @property - def percentage_step(self) -> float: - """Return the step of the fan speed.""" - return self._speed_step From d58347031667d18b6554dd762a5d1ed7c6df2719 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Fri, 3 Jan 2025 09:29:24 +0800 Subject: [PATCH 2/7] fix: fan speed names map --- custom_components/xiaomi_home/fan.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index aa98c8e..283c63c 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -96,7 +96,7 @@ class Fan(MIoTServiceEntity, FanEntity): _speed_max: Optional[int] _speed_step: Optional[int] _speed_names: Optional[list] - _speed_name_value: Optional[list] + _speed_name_map: Optional[dict[int, str]] _mode_list: Optional[dict[Any, Any]] def __init__( @@ -115,7 +115,7 @@ class Fan(MIoTServiceEntity, FanEntity): self._speed_max = 0 self._speed_step = 1 self._speed_names = [] - self._speed_name_value = [] + self._speed_name_map = {} self._mode_list = None @@ -137,9 +137,9 @@ class Fan(MIoTServiceEntity, FanEntity): self._prop_fan_level = prop elif isinstance(prop.value_list, list) and prop.value_list: # Fan level with value-list - for item in prop.value_list: - self._speed_names.append(item['description']) - self._speed_name_value.append(item['value']) + self._speed_name_map = {item['value']: item['description'] + for item in prop.value_list} + self._speed_names = list(self._speed_name_map.values()) self._attr_speed_count = len(prop.value_list) self._attr_supported_features |= FanEntityFeature.SET_SPEED self._prop_fan_level = prop @@ -189,9 +189,10 @@ class Fan(MIoTServiceEntity, FanEntity): if self._speed_names: speed = percentage_to_ordered_list_item(self._speed_names, percentage) - order = self._speed_names.index(speed) + speed_value = self.get_map_value(map_=self._speed_name_map, + description=speed) await self.set_property_async(prop=self._prop_fan_level, - value=self._speed_name_value[order]) + value=speed_value) else: speed = int((self._speed_max - self._speed_min)*percentage/100 )+self._speed_min @@ -263,9 +264,8 @@ class Fan(MIoTServiceEntity, FanEntity): if fan_level is None: return None if self._speed_names: - order = self._speed_name_value.index(fan_level) return ordered_list_item_to_percentage(self._speed_names, - self._speed_names[order]) + self._speed_name_map[fan_level]) else: return ranged_value_to_percentage( low_high_range=(self._speed_min, self._speed_max), From 131563d594dd80efb7ac09e026b9fc0999258514 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Fri, 3 Jan 2025 10:14:04 +0800 Subject: [PATCH 3/7] fix: set percentage --- custom_components/xiaomi_home/fan.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index 283c63c..4789f4d 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -194,12 +194,10 @@ class Fan(MIoTServiceEntity, FanEntity): await self.set_property_async(prop=self._prop_fan_level, value=speed_value) else: - speed = int((self._speed_max - self._speed_min)*percentage/100 - )+self._speed_min - step_cnt = int((speed - self._speed_min)/self._speed_step) - speed = self._speed_min+step_cnt*self._speed_step await self.set_property_async(prop=self._prop_fan_level, - value=speed) + value=int(percentage_to_ranged_value( + low_high_range=(self._speed_min, self._speed_max), + percentage=percentage))) # preset_mode if preset_mode: await self.set_property_async( @@ -217,11 +215,18 @@ class Fan(MIoTServiceEntity, FanEntity): async def async_set_percentage(self, percentage: int) -> None: """Set the percentage of the fan speed.""" if percentage > 0: - await self.set_property_async( - prop=self._prop_fan_level, - value=int(percentage_to_ranged_value( - low_high_range=(self._speed_min, self._speed_max), - percentage=percentage))) + if self._speed_names: + speed = percentage_to_ordered_list_item(self._speed_names, + percentage) + speed_value = self.get_map_value(map_=self._speed_name_map, + description=speed) + await self.set_property_async(prop=self._prop_fan_level, + value=speed_value) + else: + await self.set_property_async(prop=self._prop_fan_level, + value=int(percentage_to_ranged_value( + low_high_range=(self._speed_min, self._speed_max), + percentage=percentage))) if not self.is_on: # If the fan is off, turn it on. await self.set_property_async(prop=self._prop_on, value=True) From 03d2223a4a9814dbd66bd6bf8d62ba83451ecca1 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Mon, 6 Jan 2025 11:07:46 +0800 Subject: [PATCH 4/7] docs: the instance code format of valuelist --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 872808a..a91eaaf 100644 --- a/README.md +++ b/README.md @@ -350,7 +350,7 @@ The instance code is the code of the MIoT-Spec-V2 instance, which is in the form ``` service: # service service::property: # property -service::property::valuelist: # the value in value-list of a property +service::property::valuelist: # The index of a value in the value-list of a property service::event: # event service::action: # action ``` From cce3913587e6fe47a5bda561827eeac2a6f5d866 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Mon, 6 Jan 2025 11:46:10 +0800 Subject: [PATCH 5/7] fix: fan level property --- custom_components/xiaomi_home/fan.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index 4789f4d..0ca2bcd 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -135,7 +135,13 @@ class Fan(MIoTServiceEntity, FanEntity): )/self._speed_step+1 self._attr_supported_features |= FanEntityFeature.SET_SPEED self._prop_fan_level = prop - elif isinstance(prop.value_list, list) and prop.value_list: + elif ( + self._prop_fan_level is None + # Fan level with value-range is prior to fan level with value-list + # when a fan has both fan level properties. + and isinstance(prop.value_list, list) + and prop.value_list + ): # Fan level with value-list self._speed_name_map = {item['value']: item['description'] for item in prop.value_list} From 76b0e7c02e84b9155892ef17a47c10ce3d310873 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Mon, 6 Jan 2025 11:53:42 +0800 Subject: [PATCH 6/7] fix: pylint too long line. --- custom_components/xiaomi_home/fan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index 0ca2bcd..ca1a5c6 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -137,8 +137,8 @@ class Fan(MIoTServiceEntity, FanEntity): self._prop_fan_level = prop elif ( self._prop_fan_level is None - # Fan level with value-range is prior to fan level with value-list - # when a fan has both fan level properties. + # Fan level with value-range is prior to fan level with + # value-list when a fan has both fan level properties. and isinstance(prop.value_list, list) and prop.value_list ): From 55c204887aa783fd06decc640415c3ba593e69af Mon Sep 17 00:00:00 2001 From: topsworld Date: Mon, 6 Jan 2025 12:05:15 +0800 Subject: [PATCH 7/7] style: code format --- custom_components/xiaomi_home/fan.py | 53 +++++++++++++++------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index ca1a5c6..caf67cb 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -92,9 +92,9 @@ class Fan(MIoTServiceEntity, FanEntity): _prop_mode: Optional[MIoTSpecProperty] _prop_horizontal_swing: Optional[MIoTSpecProperty] - _speed_min: Optional[int] - _speed_max: Optional[int] - _speed_step: Optional[int] + _speed_min: int + _speed_max: int + _speed_step: int _speed_names: Optional[list] _speed_name_map: Optional[dict[int, str]] _mode_list: Optional[dict[Any, Any]] @@ -131,20 +131,21 @@ class Fan(MIoTServiceEntity, FanEntity): self._speed_min = prop.value_range['min'] self._speed_max = prop.value_range['max'] self._speed_step = prop.value_range['step'] - self._attr_speed_count = (self._speed_max - self._speed_min - )/self._speed_step+1 + self._attr_speed_count = int(( + self._speed_max - self._speed_min)/self._speed_step)+1 self._attr_supported_features |= FanEntityFeature.SET_SPEED self._prop_fan_level = prop elif ( self._prop_fan_level is None - # Fan level with value-range is prior to fan level with - # value-list when a fan has both fan level properties. and isinstance(prop.value_list, list) and prop.value_list ): # Fan level with value-list - self._speed_name_map = {item['value']: item['description'] - for item in prop.value_list} + # Fan level with value-range is prior to fan level with + # value-list when a fan has both fan level properties. + self._speed_name_map = { + item['value']: item['description'] + for item in prop.value_list} self._speed_names = list(self._speed_name_map.values()) self._attr_speed_count = len(prop.value_list) self._attr_supported_features |= FanEntityFeature.SET_SPEED @@ -193,14 +194,15 @@ class Fan(MIoTServiceEntity, FanEntity): # percentage if percentage: if self._speed_names: - speed = percentage_to_ordered_list_item(self._speed_names, - percentage) - speed_value = self.get_map_value(map_=self._speed_name_map, - description=speed) - await self.set_property_async(prop=self._prop_fan_level, - value=speed_value) + speed = percentage_to_ordered_list_item( + self._speed_names, percentage) + speed_value = self.get_map_value( + map_=self._speed_name_map, description=speed) + await self.set_property_async( + prop=self._prop_fan_level, value=speed_value) else: - await self.set_property_async(prop=self._prop_fan_level, + await self.set_property_async( + prop=self._prop_fan_level, value=int(percentage_to_ranged_value( low_high_range=(self._speed_min, self._speed_max), percentage=percentage))) @@ -222,14 +224,15 @@ class Fan(MIoTServiceEntity, FanEntity): """Set the percentage of the fan speed.""" if percentage > 0: if self._speed_names: - speed = percentage_to_ordered_list_item(self._speed_names, - percentage) - speed_value = self.get_map_value(map_=self._speed_name_map, - description=speed) - await self.set_property_async(prop=self._prop_fan_level, - value=speed_value) + speed = percentage_to_ordered_list_item( + self._speed_names, percentage) + speed_value = self.get_map_value( + map_=self._speed_name_map, description=speed) + await self.set_property_async( + prop=self._prop_fan_level, value=speed_value) else: - await self.set_property_async(prop=self._prop_fan_level, + await self.set_property_async( + prop=self._prop_fan_level, value=int(percentage_to_ranged_value( low_high_range=(self._speed_min, self._speed_max), percentage=percentage))) @@ -275,8 +278,8 @@ class Fan(MIoTServiceEntity, FanEntity): if fan_level is None: return None if self._speed_names: - return ordered_list_item_to_percentage(self._speed_names, - self._speed_name_map[fan_level]) + return ordered_list_item_to_percentage( + self._speed_names, self._speed_name_map[fan_level]) else: return ranged_value_to_percentage( low_high_range=(self._speed_min, self._speed_max),