From 627c7445b3fcc7d23b4bb3a67d84d557109763b2 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Mon, 17 Mar 2025 14:40:22 +0800 Subject: [PATCH 1/5] fix: on/off feature initialization --- custom_components/xiaomi_home/climate.py | 37 +++++++++++-------- .../xiaomi_home/miot/specs/spec_modify.yaml | 5 +++ 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index 63c01a7..84026b3 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -101,21 +101,20 @@ class FeatureOnOff(MIoTServiceEntity, ClimateEntity): self._prop_on = None super().__init__(miot_device=miot_device, entity_data=entity_data) - # properties - for prop in entity_data.props: - if prop.name == 'on': - if ( - # The "on" property of the "fan-control" service is not - # the on/off feature of the entity. - prop.service.name == 'air-conditioner' or - prop.service.name == 'heater' or - prop.service.name == 'thermostat' or - prop.service.name == 'electric-blanket'): - self._attr_supported_features |= ( - ClimateEntityFeature.TURN_ON) - self._attr_supported_features |= ( - ClimateEntityFeature.TURN_OFF) - self._prop_on = prop + + def _init_on_off(self, service_name: str, prop_name: str) -> None: + """Initialize the on_off feature.""" + for prop in self.entity_data.props: + if prop.name == prop_name and prop.service.name == service_name: + if prop.format_ != bool: + _LOGGER.error('wrong format %s %s, %s', service_name, + prop_name, self.entity_id) + continue + self._attr_supported_features |= ( + ClimateEntityFeature.TURN_ON) + self._attr_supported_features |= ( + ClimateEntityFeature.TURN_OFF) + self._prop_on = prop async def async_turn_on(self) -> None: """Turn on.""" @@ -447,6 +446,8 @@ class Heater(FeatureOnOff, FeatureTargetTemperature, FeatureTemperature, self._attr_icon = 'mdi:radiator' # hvac modes self._attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF] + # on/off + self._init_on_off('heater', 'on') # preset modes self._init_preset_modes('heater', 'heat-level') @@ -482,6 +483,8 @@ class AirConditioner(FeatureOnOff, FeatureTargetTemperature, super().__init__(miot_device=miot_device, entity_data=entity_data) self._attr_icon = 'mdi:air-conditioner' + # on/off + self._init_on_off('air-conditioner', 'on') # hvac modes self._attr_hvac_modes = None for prop in entity_data.props: @@ -677,6 +680,8 @@ class Thermostat(FeatureOnOff, FeatureTargetTemperature, FeatureTemperature, self._attr_icon = 'mdi:thermostat' # hvac modes self._attr_hvac_modes = [HVACMode.AUTO, HVACMode.OFF] + # on/off + self._init_on_off('thermostat', 'on') # preset modes self._init_preset_modes('thermostat', 'mode') @@ -705,6 +710,8 @@ class ElectricBlanket(FeatureOnOff, FeatureTargetTemperature, self._attr_icon = 'mdi:rug' # hvac modes self._attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF] + # on/off + self._init_on_off('electric-blanket', 'on') # preset modes self._init_preset_modes('electric-blanket', 'mode') diff --git a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml index bee4ddf..cd156a1 100644 --- a/custom_components/xiaomi_home/miot/specs/spec_modify.yaml +++ b/custom_components/xiaomi_home/miot/specs/spec_modify.yaml @@ -76,3 +76,8 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:2: urn:miot-spec-v2:d urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:5: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 +urn:miot-spec-v2:device:airer:0000A00D:mrbond-m33a:1: + prop.2.3: + name: current-position-a + prop.2.11: + name: current-position-b From dd46aa02f65501cfdda4e66bc1622feca2e95da1 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Mon, 17 Mar 2025 14:58:37 +0800 Subject: [PATCH 2/5] fix: the service name of the mode property --- custom_components/xiaomi_home/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index 84026b3..1df2e75 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -488,7 +488,7 @@ class AirConditioner(FeatureOnOff, FeatureTargetTemperature, # hvac modes self._attr_hvac_modes = None for prop in entity_data.props: - if prop.name == 'mode': + if prop.name == 'mode' and prop.service.name == 'air-conditioner': if not prop.value_list: _LOGGER.error('invalid mode value_list, %s', self.entity_id) continue @@ -623,7 +623,7 @@ class PtcBathHeater(FeatureTargetTemperature, FeatureTemperature, self._attr_icon = 'mdi:hvac' # hvac modes for prop in entity_data.props: - if prop.name == 'mode': + if prop.name == 'mode' and prop.service.name == 'ptc-bath-heater': if not prop.value_list: _LOGGER.error('invalid mode value_list, %s', self.entity_id) continue From abd86e58e3dc8c1968ba5d6319e55beba2a84b93 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Tue, 18 Mar 2025 17:05:21 +0800 Subject: [PATCH 3/5] fix: required temperature_unit --- custom_components/xiaomi_home/climate.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index 1df2e75..b7cd781 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -51,6 +51,7 @@ from typing import Any, Optional from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.const import UnitOfTemperature from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.components.climate import ( FAN_ON, FAN_OFF, SWING_OFF, SWING_BOTH, SWING_VERTICAL, SWING_HORIZONTAL, @@ -133,6 +134,7 @@ class FeatureTargetTemperature(MIoTServiceEntity, ClimateEntity): entity_data: MIoTEntityData) -> None: """Initialize the feature class.""" self._prop_target_temp = None + self._attr_temperature_unit = None super().__init__(miot_device=miot_device, entity_data=entity_data) # properties @@ -150,6 +152,9 @@ class FeatureTargetTemperature(MIoTServiceEntity, ClimateEntity): self._attr_supported_features |= ( ClimateEntityFeature.TARGET_TEMPERATURE) self._prop_target_temp = prop + # temperature_unit is required by the climate entity + if not self._attr_temperature_unit: + self._attr_temperature_unit = UnitOfTemperature.CELSIUS async def async_set_temperature(self, **kwargs): """Set the target temperature.""" From 928bab2ba03d9aff05bf428c591a6370ae2f4130 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Tue, 18 Mar 2025 17:24:17 +0800 Subject: [PATCH 4/5] fix: hvac mode of ptc-bath-heater --- custom_components/xiaomi_home/climate.py | 34 +++++++++--------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index b7cd781..a798e5d 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -111,10 +111,8 @@ class FeatureOnOff(MIoTServiceEntity, ClimateEntity): _LOGGER.error('wrong format %s %s, %s', service_name, prop_name, self.entity_id) continue - self._attr_supported_features |= ( - ClimateEntityFeature.TURN_ON) - self._attr_supported_features |= ( - ClimateEntityFeature.TURN_OFF) + self._attr_supported_features |= ClimateEntityFeature.TURN_ON + self._attr_supported_features |= ClimateEntityFeature.TURN_OFF self._prop_on = prop async def async_turn_on(self) -> None: @@ -635,15 +633,10 @@ class PtcBathHeater(FeatureTargetTemperature, FeatureTemperature, self._hvac_mode_map = {} for item in prop.value_list.items: if item.name in {'off', 'idle'}: - if (HVACMode.OFF - not in list(self._hvac_mode_map.values())): - self._hvac_mode_map[item.value] = HVACMode.OFF - elif (HVACMode.AUTO - not in list(self._hvac_mode_map.values())): - self._hvac_mode_map[item.value] = HVACMode.AUTO - self._attr_hvac_modes = list(self._hvac_mode_map.values()) - if HVACMode.OFF in self._attr_hvac_modes: - self._prop_mode = prop + self._hvac_mode_map[item.value] = HVACMode.OFF + break + if self._hvac_mode_map: + self._attr_hvac_modes = [HVACMode.AUTO, HVACMode.OFF] else: _LOGGER.error('no idle mode, %s', self.entity_id) # preset modes @@ -651,7 +644,7 @@ class PtcBathHeater(FeatureTargetTemperature, FeatureTemperature, async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the target hvac mode.""" - if self._prop_mode is None: + if self._prop_mode is None or hvac_mode != HVACMode.OFF: 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( @@ -664,13 +657,12 @@ class PtcBathHeater(FeatureTargetTemperature, FeatureTemperature, """The current hvac mode.""" if self._prop_mode is None: return None - mode_value = self.get_map_value( - map_=self._hvac_mode_map, - key=self.get_prop_value(prop=self._prop_mode)) - if mode_value == HVACMode.OFF or mode_value is None: - return mode_value - return HVACMode.AUTO if (HVACMode.AUTO - in self._attr_hvac_modes) else None + current_mode = self.get_prop_value(prop=self._prop_mode) + if current_mode is None: + return None + mode_value = self.get_map_value(map_=self._hvac_mode_map, + key=current_mode) + return HVACMode.OFF if mode_value == HVACMode.OFF else HVACMode.AUTO class Thermostat(FeatureOnOff, FeatureTargetTemperature, FeatureTemperature, From 3c3391b7456e95db224cfaac84e36dd9fe94df17 Mon Sep 17 00:00:00 2001 From: LiShuzhen Date: Tue, 18 Mar 2025 17:31:35 +0800 Subject: [PATCH 5/5] perf: reduce loop times --- custom_components/xiaomi_home/climate.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index a798e5d..f0b38b7 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -114,6 +114,7 @@ class FeatureOnOff(MIoTServiceEntity, ClimateEntity): self._attr_supported_features |= ClimateEntityFeature.TURN_ON self._attr_supported_features |= ClimateEntityFeature.TURN_OFF self._prop_on = prop + break async def async_turn_on(self) -> None: """Turn on.""" @@ -150,6 +151,7 @@ class FeatureTargetTemperature(MIoTServiceEntity, ClimateEntity): self._attr_supported_features |= ( ClimateEntityFeature.TARGET_TEMPERATURE) self._prop_target_temp = prop + break # temperature_unit is required by the climate entity if not self._attr_temperature_unit: self._attr_temperature_unit = UnitOfTemperature.CELSIUS @@ -199,6 +201,7 @@ class FeaturePresetMode(MIoTServiceEntity, ClimateEntity): self._attr_supported_features |= ( ClimateEntityFeature.PRESET_MODE) self._prop_mode = prop + break async def async_set_preset_mode(self, preset_mode: str) -> None: """Set the preset mode.""" @@ -367,6 +370,7 @@ class FeatureTemperature(MIoTServiceEntity, ClimateEntity): for prop in entity_data.props: if prop.name == 'temperature': self._prop_env_temperature = prop + break @property def current_temperature(self) -> Optional[float]: @@ -389,6 +393,7 @@ class FeatureHumidity(MIoTServiceEntity, ClimateEntity): for prop in entity_data.props: if prop.name == 'relative-humidity': self._prop_env_humidity = prop + break @property def current_humidity(self) -> Optional[float]: @@ -420,6 +425,7 @@ class FeatureTargetHumidity(MIoTServiceEntity, ClimateEntity): self._attr_supported_features |= ( ClimateEntityFeature.TARGET_HUMIDITY) self._prop_target_humidity = prop + break async def async_set_humidity(self, humidity): """Set the target humidity."""