feat: thermostat preset mode (#833)
Some checks are pending
Tests / check-rule-format (push) Waiting to run
Validate / validate-hassfest (push) Waiting to run
Validate / validate-hacs (push) Waiting to run
Validate / validate-lint (push) Waiting to run
Validate / validate-setup (push) Waiting to run

This commit is contained in:
Li Shuzhen 2025-03-07 08:48:35 +08:00 committed by GitHub
parent 4482d257dc
commit 97d89b3a04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 22 additions and 67 deletions

View File

@ -189,7 +189,7 @@ class FeaturePresetMode(MIoTServiceEntity, ClimateEntity):
for prop in self.entity_data.props: for prop in self.entity_data.props:
if prop.name == prop_name and prop.service.name == service_name: if prop.name == prop_name and prop.service.name == service_name:
if not prop.value_list: 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) prop_name, self.entity_id)
continue continue
self._mode_map = prop.value_list.to_map() 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) super().__init__(miot_device=miot_device, entity_data=entity_data)
# properties # properties
for prop in entity_data.props: 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: if not prop.value_list:
_LOGGER.error('invalid fan-level value_list, %s', _LOGGER.error('invalid fan-level value_list, %s',
self.entity_id) self.entity_id)
@ -665,78 +667,31 @@ class PtcBathHeater(FeatureTargetTemperature, FeatureTemperature,
class Thermostat(FeatureOnOff, FeatureTargetTemperature, FeatureTemperature, class Thermostat(FeatureOnOff, FeatureTargetTemperature, FeatureTemperature,
FeatureHumidity, FeatureFanMode): FeatureHumidity, FeatureFanMode, FeaturePresetMode):
"""Thermostat""" """Thermostat"""
_prop_mode: Optional[MIoTSpecProperty]
_hvac_mode_map: Optional[dict[int, HVACMode]]
def __init__(self, miot_device: MIoTDevice, def __init__(self, miot_device: MIoTDevice,
entity_data: MIoTEntityData) -> None: entity_data: MIoTEntityData) -> None:
"""Initialize the thermostat.""" """Initialize the thermostat."""
self._prop_mode = None
self._hvac_mode_map = None
super().__init__(miot_device=miot_device, entity_data=entity_data) super().__init__(miot_device=miot_device, entity_data=entity_data)
self._attr_icon = 'mdi:thermostat' self._attr_icon = 'mdi:thermostat'
# hvac modes # hvac modes
self._attr_hvac_modes = None self._attr_hvac_modes = [HVACMode.AUTO, HVACMode.OFF]
for prop in entity_data.props: # preset modes
if prop.name == 'mode': self._init_preset_modes('thermostat', '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)
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the target hvac mode.""" """Set the target hvac mode."""
# set the device off await self.set_property_async(
if hvac_mode == HVACMode.OFF: prop=self._prop_on,
if not await self.set_property_async(prop=self._prop_on, value=False if hvac_mode == HVACMode.OFF else True)
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}')
@property @property
def hvac_mode(self) -> Optional[HVACMode]: def hvac_mode(self) -> Optional[HVACMode]:
"""The current hvac mode.""" """The current hvac mode."""
if self.get_prop_value(prop=self._prop_on) is False: return (HVACMode.AUTO if self.get_prop_value(
return HVACMode.OFF prop=self._prop_on) else 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)
class ElectricBlanket(FeatureOnOff, FeatureTargetTemperature, class ElectricBlanket(FeatureOnOff, FeatureTargetTemperature,

View File

@ -1473,10 +1473,12 @@ class MIoTSpecParser:
key=':'.join(p_type_strs[:5])) key=':'.join(p_type_strs[:5]))
or property_['description'] or property_['description']
or spec_prop.name) or spec_prop.name)
if 'value-range' in property_: # Modify value-list before translation
spec_prop.value_range = property_['value-range'] v_list: list[dict] = self._spec_modify.get_prop_value_list(
elif 'value-list' in property_: siid=service['iid'], piid=property_['iid'])
v_list: list[dict] = property_['value-list'] 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): for index, v in enumerate(v_list):
if v['description'].strip() == '': if v['description'].strip() == '':
v['description'] = f'v_{v["value"]}' v['description'] = f'v_{v["value"]}'
@ -1490,6 +1492,8 @@ class MIoTSpecParser:
f'{v["description"]}') f'{v["description"]}')
or v['name']) or v['name'])
spec_prop.value_list = MIoTSpecValueList.from_spec(v_list) 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': elif property_['format'] == 'bool':
v_tag = ':'.join(p_type_strs[:5]) v_tag = ':'.join(p_type_strs[:5])
v_descriptions = ( v_descriptions = (
@ -1514,10 +1518,6 @@ class MIoTSpecParser:
siid=service['iid'], piid=property_['iid']) siid=service['iid'], piid=property_['iid'])
if custom_range: if custom_range:
spec_prop.value_range = 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 # Parse service event
for event in service.get('events', []): for event in service.get('events', []):
if ( if (