From 7aceb0493ce00c5f84cf171a0c7cd9aa6d649444 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Wed, 5 Mar 2025 14:42:33 +0800 Subject: [PATCH 1/3] fix: fan-level property in thermostat service --- custom_components/xiaomi_home/climate.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index 42897d7..58f1bdb 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) From 8ed972cdb00f7815543e18b3afb0f823a0c66296 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Wed, 5 Mar 2025 18:29:02 +0800 Subject: [PATCH 2/3] feat: cubee.airrtc.1230t hvac mode --- custom_components/xiaomi_home/miot/miot_spec.py | 16 ++++++++-------- .../xiaomi_home/miot/specs/spec_modify.yaml | 11 +++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/custom_components/xiaomi_home/miot/miot_spec.py b/custom_components/xiaomi_home/miot/miot_spec.py index d2292e8..ef5aeec 100644 --- a/custom_components/xiaomi_home/miot/miot_spec.py +++ b/custom_components/xiaomi_home/miot/miot_spec.py @@ -1453,10 +1453,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"]}' @@ -1470,6 +1472,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 = ( @@ -1494,10 +1498,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 ( diff --git a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml index 58ba8ef..297a6a4 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml @@ -58,3 +58,14 @@ urn:miot-spec-v2:device:bath-heater:0000A028:opple-acmoto:1: description: medium - value: 255 description: high +urn:miot-spec-v2:device:thermostat:0000A031:cubee-1230t:1: + prop.2.4: + value-list: + - value: 0 + description: cool + - value: 1 + description: heat + - value: 2 + description: fan + - value: 3 + description: auto From 8abb7979c33572600a686155cb30b6105d43e96a Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Thu, 6 Mar 2025 17:56:44 +0800 Subject: [PATCH 3/3] feat: add preset mode for thermostat --- custom_components/xiaomi_home/climate.py | 67 +++---------------- .../xiaomi_home/miot/specs/spec_modify.yaml | 11 --- 2 files changed, 10 insertions(+), 68 deletions(-) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index 58f1bdb..5a390b2 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -667,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/specs/spec_modify.yaml b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml index 297a6a4..58ba8ef 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml @@ -58,14 +58,3 @@ urn:miot-spec-v2:device:bath-heater:0000A028:opple-acmoto:1: description: medium - value: 255 description: high -urn:miot-spec-v2:device:thermostat:0000A031:cubee-1230t:1: - prop.2.4: - value-list: - - value: 0 - description: cool - - value: 1 - description: heat - - value: 2 - description: fan - - value: 3 - description: auto