mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2026-01-18 16:10:44 +08:00
feat: update value-list logic
This commit is contained in:
parent
3399e3bb20
commit
d25d3f6a93
@ -156,27 +156,24 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'unknown on property, %s', self.entity_id)
|
'unknown on property, %s', self.entity_id)
|
||||||
elif prop.name == 'mode':
|
elif prop.name == 'mode':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'invalid mode value_list, %s', self.entity_id)
|
'invalid mode value_list, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._hvac_mode_map = {}
|
self._hvac_mode_map = {}
|
||||||
for item in prop.value_list:
|
for item in prop.value_list.items:
|
||||||
if item['name'].lower() in {'off', 'idle'}:
|
if item.name in {'off', 'idle'}:
|
||||||
self._hvac_mode_map[item['value']] = HVACMode.OFF
|
self._hvac_mode_map[item.value] = HVACMode.OFF
|
||||||
elif item['name'].lower() in {'auto'}:
|
elif item.name in {'auto'}:
|
||||||
self._hvac_mode_map[item['value']] = HVACMode.AUTO
|
self._hvac_mode_map[item.value] = HVACMode.AUTO
|
||||||
elif item['name'].lower() in {'cool'}:
|
elif item.name in {'cool'}:
|
||||||
self._hvac_mode_map[item['value']] = HVACMode.COOL
|
self._hvac_mode_map[item.value] = HVACMode.COOL
|
||||||
elif item['name'].lower() in {'heat'}:
|
elif item.name in {'heat'}:
|
||||||
self._hvac_mode_map[item['value']] = HVACMode.HEAT
|
self._hvac_mode_map[item.value] = HVACMode.HEAT
|
||||||
elif item['name'].lower() in {'dry'}:
|
elif item.name in {'dry'}:
|
||||||
self._hvac_mode_map[item['value']] = HVACMode.DRY
|
self._hvac_mode_map[item.value] = HVACMode.DRY
|
||||||
elif item['name'].lower() in {'fan'}:
|
elif item.name in {'fan'}:
|
||||||
self._hvac_mode_map[item['value']] = HVACMode.FAN_ONLY
|
self._hvac_mode_map[item.value] = HVACMode.FAN_ONLY
|
||||||
self._attr_hvac_modes = list(self._hvac_mode_map.values())
|
self._attr_hvac_modes = list(self._hvac_mode_map.values())
|
||||||
self._prop_mode = prop
|
self._prop_mode = prop
|
||||||
elif prop.name == 'target-temperature':
|
elif prop.name == 'target-temperature':
|
||||||
@ -204,16 +201,11 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
ClimateEntityFeature.TARGET_HUMIDITY)
|
ClimateEntityFeature.TARGET_HUMIDITY)
|
||||||
self._prop_target_humi = prop
|
self._prop_target_humi = prop
|
||||||
elif prop.name == 'fan-level':
|
elif prop.name == 'fan-level':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'invalid fan-level value_list, %s', self.entity_id)
|
'invalid fan-level value_list, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._fan_mode_map = {
|
self._fan_mode_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
|
||||||
for item in prop.value_list}
|
|
||||||
self._attr_fan_modes = list(self._fan_mode_map.values())
|
self._attr_fan_modes = list(self._fan_mode_map.values())
|
||||||
self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
|
self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
|
||||||
self._prop_fan_level = prop
|
self._prop_fan_level = prop
|
||||||
@ -269,8 +261,8 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
elif self.get_prop_value(prop=self._prop_on) is False:
|
elif self.get_prop_value(prop=self._prop_on) is False:
|
||||||
await self.set_property_async(prop=self._prop_on, value=True)
|
await self.set_property_async(prop=self._prop_on, value=True)
|
||||||
# set mode
|
# set mode
|
||||||
mode_value = self.get_map_value(
|
mode_value = self.get_map_key(
|
||||||
map_=self._hvac_mode_map, description=hvac_mode)
|
map_=self._hvac_mode_map, value=hvac_mode)
|
||||||
if (
|
if (
|
||||||
mode_value is None or
|
mode_value is None or
|
||||||
not await self.set_property_async(
|
not await self.set_property_async(
|
||||||
@ -339,8 +331,8 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
|
|
||||||
async def async_set_fan_mode(self, fan_mode):
|
async def async_set_fan_mode(self, fan_mode):
|
||||||
"""Set new target fan mode."""
|
"""Set new target fan mode."""
|
||||||
mode_value = self.get_map_value(
|
mode_value = self.get_map_key(
|
||||||
map_=self._fan_mode_map, description=fan_mode)
|
map_=self._fan_mode_map, value=fan_mode)
|
||||||
if mode_value is None or not await self.set_property_async(
|
if mode_value is None or not await self.set_property_async(
|
||||||
prop=self._prop_fan_level, value=mode_value):
|
prop=self._prop_fan_level, value=mode_value):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
@ -376,9 +368,9 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
"""Return the hvac mode. e.g., heat, cool mode."""
|
"""Return the hvac mode. e.g., heat, cool mode."""
|
||||||
if self.get_prop_value(prop=self._prop_on) is False:
|
if self.get_prop_value(prop=self._prop_on) is False:
|
||||||
return HVACMode.OFF
|
return HVACMode.OFF
|
||||||
return self.get_map_description(
|
return self.get_map_key(
|
||||||
map_=self._hvac_mode_map,
|
map_=self._hvac_mode_map,
|
||||||
key=self.get_prop_value(prop=self._prop_mode))
|
value=self.get_prop_value(prop=self._prop_mode))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fan_mode(self) -> Optional[str]:
|
def fan_mode(self) -> Optional[str]:
|
||||||
@ -386,7 +378,7 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
|
|
||||||
Requires ClimateEntityFeature.FAN_MODE.
|
Requires ClimateEntityFeature.FAN_MODE.
|
||||||
"""
|
"""
|
||||||
return self.get_map_description(
|
return self.get_map_value(
|
||||||
map_=self._fan_mode_map,
|
map_=self._fan_mode_map,
|
||||||
key=self.get_prop_value(prop=self._prop_fan_level))
|
key=self.get_prop_value(prop=self._prop_fan_level))
|
||||||
|
|
||||||
@ -446,8 +438,8 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
}.get(v_ac_state['M'], None)
|
}.get(v_ac_state['M'], None)
|
||||||
if mode:
|
if mode:
|
||||||
self.set_prop_value(
|
self.set_prop_value(
|
||||||
prop=self._prop_mode, value=self.get_map_value(
|
prop=self._prop_mode, value=self.get_map_key(
|
||||||
map_=self._hvac_mode_map, description=mode))
|
map_=self._hvac_mode_map, value=mode))
|
||||||
# T: target temperature
|
# T: target temperature
|
||||||
if 'T' in v_ac_state and self._prop_target_temp:
|
if 'T' in v_ac_state and self._prop_target_temp:
|
||||||
self.set_prop_value(prop=self._prop_target_temp,
|
self.set_prop_value(prop=self._prop_target_temp,
|
||||||
@ -530,16 +522,11 @@ class Heater(MIoTServiceEntity, ClimateEntity):
|
|||||||
ClimateEntityFeature.TARGET_TEMPERATURE)
|
ClimateEntityFeature.TARGET_TEMPERATURE)
|
||||||
self._prop_target_temp = prop
|
self._prop_target_temp = prop
|
||||||
elif prop.name == 'heat-level':
|
elif prop.name == 'heat-level':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'invalid heat-level value_list, %s', self.entity_id)
|
'invalid heat-level value_list, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._heat_level_map = {
|
self._heat_level_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
|
||||||
for item in prop.value_list}
|
|
||||||
self._attr_preset_modes = list(self._heat_level_map.values())
|
self._attr_preset_modes = list(self._heat_level_map.values())
|
||||||
self._attr_supported_features |= (
|
self._attr_supported_features |= (
|
||||||
ClimateEntityFeature.PRESET_MODE)
|
ClimateEntityFeature.PRESET_MODE)
|
||||||
@ -582,8 +569,8 @@ class Heater(MIoTServiceEntity, ClimateEntity):
|
|||||||
"""Set the preset mode."""
|
"""Set the preset mode."""
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
self._prop_heat_level,
|
self._prop_heat_level,
|
||||||
value=self.get_map_value(
|
value=self.get_map_key(
|
||||||
map_=self._heat_level_map, description=preset_mode))
|
map_=self._heat_level_map, value=preset_mode))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature(self) -> Optional[float]:
|
def target_temperature(self) -> Optional[float]:
|
||||||
@ -613,7 +600,7 @@ class Heater(MIoTServiceEntity, ClimateEntity):
|
|||||||
@property
|
@property
|
||||||
def preset_mode(self) -> Optional[str]:
|
def preset_mode(self) -> Optional[str]:
|
||||||
return (
|
return (
|
||||||
self.get_map_description(
|
self.get_map_value(
|
||||||
map_=self._heat_level_map,
|
map_=self._heat_level_map,
|
||||||
key=self.get_prop_value(prop=self._prop_heat_level))
|
key=self.get_prop_value(prop=self._prop_heat_level))
|
||||||
if self._prop_heat_level else None)
|
if self._prop_heat_level else None)
|
||||||
|
|||||||
@ -132,42 +132,36 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
|||||||
# properties
|
# properties
|
||||||
for prop in entity_data.props:
|
for prop in entity_data.props:
|
||||||
if prop.name == 'motor-control':
|
if prop.name == 'motor-control':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'motor-control value_list is None, %s', self.entity_id)
|
'motor-control value_list is None, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
for item in prop.value_list:
|
for item in prop.value_list.items:
|
||||||
if item['name'].lower() in ['open']:
|
if item.name in {'open'}:
|
||||||
self._attr_supported_features |= (
|
self._attr_supported_features |= (
|
||||||
CoverEntityFeature.OPEN)
|
CoverEntityFeature.OPEN)
|
||||||
self._prop_motor_value_open = item['value']
|
self._prop_motor_value_open = item.value
|
||||||
elif item['name'].lower() in ['close']:
|
elif item.name in {'close'}:
|
||||||
self._attr_supported_features |= (
|
self._attr_supported_features |= (
|
||||||
CoverEntityFeature.CLOSE)
|
CoverEntityFeature.CLOSE)
|
||||||
self._prop_motor_value_close = item['value']
|
self._prop_motor_value_close = item.value
|
||||||
elif item['name'].lower() in ['pause']:
|
elif item.name in {'pause'}:
|
||||||
self._attr_supported_features |= (
|
self._attr_supported_features |= (
|
||||||
CoverEntityFeature.STOP)
|
CoverEntityFeature.STOP)
|
||||||
self._prop_motor_value_pause = item['value']
|
self._prop_motor_value_pause = item.value
|
||||||
self._prop_motor_control = prop
|
self._prop_motor_control = prop
|
||||||
elif prop.name == 'status':
|
elif prop.name == 'status':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'status value_list is None, %s', self.entity_id)
|
'status value_list is None, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
for item in prop.value_list:
|
for item in prop.value_list.items:
|
||||||
if item['name'].lower() in ['opening', 'open']:
|
if item.name in {'opening', 'open'}:
|
||||||
self._prop_status_opening = item['value']
|
self._prop_status_opening = item.value
|
||||||
elif item['name'].lower() in ['closing', 'close']:
|
elif item.name in {'closing', 'close'}:
|
||||||
self._prop_status_closing = item['value']
|
self._prop_status_closing = item.value
|
||||||
elif item['name'].lower() in ['stop', 'pause']:
|
elif item.name in {'stop', 'pause'}:
|
||||||
self._prop_status_stop = item['value']
|
self._prop_status_stop = item.value
|
||||||
self._prop_status = prop
|
self._prop_status = prop
|
||||||
elif prop.name == 'current-position':
|
elif prop.name == 'current-position':
|
||||||
self._prop_current_position = prop
|
self._prop_current_position = prop
|
||||||
|
|||||||
@ -90,10 +90,10 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
_prop_mode: Optional[MIoTSpecProperty]
|
_prop_mode: Optional[MIoTSpecProperty]
|
||||||
_prop_horizontal_swing: Optional[MIoTSpecProperty]
|
_prop_horizontal_swing: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
_speed_min: Optional[int]
|
_speed_min: int
|
||||||
_speed_max: Optional[int]
|
_speed_max: int
|
||||||
_speed_step: Optional[int]
|
_speed_step: int
|
||||||
_mode_list: Optional[dict[Any, Any]]
|
_mode_map: Optional[dict[Any, Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
@ -110,7 +110,7 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
self._speed_min = 65535
|
self._speed_min = 65535
|
||||||
self._speed_max = 0
|
self._speed_max = 0
|
||||||
self._speed_step = 1
|
self._speed_step = 1
|
||||||
self._mode_list = None
|
self._mode_map = None
|
||||||
|
|
||||||
# properties
|
# properties
|
||||||
for prop in entity_data.props:
|
for prop in entity_data.props:
|
||||||
@ -129,47 +129,28 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
self._prop_fan_level = prop
|
self._prop_fan_level = prop
|
||||||
elif (
|
elif (
|
||||||
self._prop_fan_level is None
|
self._prop_fan_level is None
|
||||||
and isinstance(prop.value_list, list)
|
|
||||||
and prop.value_list
|
and prop.value_list
|
||||||
):
|
):
|
||||||
# Fan level with value-list
|
# Fan level with value-list
|
||||||
for item in prop.value_list:
|
for item in prop.value_list.items:
|
||||||
self._speed_min = min(self._speed_min, item['value'])
|
self._speed_min = min(self._speed_min, item.value)
|
||||||
self._speed_max = max(self._speed_max, item['value'])
|
self._speed_max = max(self._speed_max, item.value)
|
||||||
self._attr_speed_count = self._speed_max - self._speed_min+1
|
self._attr_speed_count = self._speed_max - self._speed_min+1
|
||||||
self._attr_supported_features |= FanEntityFeature.SET_SPEED
|
self._attr_supported_features |= FanEntityFeature.SET_SPEED
|
||||||
self._prop_fan_level = prop
|
self._prop_fan_level = prop
|
||||||
elif prop.name == 'mode':
|
elif prop.name == 'mode':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'mode value_list is None, %s', self.entity_id)
|
'mode value_list is None, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._mode_list = {
|
self._mode_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
self._attr_preset_modes = list(self._mode_map.values())
|
||||||
for item in prop.value_list}
|
|
||||||
self._attr_preset_modes = list(self._mode_list.values())
|
|
||||||
self._attr_supported_features |= FanEntityFeature.PRESET_MODE
|
self._attr_supported_features |= FanEntityFeature.PRESET_MODE
|
||||||
self._prop_mode = prop
|
self._prop_mode = prop
|
||||||
elif prop.name == 'horizontal-swing':
|
elif prop.name == 'horizontal-swing':
|
||||||
self._attr_supported_features |= FanEntityFeature.OSCILLATE
|
self._attr_supported_features |= FanEntityFeature.OSCILLATE
|
||||||
self._prop_horizontal_swing = prop
|
self._prop_horizontal_swing = prop
|
||||||
|
|
||||||
def __get_mode_description(self, key: int) -> Optional[str]:
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
return self._mode_list.get(key, None)
|
|
||||||
|
|
||||||
def __get_mode_value(self, description: str) -> Optional[int]:
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
for key, value in self._mode_list.items():
|
|
||||||
if value == description:
|
|
||||||
return key
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def async_turn_on(
|
async def async_turn_on(
|
||||||
self, percentage: int = None, preset_mode: str = None, **kwargs: Any
|
self, percentage: int = None, preset_mode: str = None, **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -189,7 +170,8 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
if preset_mode:
|
if preset_mode:
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
self._prop_mode,
|
self._prop_mode,
|
||||||
value=self.__get_mode_value(description=preset_mode))
|
value=self.get_map_key(
|
||||||
|
map_=self._mode_map, value=preset_mode))
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the fan off."""
|
"""Turn the fan off."""
|
||||||
@ -217,7 +199,8 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
"""Set the preset mode."""
|
"""Set the preset mode."""
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
self._prop_mode,
|
self._prop_mode,
|
||||||
value=self.__get_mode_value(description=preset_mode))
|
value=self.get_map_key(
|
||||||
|
map_=self._mode_map, value=preset_mode))
|
||||||
|
|
||||||
async def async_set_direction(self, direction: str) -> None:
|
async def async_set_direction(self, direction: str) -> None:
|
||||||
"""Set the direction of the fan."""
|
"""Set the direction of the fan."""
|
||||||
@ -238,7 +221,8 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
"""Return the current preset mode,
|
"""Return the current preset mode,
|
||||||
e.g., auto, smart, eco, favorite."""
|
e.g., auto, smart, eco, favorite."""
|
||||||
return (
|
return (
|
||||||
self.__get_mode_description(
|
self.get_map_value(
|
||||||
|
map_=self._mode_map,
|
||||||
key=self.get_prop_value(prop=self._prop_mode))
|
key=self.get_prop_value(prop=self._prop_mode))
|
||||||
if self._prop_mode else None)
|
if self._prop_mode else None)
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
|||||||
_prop_target_humidity: Optional[MIoTSpecProperty]
|
_prop_target_humidity: Optional[MIoTSpecProperty]
|
||||||
_prop_humidity: Optional[MIoTSpecProperty]
|
_prop_humidity: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
_mode_list: dict[Any, Any]
|
_mode_map: dict[Any, Any]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
@ -110,7 +110,7 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
|||||||
self._prop_mode = None
|
self._prop_mode = None
|
||||||
self._prop_target_humidity = None
|
self._prop_target_humidity = None
|
||||||
self._prop_humidity = None
|
self._prop_humidity = None
|
||||||
self._mode_list = None
|
self._mode_map = None
|
||||||
|
|
||||||
# properties
|
# properties
|
||||||
for prop in entity_data.props:
|
for prop in entity_data.props:
|
||||||
@ -129,18 +129,13 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
|||||||
self._prop_target_humidity = prop
|
self._prop_target_humidity = prop
|
||||||
# mode
|
# mode
|
||||||
elif prop.name == 'mode':
|
elif prop.name == 'mode':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'mode value_list is None, %s', self.entity_id)
|
'mode value_list is None, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._mode_list = {
|
self._mode_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
|
||||||
for item in prop.value_list}
|
|
||||||
self._attr_available_modes = list(
|
self._attr_available_modes = list(
|
||||||
self._mode_list.values())
|
self._mode_map.values())
|
||||||
self._attr_supported_features |= HumidifierEntityFeature.MODES
|
self._attr_supported_features |= HumidifierEntityFeature.MODES
|
||||||
self._prop_mode = prop
|
self._prop_mode = prop
|
||||||
# relative-humidity
|
# relative-humidity
|
||||||
@ -163,7 +158,8 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
|||||||
async def async_set_mode(self, mode: str) -> None:
|
async def async_set_mode(self, mode: str) -> None:
|
||||||
"""Set new target preset mode."""
|
"""Set new target preset mode."""
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
prop=self._prop_mode, value=self.__get_mode_value(description=mode))
|
prop=self._prop_mode,
|
||||||
|
value=self.get_map_key(map_=self._mode_map, value=mode))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> Optional[bool]:
|
def is_on(self) -> Optional[bool]:
|
||||||
@ -183,20 +179,6 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
|||||||
@property
|
@property
|
||||||
def mode(self) -> Optional[str]:
|
def mode(self) -> Optional[str]:
|
||||||
"""Return the current preset mode."""
|
"""Return the current preset mode."""
|
||||||
return self.__get_mode_description(
|
return self.get_map_value(
|
||||||
|
map_=self._mode_map,
|
||||||
key=self.get_prop_value(prop=self._prop_mode))
|
key=self.get_prop_value(prop=self._prop_mode))
|
||||||
|
|
||||||
def __get_mode_description(self, key: int) -> Optional[str]:
|
|
||||||
"""Convert mode value to description."""
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
return self._mode_list.get(key, None)
|
|
||||||
|
|
||||||
def __get_mode_value(self, description: str) -> Optional[int]:
|
|
||||||
"""Convert mode description to value."""
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
for key, value in self._mode_list.items():
|
|
||||||
if value == description:
|
|
||||||
return key
|
|
||||||
return None
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
_prop_mode: Optional[MIoTSpecProperty]
|
_prop_mode: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
_brightness_scale: Optional[tuple[int, int]]
|
_brightness_scale: Optional[tuple[int, int]]
|
||||||
_mode_list: Optional[dict[Any, Any]]
|
_mode_map: Optional[dict[Any, Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
@ -122,7 +122,7 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
self._prop_color = None
|
self._prop_color = None
|
||||||
self._prop_mode = None
|
self._prop_mode = None
|
||||||
self._brightness_scale = None
|
self._brightness_scale = None
|
||||||
self._mode_list = None
|
self._mode_map = None
|
||||||
|
|
||||||
# properties
|
# properties
|
||||||
for prop in entity_data.props:
|
for prop in entity_data.props:
|
||||||
@ -136,15 +136,12 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
prop.value_range.min_, prop.value_range.max_)
|
prop.value_range.min_, prop.value_range.max_)
|
||||||
self._prop_brightness = prop
|
self._prop_brightness = prop
|
||||||
elif (
|
elif (
|
||||||
self._mode_list is None
|
self._mode_map is None
|
||||||
and isinstance(prop.value_list, list)
|
|
||||||
and prop.value_list
|
and prop.value_list
|
||||||
):
|
):
|
||||||
# For value-list brightness
|
# For value-list brightness
|
||||||
self._mode_list = {
|
self._mode_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
self._attr_effect_list = list(self._mode_map.values())
|
||||||
for item in prop.value_list}
|
|
||||||
self._attr_effect_list = list(self._mode_list.values())
|
|
||||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||||
self._prop_mode = prop
|
self._prop_mode = prop
|
||||||
else:
|
else:
|
||||||
@ -171,13 +168,8 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
# mode
|
# mode
|
||||||
if prop.name == 'mode':
|
if prop.name == 'mode':
|
||||||
mode_list = None
|
mode_list = None
|
||||||
if (
|
if prop.value_list:
|
||||||
isinstance(prop.value_list, list)
|
mode_list = prop.value_list.to_map()
|
||||||
and prop.value_list
|
|
||||||
):
|
|
||||||
mode_list = {
|
|
||||||
item['value']: item['description']
|
|
||||||
for item in prop.value_list}
|
|
||||||
elif prop.value_range:
|
elif prop.value_range:
|
||||||
mode_list = {}
|
mode_list = {}
|
||||||
if (
|
if (
|
||||||
@ -197,8 +189,8 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
prop.value_range.step):
|
prop.value_range.step):
|
||||||
mode_list[value] = f'mode {value}'
|
mode_list[value] = f'mode {value}'
|
||||||
if mode_list:
|
if mode_list:
|
||||||
self._mode_list = mode_list
|
self._mode_map = mode_list
|
||||||
self._attr_effect_list = list(self._mode_list.values())
|
self._attr_effect_list = list(self._mode_map.values())
|
||||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||||
self._prop_mode = prop
|
self._prop_mode = prop
|
||||||
else:
|
else:
|
||||||
@ -213,21 +205,6 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
self._attr_supported_color_modes.add(ColorMode.ONOFF)
|
self._attr_supported_color_modes.add(ColorMode.ONOFF)
|
||||||
self._attr_color_mode = ColorMode.ONOFF
|
self._attr_color_mode = ColorMode.ONOFF
|
||||||
|
|
||||||
def __get_mode_description(self, key: int) -> Optional[str]:
|
|
||||||
"""Convert mode value to description."""
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
return self._mode_list.get(key, None)
|
|
||||||
|
|
||||||
def __get_mode_value(self, description: str) -> Optional[int]:
|
|
||||||
"""Convert mode description to value."""
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
for key, value in self._mode_list.items():
|
|
||||||
if value == description:
|
|
||||||
return key
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> Optional[bool]:
|
def is_on(self) -> Optional[bool]:
|
||||||
"""Return if the light is on."""
|
"""Return if the light is on."""
|
||||||
@ -264,7 +241,8 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
@property
|
@property
|
||||||
def effect(self) -> Optional[str]:
|
def effect(self) -> Optional[str]:
|
||||||
"""Return the current mode."""
|
"""Return the current mode."""
|
||||||
return self.__get_mode_description(
|
return self.get_map_value(
|
||||||
|
map_=self._mode_map,
|
||||||
key=self.get_prop_value(prop=self._prop_mode))
|
key=self.get_prop_value(prop=self._prop_mode))
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs) -> None:
|
async def async_turn_on(self, **kwargs) -> None:
|
||||||
@ -303,7 +281,8 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
if ATTR_EFFECT in kwargs:
|
if ATTR_EFFECT in kwargs:
|
||||||
result = await self.set_property_async(
|
result = await self.set_property_async(
|
||||||
prop=self._prop_mode,
|
prop=self._prop_mode,
|
||||||
value=self.__get_mode_value(description=kwargs[ATTR_EFFECT]))
|
value=self.get_map_key(
|
||||||
|
map_=self._mode_map, value=kwargs[ATTR_EFFECT]))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs) -> None:
|
async def async_turn_off(self, **kwargs) -> None:
|
||||||
|
|||||||
@ -95,6 +95,7 @@ from .miot_spec import (
|
|||||||
MIoTSpecInstance,
|
MIoTSpecInstance,
|
||||||
MIoTSpecProperty,
|
MIoTSpecProperty,
|
||||||
MIoTSpecService,
|
MIoTSpecService,
|
||||||
|
MIoTSpecValueList,
|
||||||
MIoTSpecValueRange
|
MIoTSpecValueRange
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -837,18 +838,20 @@ class MIoTServiceEntity(Entity):
|
|||||||
self.miot_device.unsub_event(
|
self.miot_device.unsub_event(
|
||||||
siid=event.service.iid, eiid=event.iid)
|
siid=event.service.iid, eiid=event.iid)
|
||||||
|
|
||||||
def get_map_description(self, map_: dict[int, Any], key: int) -> Any:
|
def get_map_value(
|
||||||
|
self, map_: dict[int, Any], key: int
|
||||||
|
) -> Any:
|
||||||
if map_ is None:
|
if map_ is None:
|
||||||
return None
|
return None
|
||||||
return map_.get(key, None)
|
return map_.get(key, None)
|
||||||
|
|
||||||
def get_map_value(
|
def get_map_key(
|
||||||
self, map_: dict[int, Any], description: Any
|
self, map_: dict[int, Any], value: Any
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
if map_ is None:
|
if map_ is None:
|
||||||
return None
|
return None
|
||||||
for key, value in map_.items():
|
for key, value_ in map_.items():
|
||||||
if value == description:
|
if value_ == value:
|
||||||
return key
|
return key
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -1009,7 +1012,7 @@ class MIoTPropertyEntity(Entity):
|
|||||||
_main_loop: asyncio.AbstractEventLoop
|
_main_loop: asyncio.AbstractEventLoop
|
||||||
_value_range: Optional[MIoTSpecValueRange]
|
_value_range: Optional[MIoTSpecValueRange]
|
||||||
# {Any: Any}
|
# {Any: Any}
|
||||||
_value_list: Optional[dict[Any, Any]]
|
_value_list: Optional[MIoTSpecValueList]
|
||||||
_value: Any
|
_value: Any
|
||||||
|
|
||||||
_pending_write_ha_state_timer: Optional[asyncio.TimerHandle]
|
_pending_write_ha_state_timer: Optional[asyncio.TimerHandle]
|
||||||
@ -1022,11 +1025,7 @@ class MIoTPropertyEntity(Entity):
|
|||||||
self.service = spec.service
|
self.service = spec.service
|
||||||
self._main_loop = miot_device.miot_client.main_loop
|
self._main_loop = miot_device.miot_client.main_loop
|
||||||
self._value_range = spec.value_range
|
self._value_range = spec.value_range
|
||||||
if spec.value_list:
|
self._value_list = spec.value_list
|
||||||
self._value_list = {
|
|
||||||
item['value']: item['description'] for item in spec.value_list}
|
|
||||||
else:
|
|
||||||
self._value_list = None
|
|
||||||
self._value = None
|
self._value = None
|
||||||
self._pending_write_ha_state_timer = None
|
self._pending_write_ha_state_timer = None
|
||||||
# Gen entity_id
|
# Gen entity_id
|
||||||
@ -1077,15 +1076,12 @@ class MIoTPropertyEntity(Entity):
|
|||||||
def get_vlist_description(self, value: Any) -> Optional[str]:
|
def get_vlist_description(self, value: Any) -> Optional[str]:
|
||||||
if not self._value_list:
|
if not self._value_list:
|
||||||
return None
|
return None
|
||||||
return self._value_list.get(value, None)
|
return self._value_list.get_description_by_value(value)
|
||||||
|
|
||||||
def get_vlist_value(self, description: str) -> Any:
|
def get_vlist_value(self, description: str) -> Any:
|
||||||
if not self._value_list:
|
if not self._value_list:
|
||||||
return None
|
return None
|
||||||
for key, value in self._value_list.items():
|
return self._value_list.get_value_by_description(description)
|
||||||
if value == description:
|
|
||||||
return key
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def set_property_async(self, value: Any) -> bool:
|
async def set_property_async(self, value: Any) -> bool:
|
||||||
if not self.spec.writable:
|
if not self.spec.writable:
|
||||||
|
|||||||
@ -50,6 +50,7 @@ import platform
|
|||||||
import time
|
import time
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
import logging
|
import logging
|
||||||
|
from slugify import slugify
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=relative-beyond-top-level
|
# pylint: disable=relative-beyond-top-level
|
||||||
@ -75,6 +76,8 @@ class MIoTSpecValueRange:
|
|||||||
self.load(value_range)
|
self.load(value_range)
|
||||||
elif isinstance(value_range, list):
|
elif isinstance(value_range, list):
|
||||||
self.from_spec(value_range)
|
self.from_spec(value_range)
|
||||||
|
else:
|
||||||
|
raise MIoTSpecError('invalid value range format')
|
||||||
|
|
||||||
def load(self, value_range: dict) -> None:
|
def load(self, value_range: dict) -> None:
|
||||||
if (
|
if (
|
||||||
@ -105,15 +108,42 @@ class MIoTSpecValueRange:
|
|||||||
return f'[{self.min_}, {self.max_}, {self.step}'
|
return f'[{self.min_}, {self.max_}, {self.step}'
|
||||||
|
|
||||||
|
|
||||||
class _MIoTSpecValueListItem:
|
class MIoTSpecValueListItem:
|
||||||
"""MIoT SPEC value list item class."""
|
"""MIoT SPEC value list item class."""
|
||||||
# All lower-case SPEC description.
|
# NOTICE: bool type without name
|
||||||
name: str
|
name: str
|
||||||
# Value
|
# Value
|
||||||
value: Any
|
value: Any
|
||||||
# Descriptions after multilingual conversion.
|
# Descriptions after multilingual conversion.
|
||||||
description: str
|
description: str
|
||||||
|
|
||||||
|
def __init__(self, item: dict) -> None:
|
||||||
|
self.load(item)
|
||||||
|
|
||||||
|
def load(self, item: dict) -> None:
|
||||||
|
if 'value' not in item or 'description' not in item:
|
||||||
|
raise MIoTSpecError('invalid value list item, %s')
|
||||||
|
|
||||||
|
self.name = item.get('name', None)
|
||||||
|
self.value = item['value']
|
||||||
|
self.description = item['description']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_spec(item: dict) -> 'MIoTSpecValueListItem':
|
||||||
|
if (
|
||||||
|
'name' not in item
|
||||||
|
or 'value' not in item
|
||||||
|
or 'description' not in item
|
||||||
|
):
|
||||||
|
raise MIoTSpecError('invalid value list item, %s')
|
||||||
|
# Slugify name and convert to lower-case.
|
||||||
|
cache = {
|
||||||
|
'name': slugify(text=item['name'], separator='_').lower(),
|
||||||
|
'value': item['value'],
|
||||||
|
'description': item['description']
|
||||||
|
}
|
||||||
|
return MIoTSpecValueListItem(cache)
|
||||||
|
|
||||||
def dump(self) -> dict:
|
def dump(self) -> dict:
|
||||||
return {
|
return {
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
@ -121,14 +151,69 @@ class _MIoTSpecValueListItem:
|
|||||||
'description': self.description
|
'description': self.description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f'{self.name}: {self.value} - {self.description}'
|
||||||
|
|
||||||
class _MIoTSpecValueList:
|
|
||||||
|
class MIoTSpecValueList:
|
||||||
"""MIoT SPEC value list class."""
|
"""MIoT SPEC value list class."""
|
||||||
items: list[_MIoTSpecValueListItem]
|
items: list[MIoTSpecValueListItem]
|
||||||
|
|
||||||
|
def __init__(self, value_list: list[dict]) -> None:
|
||||||
|
if not isinstance(value_list, list):
|
||||||
|
raise MIoTSpecError('invalid value list format')
|
||||||
|
self.items = []
|
||||||
|
self.load(value_list)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def names(self) -> list[str]:
|
||||||
|
return [item.name for item in self.items]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> list[Any]:
|
||||||
|
return [item.value for item in self.items]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def descriptions(self) -> list[str]:
|
||||||
|
return [item.description for item in self.items]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_spec(value_list: list[dict]) -> 'MIoTSpecValueList':
|
||||||
|
result = MIoTSpecValueList([])
|
||||||
|
dup_desc: dict[str, int] = {}
|
||||||
|
for item in value_list:
|
||||||
|
# Handle duplicate descriptions.
|
||||||
|
count = 0
|
||||||
|
if item['description'] in dup_desc:
|
||||||
|
count = dup_desc[item['description']]
|
||||||
|
count += 1
|
||||||
|
dup_desc[item['description']] = count
|
||||||
|
if count > 1:
|
||||||
|
item['name'] = f'{item["name"]}_{count}'
|
||||||
|
item['description'] = f'{item["description"]}_{count}'
|
||||||
|
|
||||||
|
result.items.append(MIoTSpecValueListItem.from_spec(item))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def load(self, value_list: list[dict]) -> None:
|
||||||
|
for item in value_list:
|
||||||
|
self.items.append(MIoTSpecValueListItem(item))
|
||||||
|
|
||||||
def to_map(self) -> dict:
|
def to_map(self) -> dict:
|
||||||
return {item.value: item.description for item in self.items}
|
return {item.value: item.description for item in self.items}
|
||||||
|
|
||||||
|
def get_value_by_description(self, description: str) -> Any:
|
||||||
|
for item in self.items:
|
||||||
|
if item.description == description:
|
||||||
|
return item.value
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_description_by_value(self, value: Any) -> Optional[str]:
|
||||||
|
for item in self.items:
|
||||||
|
if item.value == value:
|
||||||
|
return item.description
|
||||||
|
return None
|
||||||
|
|
||||||
def dump(self) -> list:
|
def dump(self) -> list:
|
||||||
return [item.dump() for item in self.items]
|
return [item.dump() for item in self.items]
|
||||||
|
|
||||||
@ -426,8 +511,7 @@ class MIoTSpecProperty(_MIoTSpecBase):
|
|||||||
precision: int
|
precision: int
|
||||||
|
|
||||||
_value_range: Optional[MIoTSpecValueRange]
|
_value_range: Optional[MIoTSpecValueRange]
|
||||||
|
_value_list: Optional[MIoTSpecValueList]
|
||||||
value_list: Optional[list[dict]]
|
|
||||||
|
|
||||||
_access: list
|
_access: list
|
||||||
_writable: bool
|
_writable: bool
|
||||||
@ -498,6 +582,22 @@ class MIoTSpecProperty(_MIoTSpecBase):
|
|||||||
self.precision = len(str(value[2]).split(
|
self.precision = len(str(value[2]).split(
|
||||||
'.')[1].rstrip('0')) if '.' in str(value[2]) else 0
|
'.')[1].rstrip('0')) if '.' in str(value[2]) else 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value_list(self) -> Optional[MIoTSpecValueList]:
|
||||||
|
return self._value_list
|
||||||
|
|
||||||
|
@value_list.setter
|
||||||
|
def value_list(
|
||||||
|
self, value: Union[list[dict], MIoTSpecValueList, None]
|
||||||
|
) -> None:
|
||||||
|
if not value:
|
||||||
|
self._value_list = None
|
||||||
|
return
|
||||||
|
if isinstance(value, list):
|
||||||
|
self._value_list = MIoTSpecValueList(value_list=value)
|
||||||
|
elif isinstance(value, MIoTSpecValueList):
|
||||||
|
self._value_list = value
|
||||||
|
|
||||||
def value_format(self, value: Any) -> Any:
|
def value_format(self, value: Any) -> Any:
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
@ -506,7 +606,7 @@ class MIoTSpecProperty(_MIoTSpecBase):
|
|||||||
if self.format_ == 'float':
|
if self.format_ == 'float':
|
||||||
return round(value, self.precision)
|
return round(value, self.precision)
|
||||||
if self.format_ == 'bool':
|
if self.format_ == 'bool':
|
||||||
return bool(value in [True, 1, 'true', '1'])
|
return bool(value in [True, 1, 'True', 'true', '1'])
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def dump(self) -> dict:
|
def dump(self) -> dict:
|
||||||
@ -522,8 +622,8 @@ class MIoTSpecProperty(_MIoTSpecBase):
|
|||||||
'access': self._access,
|
'access': self._access,
|
||||||
'unit': self.unit,
|
'unit': self.unit,
|
||||||
'value_range': (
|
'value_range': (
|
||||||
self.value_range.dump() if self.value_range else None),
|
self._value_range.dump() if self._value_range else None),
|
||||||
'value_list': self.value_list,
|
'value_list': self._value_list.dump() if self._value_list else None,
|
||||||
'precision': self.precision
|
'precision': self.precision
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,8 +652,8 @@ class MIoTSpecEvent(_MIoTSpecBase):
|
|||||||
'description': self.description,
|
'description': self.description,
|
||||||
'description_trans': self.description_trans,
|
'description_trans': self.description_trans,
|
||||||
'proprietary': self.proprietary,
|
'proprietary': self.proprietary,
|
||||||
'need_filter': self.need_filter,
|
|
||||||
'argument': [prop.iid for prop in self.argument],
|
'argument': [prop.iid for prop in self.argument],
|
||||||
|
'need_filter': self.need_filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -583,10 +683,10 @@ class MIoTSpecAction(_MIoTSpecBase):
|
|||||||
'iid': self.iid,
|
'iid': self.iid,
|
||||||
'description': self.description,
|
'description': self.description,
|
||||||
'description_trans': self.description_trans,
|
'description_trans': self.description_trans,
|
||||||
'proprietary': self.proprietary,
|
|
||||||
'need_filter': self.need_filter,
|
|
||||||
'in': [prop.iid for prop in self.in_],
|
'in': [prop.iid for prop in self.in_],
|
||||||
'out': [prop.iid for prop in self.out]
|
'out': [prop.iid for prop in self.out],
|
||||||
|
'proprietary': self.proprietary,
|
||||||
|
'need_filter': self.need_filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -611,9 +711,9 @@ class MIoTSpecService(_MIoTSpecBase):
|
|||||||
'description_trans': self.description_trans,
|
'description_trans': self.description_trans,
|
||||||
'proprietary': self.proprietary,
|
'proprietary': self.proprietary,
|
||||||
'properties': [prop.dump() for prop in self.properties],
|
'properties': [prop.dump() for prop in self.properties],
|
||||||
'need_filter': self.need_filter,
|
|
||||||
'events': [event.dump() for event in self.events],
|
'events': [event.dump() for event in self.events],
|
||||||
'actions': [action.dump() for action in self.actions],
|
'actions': [action.dump() for action in self.actions],
|
||||||
|
'need_filter': self.need_filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -903,11 +1003,11 @@ class MIoTSpecParser:
|
|||||||
return MIoTSpecInstance.load(specs=cache_result)
|
return MIoTSpecInstance.load(specs=cache_result)
|
||||||
# Retry three times
|
# Retry three times
|
||||||
for index in range(3):
|
for index in range(3):
|
||||||
# try:
|
try:
|
||||||
return await self.__parse(urn=urn)
|
return await self.__parse(urn=urn)
|
||||||
# except Exception as err: # pylint: disable=broad-exception-caught
|
except Exception as err: # pylint: disable=broad-exception-caught
|
||||||
# _LOGGER.error(
|
_LOGGER.error(
|
||||||
# 'parse error, retry, %d, %s, %s', index, urn, err)
|
'parse error, retry, %d, %s, %s', index, urn, err)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def refresh_async(self, urn_list: list[str]) -> int:
|
async def refresh_async(self, urn_list: list[str]) -> int:
|
||||||
@ -1055,14 +1155,14 @@ class MIoTSpecParser:
|
|||||||
or self._std_lib.value_translate(
|
or self._std_lib.value_translate(
|
||||||
key=f'{type_strs[:5]}|{p_type_strs[3]}|'
|
key=f'{type_strs[:5]}|{p_type_strs[3]}|'
|
||||||
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 = v_list
|
|
||||||
elif property_['format'] == 'bool':
|
elif property_['format'] == 'bool':
|
||||||
v_tag = ':'.join(p_type_strs[:5])
|
v_tag = ':'.join(p_type_strs[:5])
|
||||||
v_descriptions: dict = (
|
v_descriptions = (
|
||||||
await self._bool_trans.translate_async(urn=v_tag))
|
await self._bool_trans.translate_async(urn=v_tag))
|
||||||
if v_descriptions:
|
if v_descriptions:
|
||||||
|
# bool without value-list.name
|
||||||
spec_prop.value_list = v_descriptions
|
spec_prop.value_list = v_descriptions
|
||||||
spec_service.properties.append(spec_prop)
|
spec_service.properties.append(spec_prop)
|
||||||
# Parse service event
|
# Parse service event
|
||||||
|
|||||||
@ -82,7 +82,8 @@ class Select(MIoTPropertyEntity, SelectEntity):
|
|||||||
def __init__(self, miot_device: MIoTDevice, spec: MIoTSpecProperty) -> None:
|
def __init__(self, miot_device: MIoTDevice, spec: MIoTSpecProperty) -> None:
|
||||||
"""Initialize the Select."""
|
"""Initialize the Select."""
|
||||||
super().__init__(miot_device=miot_device, spec=spec)
|
super().__init__(miot_device=miot_device, spec=spec)
|
||||||
self._attr_options = list(self._value_list.values())
|
if self._value_list:
|
||||||
|
self._attr_options = self._value_list.descriptions
|
||||||
|
|
||||||
async def async_select_option(self, option: str) -> None:
|
async def async_select_option(self, option: str) -> None:
|
||||||
"""Change the selected option."""
|
"""Change the selected option."""
|
||||||
|
|||||||
@ -91,7 +91,7 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
|
|||||||
self._attr_device_class = SensorDeviceClass.ENUM
|
self._attr_device_class = SensorDeviceClass.ENUM
|
||||||
self._attr_icon = 'mdi:message-text'
|
self._attr_icon = 'mdi:message-text'
|
||||||
self._attr_native_unit_of_measurement = None
|
self._attr_native_unit_of_measurement = None
|
||||||
self._attr_options = list(self._value_list.values())
|
self._attr_options = self._value_list.descriptions
|
||||||
else:
|
else:
|
||||||
self._attr_device_class = spec.device_class
|
self._attr_device_class = spec.device_class
|
||||||
if spec.external_unit:
|
if spec.external_unit:
|
||||||
@ -122,7 +122,7 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
|
|||||||
'%s, data exception, out of range, %s, %s',
|
'%s, data exception, out of range, %s, %s',
|
||||||
self.entity_id, self._value, self._value_range)
|
self.entity_id, self._value, self._value_range)
|
||||||
if self._value_list:
|
if self._value_list:
|
||||||
return self._value_list.get(self._value, None)
|
return self.get_vlist_description(self._value)
|
||||||
if isinstance(self._value, str):
|
if isinstance(self._value, str):
|
||||||
return self._value[:255]
|
return self._value[:255]
|
||||||
return self._value
|
return self._value
|
||||||
|
|||||||
@ -120,28 +120,18 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
# properties
|
# properties
|
||||||
for prop in entity_data.props:
|
for prop in entity_data.props:
|
||||||
if prop.name == 'status':
|
if prop.name == 'status':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'invalid status value_list, %s', self.entity_id)
|
'invalid status value_list, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._status_map = {
|
self._status_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
|
||||||
for item in prop.value_list}
|
|
||||||
self._prop_status = prop
|
self._prop_status = prop
|
||||||
elif prop.name == 'fan-level':
|
elif prop.name == 'fan-level':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'invalid fan-level value_list, %s', self.entity_id)
|
'invalid fan-level value_list, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._fan_level_map = {
|
self._fan_level_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
|
||||||
for item in prop.value_list}
|
|
||||||
self._attr_fan_speed_list = list(self._fan_level_map.values())
|
self._attr_fan_speed_list = list(self._fan_level_map.values())
|
||||||
self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED
|
self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED
|
||||||
self._prop_fan_level = prop
|
self._prop_fan_level = prop
|
||||||
@ -202,7 +192,7 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
@property
|
@property
|
||||||
def state(self) -> Optional[str]:
|
def state(self) -> Optional[str]:
|
||||||
"""Return the current state of the vacuum cleaner."""
|
"""Return the current state of the vacuum cleaner."""
|
||||||
return self.get_map_description(
|
return self.get_map_value(
|
||||||
map_=self._status_map,
|
map_=self._status_map,
|
||||||
key=self.get_prop_value(prop=self._prop_status))
|
key=self.get_prop_value(prop=self._prop_status))
|
||||||
|
|
||||||
@ -214,6 +204,6 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
@property
|
@property
|
||||||
def fan_speed(self) -> Optional[str]:
|
def fan_speed(self) -> Optional[str]:
|
||||||
"""Return the current fan speed of the vacuum cleaner."""
|
"""Return the current fan speed of the vacuum cleaner."""
|
||||||
return self.get_map_description(
|
return self.get_map_value(
|
||||||
map_=self._fan_level_map,
|
map_=self._fan_level_map,
|
||||||
key=self.get_prop_value(prop=self._prop_fan_level))
|
key=self.get_prop_value(prop=self._prop_fan_level))
|
||||||
|
|||||||
@ -93,7 +93,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
_prop_target_temp: Optional[MIoTSpecProperty]
|
_prop_target_temp: Optional[MIoTSpecProperty]
|
||||||
_prop_mode: Optional[MIoTSpecProperty]
|
_prop_mode: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
_mode_list: Optional[dict[Any, Any]]
|
_mode_map: Optional[dict[Any, Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
@ -106,7 +106,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
self._prop_temp = None
|
self._prop_temp = None
|
||||||
self._prop_target_temp = None
|
self._prop_target_temp = None
|
||||||
self._prop_mode = None
|
self._prop_mode = None
|
||||||
self._mode_list = None
|
self._mode_map = None
|
||||||
|
|
||||||
# properties
|
# properties
|
||||||
for prop in entity_data.props:
|
for prop in entity_data.props:
|
||||||
@ -143,17 +143,12 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
self._prop_target_temp = prop
|
self._prop_target_temp = prop
|
||||||
# mode
|
# mode
|
||||||
if prop.name == 'mode':
|
if prop.name == 'mode':
|
||||||
if (
|
if not prop.value_list:
|
||||||
not isinstance(prop.value_list, list)
|
|
||||||
or not prop.value_list
|
|
||||||
):
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'mode value_list is None, %s', self.entity_id)
|
'mode value_list is None, %s', self.entity_id)
|
||||||
continue
|
continue
|
||||||
self._mode_list = {
|
self._mode_map = prop.value_list.to_map()
|
||||||
item['value']: item['description']
|
self._attr_operation_list = list(self._mode_map.values())
|
||||||
for item in prop.value_list}
|
|
||||||
self._attr_operation_list = list(self._mode_list.values())
|
|
||||||
self._attr_supported_features |= (
|
self._attr_supported_features |= (
|
||||||
WaterHeaterEntityFeature.OPERATION_MODE)
|
WaterHeaterEntityFeature.OPERATION_MODE)
|
||||||
self._prop_mode = prop
|
self._prop_mode = prop
|
||||||
@ -189,7 +184,9 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
prop=self._prop_on, value=True, update=False)
|
prop=self._prop_on, value=True, update=False)
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
prop=self._prop_mode,
|
prop=self._prop_mode,
|
||||||
value=self.__get_mode_value(description=operation_mode))
|
value=self.get_map_key(
|
||||||
|
map_=self._mode_map,
|
||||||
|
value=operation_mode))
|
||||||
|
|
||||||
async def async_turn_away_mode_on(self) -> None:
|
async def async_turn_away_mode_on(self) -> None:
|
||||||
"""Set the water heater to away mode."""
|
"""Set the water heater to away mode."""
|
||||||
@ -212,20 +209,6 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
return STATE_OFF
|
return STATE_OFF
|
||||||
if not self._prop_mode and self.get_prop_value(prop=self._prop_on):
|
if not self._prop_mode and self.get_prop_value(prop=self._prop_on):
|
||||||
return STATE_ON
|
return STATE_ON
|
||||||
return self.__get_mode_description(
|
return self.get_map_value(
|
||||||
|
map_=self._mode_map,
|
||||||
key=self.get_prop_value(prop=self._prop_mode))
|
key=self.get_prop_value(prop=self._prop_mode))
|
||||||
|
|
||||||
def __get_mode_description(self, key: int) -> Optional[str]:
|
|
||||||
"""Convert mode value to description."""
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
return self._mode_list.get(key, None)
|
|
||||||
|
|
||||||
def __get_mode_value(self, description: str) -> Optional[int]:
|
|
||||||
"""Convert mode description to value."""
|
|
||||||
if self._mode_list is None:
|
|
||||||
return None
|
|
||||||
for key, value in self._mode_list.items():
|
|
||||||
if value == description:
|
|
||||||
return key
|
|
||||||
return None
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user