From ce7ce7af4bd348213136c9d23887cff7e8867e16 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Tue, 7 Jan 2025 20:21:04 +0800 Subject: [PATCH] fix: fan speed (#464) * fix: fan speed * fix: fan speed names map * fix: set percentage * docs: the instance code format of valuelist * fix: fan level property * fix: pylint too long line. * style: code format --------- Co-authored-by: topsworld --- README.md | 2 +- custom_components/xiaomi_home/fan.py | 80 +++++++++++++++++++--------- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d0cdc97b..e6539e7f 100644 --- a/README.md +++ b/README.md @@ -351,7 +351,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 ``` diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index 42947cec..caf67cb8 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -55,7 +55,9 @@ 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 @@ -90,9 +92,11 @@ 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]] def __init__( @@ -110,6 +114,9 @@ def __init__( self._speed_min = 65535 self._speed_max = 0 self._speed_step = 1 + self._speed_names = [] + self._speed_name_map = {} + self._mode_list = None # properties @@ -124,7 +131,8 @@ def __init__( 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 = int(( + self._speed_max - self._speed_min)/self._speed_step)+1 self._attr_supported_features |= FanEntityFeature.SET_SPEED self._prop_fan_level = prop elif ( @@ -133,10 +141,13 @@ def __init__( 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 + # 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 self._prop_fan_level = prop elif prop.name == 'mode': @@ -182,9 +193,19 @@ async def async_turn_on( 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) + 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))) # preset_mode if preset_mode: await self.set_property_async( @@ -202,11 +223,19 @@ async def async_toggle(self, **kwargs: Any) -> None: 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) @@ -246,9 +275,15 @@ def preset_mode(self) -> Optional[str]: 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: + 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), + value=fan_level) @property def oscillating(self) -> Optional[bool]: @@ -257,8 +292,3 @@ def oscillating(self) -> Optional[bool]: 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