From 97d89b3a045cad0ed95c46671c46cc24ddaf6069 Mon Sep 17 00:00:00 2001 From: Li Shuzhen Date: Fri, 7 Mar 2025 08:48:35 +0800 Subject: [PATCH] feat: thermostat preset mode (#833) --- custom_components/xiaomi_home/climate.py | 73 ++++--------------- .../xiaomi_home/miot/miot_spec.py | 16 ++-- 2 files changed, 22 insertions(+), 67 deletions(-) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index 42897d7..5a390b2 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -189,7 +189,7 @@ class FeaturePresetMode(MIoTServiceEntity, ClimateEntity): for prop in self.entity_data.props: if prop.name == prop_name and prop.service.name == service_name: if not prop.value_list: - _LOGGER.error('invalid %s %s value_list, %s',service_name, + _LOGGER.error('invalid %s %s value_list, %s', service_name, prop_name, self.entity_id) continue self._mode_map = prop.value_list.to_map() @@ -229,7 +229,9 @@ class FeatureFanMode(MIoTServiceEntity, ClimateEntity): super().__init__(miot_device=miot_device, entity_data=entity_data) # properties for prop in entity_data.props: - if prop.name == 'fan-level' and prop.service.name == 'fan-control': + if (prop.name == 'fan-level' and + (prop.service.name == 'fan-control' or + prop.service.name == 'thermostat')): if not prop.value_list: _LOGGER.error('invalid fan-level value_list, %s', self.entity_id) @@ -665,78 +667,31 @@ class PtcBathHeater(FeatureTargetTemperature, FeatureTemperature, class Thermostat(FeatureOnOff, FeatureTargetTemperature, FeatureTemperature, - FeatureHumidity, FeatureFanMode): + FeatureHumidity, FeatureFanMode, FeaturePresetMode): """Thermostat""" - _prop_mode: Optional[MIoTSpecProperty] - _hvac_mode_map: Optional[dict[int, HVACMode]] def __init__(self, miot_device: MIoTDevice, entity_data: MIoTEntityData) -> None: """Initialize the thermostat.""" - self._prop_mode = None - self._hvac_mode_map = None - super().__init__(miot_device=miot_device, entity_data=entity_data) + self._attr_icon = 'mdi:thermostat' # hvac modes - self._attr_hvac_modes = None - for prop in entity_data.props: - if prop.name == 'mode': - if not prop.value_list: - _LOGGER.error('invalid mode value_list, %s', self.entity_id) - continue - self._hvac_mode_map = {} - for item in prop.value_list.items: - if item.name in {'off', 'idle'}: - self._hvac_mode_map[item.value] = HVACMode.OFF - elif item.name in {'auto'}: - self._hvac_mode_map[item.value] = HVACMode.AUTO - elif item.name in {'cool'}: - self._hvac_mode_map[item.value] = HVACMode.COOL - elif item.name in {'heat'}: - self._hvac_mode_map[item.value] = HVACMode.HEAT - elif item.name in {'dry'}: - self._hvac_mode_map[item.value] = HVACMode.DRY - elif item.name in {'fan'}: - self._hvac_mode_map[item.value] = HVACMode.FAN_ONLY - self._attr_hvac_modes = list(self._hvac_mode_map.values()) - self._prop_mode = prop - - if self._attr_hvac_modes is None: - self._attr_hvac_modes = [HVACMode.OFF, HVACMode.AUTO] - elif HVACMode.OFF not in self._attr_hvac_modes: - self._attr_hvac_modes.insert(0, HVACMode.OFF) + self._attr_hvac_modes = [HVACMode.AUTO, HVACMode.OFF] + # preset modes + self._init_preset_modes('thermostat', 'mode') async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the target hvac mode.""" - # set the device off - if hvac_mode == HVACMode.OFF: - if not await self.set_property_async(prop=self._prop_on, - value=False): - raise RuntimeError(f'set climate prop.on failed, {hvac_mode}, ' - f'{self.entity_id}') - return - # set the device on - elif self.get_prop_value(prop=self._prop_on) is False: - await self.set_property_async(prop=self._prop_on, value=True) - # set mode - if self._prop_mode is None: - return - mode_value = self.get_map_key(map_=self._hvac_mode_map, value=hvac_mode) - if mode_value is None or not await self.set_property_async( - prop=self._prop_mode, value=mode_value): - raise RuntimeError( - f'set climate prop.mode failed, {hvac_mode}, {self.entity_id}') + await self.set_property_async( + prop=self._prop_on, + value=False if hvac_mode == HVACMode.OFF else True) @property def hvac_mode(self) -> Optional[HVACMode]: """The current hvac mode.""" - if self.get_prop_value(prop=self._prop_on) is False: - return HVACMode.OFF - return (self.get_map_value(map_=self._hvac_mode_map, - key=self.get_prop_value( - prop=self._prop_mode)) - if self._prop_mode else None) + return (HVACMode.AUTO if self.get_prop_value( + prop=self._prop_on) else HVACMode.OFF) class ElectricBlanket(FeatureOnOff, FeatureTargetTemperature, diff --git a/custom_components/xiaomi_home/miot/miot_spec.py b/custom_components/xiaomi_home/miot/miot_spec.py index d1170d1..fca22a7 100644 --- a/custom_components/xiaomi_home/miot/miot_spec.py +++ b/custom_components/xiaomi_home/miot/miot_spec.py @@ -1473,10 +1473,12 @@ class MIoTSpecParser: key=':'.join(p_type_strs[:5])) or property_['description'] or spec_prop.name) - if 'value-range' in property_: - spec_prop.value_range = property_['value-range'] - elif 'value-list' in property_: - v_list: list[dict] = property_['value-list'] + # Modify value-list before translation + v_list: list[dict] = self._spec_modify.get_prop_value_list( + siid=service['iid'], piid=property_['iid']) + if (v_list is None) and ('value-list' in property_): + v_list = property_['value-list'] + if v_list is not None: for index, v in enumerate(v_list): if v['description'].strip() == '': v['description'] = f'v_{v["value"]}' @@ -1490,6 +1492,8 @@ class MIoTSpecParser: f'{v["description"]}') or v['name']) spec_prop.value_list = MIoTSpecValueList.from_spec(v_list) + if 'value-range' in property_: + spec_prop.value_range = property_['value-range'] elif property_['format'] == 'bool': v_tag = ':'.join(p_type_strs[:5]) v_descriptions = ( @@ -1514,10 +1518,6 @@ class MIoTSpecParser: siid=service['iid'], piid=property_['iid']) if custom_range: spec_prop.value_range = custom_range - custom_list = self._spec_modify.get_prop_value_list( - siid=service['iid'], piid=property_['iid']) - if custom_list: - spec_prop.value_list = custom_list # Parse service event for event in service.get('events', []): if (