diff --git a/custom_components/xiaomi_home/climate.py b/custom_components/xiaomi_home/climate.py index bd4cfe3..1160559 100644 --- a/custom_components/xiaomi_home/climate.py +++ b/custom_components/xiaomi_home/climate.py @@ -180,26 +180,26 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity): self._attr_hvac_modes = list(self._hvac_mode_map.values()) self._prop_mode = prop elif prop.name == 'target-temperature': - if not isinstance(prop.value_range, dict): + if not prop.value_range: _LOGGER.error( 'invalid target-temperature value_range format, %s', self.entity_id) continue - self._attr_min_temp = prop.value_range['min'] - self._attr_max_temp = prop.value_range['max'] - self._attr_target_temperature_step = prop.value_range['step'] + self._attr_min_temp = prop.value_range.min_ + self._attr_max_temp = prop.value_range.max_ + self._attr_target_temperature_step = prop.value_range.step self._attr_temperature_unit = prop.external_unit self._attr_supported_features |= ( ClimateEntityFeature.TARGET_TEMPERATURE) self._prop_target_temp = prop elif prop.name == 'target-humidity': - if not isinstance(prop.value_range, dict): + if not prop.value_range: _LOGGER.error( 'invalid target-humidity value_range format, %s', self.entity_id) continue - self._attr_min_humidity = prop.value_range['min'] - self._attr_max_humidity = prop.value_range['max'] + self._attr_min_humidity = prop.value_range.min_ + self._attr_max_humidity = prop.value_range.max_ self._attr_supported_features |= ( ClimateEntityFeature.TARGET_HUMIDITY) self._prop_target_humi = prop @@ -517,14 +517,14 @@ class Heater(MIoTServiceEntity, ClimateEntity): ClimateEntityFeature.TURN_OFF) self._prop_on = prop elif prop.name == 'target-temperature': - if not isinstance(prop.value_range, dict): + if not prop.value_range: _LOGGER.error( 'invalid target-temperature value_range format, %s', self.entity_id) continue - self._attr_min_temp = prop.value_range['min'] - self._attr_max_temp = prop.value_range['max'] - self._attr_target_temperature_step = prop.value_range['step'] + self._attr_min_temp = prop.value_range.min_ + self._attr_max_temp = prop.value_range.max_ + self._attr_target_temperature_step = prop.value_range.step self._attr_temperature_unit = prop.external_unit self._attr_supported_features |= ( ClimateEntityFeature.TARGET_TEMPERATURE) diff --git a/custom_components/xiaomi_home/cover.py b/custom_components/xiaomi_home/cover.py index d8236c7..432fe8f 100644 --- a/custom_components/xiaomi_home/cover.py +++ b/custom_components/xiaomi_home/cover.py @@ -172,13 +172,13 @@ class Cover(MIoTServiceEntity, CoverEntity): elif prop.name == 'current-position': self._prop_current_position = prop elif prop.name == 'target-position': - if not isinstance(prop.value_range, dict): + if not prop.value_range: _LOGGER.error( 'invalid target-position value_range format, %s', self.entity_id) continue - self._prop_position_value_min = prop.value_range['min'] - self._prop_position_value_max = prop.value_range['max'] + self._prop_position_value_min = prop.value_range.min_ + self._prop_position_value_max = prop.value_range.max_ self._prop_position_value_range = ( self._prop_position_value_max - self._prop_position_value_min) diff --git a/custom_components/xiaomi_home/fan.py b/custom_components/xiaomi_home/fan.py index 42947ce..533a573 100644 --- a/custom_components/xiaomi_home/fan.py +++ b/custom_components/xiaomi_home/fan.py @@ -119,11 +119,11 @@ class Fan(MIoTServiceEntity, FanEntity): self._attr_supported_features |= FanEntityFeature.TURN_OFF self._prop_on = prop elif prop.name == 'fan-level': - if isinstance(prop.value_range, dict): + if prop.value_range: # Fan level with value-range - self._speed_min = prop.value_range['min'] - self._speed_max = prop.value_range['max'] - self._speed_step = prop.value_range['step'] + self._speed_min = prop.value_range.min_ + self._speed_max = prop.value_range.max_ + self._speed_step = prop.value_range.step self._attr_speed_count = self._speed_max - self._speed_min+1 self._attr_supported_features |= FanEntityFeature.SET_SPEED self._prop_fan_level = prop diff --git a/custom_components/xiaomi_home/humidifier.py b/custom_components/xiaomi_home/humidifier.py index 9739da4..9ffb56c 100644 --- a/custom_components/xiaomi_home/humidifier.py +++ b/custom_components/xiaomi_home/humidifier.py @@ -119,13 +119,13 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity): self._prop_on = prop # target-humidity elif prop.name == 'target-humidity': - if not isinstance(prop.value_range, dict): + if not prop.value_range: _LOGGER.error( 'invalid target-humidity value_range format, %s', self.entity_id) continue - self._attr_min_humidity = prop.value_range['min'] - self._attr_max_humidity = prop.value_range['max'] + self._attr_min_humidity = prop.value_range.min_ + self._attr_max_humidity = prop.value_range.max_ self._prop_target_humidity = prop # mode elif prop.name == 'mode': diff --git a/custom_components/xiaomi_home/light.py b/custom_components/xiaomi_home/light.py index 666464e..36298dd 100644 --- a/custom_components/xiaomi_home/light.py +++ b/custom_components/xiaomi_home/light.py @@ -131,9 +131,9 @@ class Light(MIoTServiceEntity, LightEntity): self._prop_on = prop # brightness if prop.name == 'brightness': - if isinstance(prop.value_range, dict): + if prop.value_range: self._brightness_scale = ( - prop.value_range['min'], prop.value_range['max']) + prop.value_range.min_, prop.value_range.max_) self._prop_brightness = prop elif ( self._mode_list is None @@ -153,13 +153,13 @@ class Light(MIoTServiceEntity, LightEntity): continue # color-temperature if prop.name == 'color-temperature': - if not isinstance(prop.value_range, dict): + if not prop.value_range: _LOGGER.info( 'invalid color-temperature value_range format, %s', self.entity_id) continue - self._attr_min_color_temp_kelvin = prop.value_range['min'] - self._attr_max_color_temp_kelvin = prop.value_range['max'] + self._attr_min_color_temp_kelvin = prop.value_range.min_ + self._attr_max_color_temp_kelvin = prop.value_range.max_ self._attr_supported_color_modes.add(ColorMode.COLOR_TEMP) self._attr_color_mode = ColorMode.COLOR_TEMP self._prop_color_temp = prop @@ -178,13 +178,13 @@ class Light(MIoTServiceEntity, LightEntity): mode_list = { item['value']: item['description'] for item in prop.value_list} - elif isinstance(prop.value_range, dict): + elif prop.value_range: mode_list = {} if ( int(( - prop.value_range['max'] - - prop.value_range['min'] - ) / prop.value_range['step']) + prop.value_range.max_ + - prop.value_range.min_ + ) / prop.value_range.step) > self._VALUE_RANGE_MODE_COUNT_MAX ): _LOGGER.info( @@ -192,9 +192,9 @@ class Light(MIoTServiceEntity, LightEntity): self.entity_id, prop.name, prop.value_range) else: for value in range( - prop.value_range['min'], - prop.value_range['max'], - prop.value_range['step']): + prop.value_range.min_, + prop.value_range.max_, + prop.value_range.step): mode_list[value] = f'mode {value}' if mode_list: self._mode_list = mode_list diff --git a/custom_components/xiaomi_home/miot/miot_device.py b/custom_components/xiaomi_home/miot/miot_device.py index 17bf9da..d074e91 100644 --- a/custom_components/xiaomi_home/miot/miot_device.py +++ b/custom_components/xiaomi_home/miot/miot_device.py @@ -94,7 +94,8 @@ from .miot_spec import ( MIoTSpecEvent, MIoTSpecInstance, MIoTSpecProperty, - MIoTSpecService + MIoTSpecService, + MIoTSpecValueRange ) _LOGGER = logging.getLogger(__name__) @@ -1006,8 +1007,7 @@ class MIoTPropertyEntity(Entity): service: MIoTSpecService _main_loop: asyncio.AbstractEventLoop - # {'min':int, 'max':int, 'step': int} - _value_range: dict[str, int] + _value_range: Optional[MIoTSpecValueRange] # {Any: Any} _value_list: Optional[dict[Any, Any]] _value: Any diff --git a/custom_components/xiaomi_home/miot/miot_spec.py b/custom_components/xiaomi_home/miot/miot_spec.py index 0729122..1593c23 100644 --- a/custom_components/xiaomi_home/miot/miot_spec.py +++ b/custom_components/xiaomi_home/miot/miot_spec.py @@ -64,13 +64,13 @@ from .miot_storage import ( _LOGGER = logging.getLogger(__name__) -class _MIoTSpecValueRange: +class MIoTSpecValueRange: """MIoT SPEC value range class.""" min_: int max_: int step: int - def __init__(self, value_range: Union[dict, list, None]) -> None: + def __init__(self, value_range: Union[dict, list]) -> None: if isinstance(value_range, dict): self.load(value_range) elif isinstance(value_range, list): @@ -101,6 +101,9 @@ class _MIoTSpecValueRange: 'step': self.step } + def __str__(self) -> str: + return f'[{self.min_}, {self.max_}, {self.step}' + class _MIoTSpecValueListItem: """MIoT SPEC value list item class.""" @@ -419,10 +422,11 @@ class _MIoTSpecBase: class MIoTSpecProperty(_MIoTSpecBase): """MIoT SPEC property class.""" format_: str - precision: int unit: Optional[str] + precision: int + + _value_range: Optional[MIoTSpecValueRange] - _value_range: Optional[_MIoTSpecValueRange] value_list: Optional[list[dict]] _access: list @@ -441,7 +445,7 @@ class MIoTSpecProperty(_MIoTSpecBase): unit: Optional[str] = None, value_range: Optional[dict] = None, value_list: Optional[list[dict]] = None, - precision: int = 0 + precision: Optional[int] = None ) -> None: super().__init__(spec=spec) self.service = service @@ -450,7 +454,7 @@ class MIoTSpecProperty(_MIoTSpecBase): self.unit = unit self.value_range = value_range self.value_list = value_list - self.precision = precision + self.precision = precision or 1 self.spec_id = hash( f'p.{self.name}.{self.service.iid}.{self.iid}') @@ -480,12 +484,19 @@ class MIoTSpecProperty(_MIoTSpecBase): return self._notifiable @property - def value_range(self) -> Optional[_MIoTSpecValueRange]: + def value_range(self) -> Optional[MIoTSpecValueRange]: return self._value_range @value_range.setter def value_range(self, value: Union[dict, list, None]) -> None: - self._value_range = _MIoTSpecValueRange(value_range=value) + """Set value-range, precision.""" + if not value: + self._value_range = None + return + self._value_range = MIoTSpecValueRange(value_range=value) + if isinstance(value, list): + self.precision = len(str(value[2]).split( + '.')[1].rstrip('0')) if '.' in str(value[2]) else 0 def value_format(self, value: Any) -> Any: if value is None: @@ -648,7 +659,7 @@ class MIoTSpecInstance: unit=prop['unit'], value_range=prop['value_range'], value_list=prop['value_list'], - precision=prop.get('precision', 0)) + precision=prop.get('precision', None)) spec_service.properties.append(spec_prop) for event in service['events']: spec_event = MIoTSpecEvent( @@ -753,9 +764,9 @@ class _MIoTSpecMultiLang: _LOGGER.info('get multi lang from local failed, %s, %s', urn, err) # Default language if not trans_cache: - if DEFAULT_INTEGRATION_LANGUAGE in trans_cloud: + if trans_cloud and DEFAULT_INTEGRATION_LANGUAGE in trans_cloud: trans_cache = trans_cloud[DEFAULT_INTEGRATION_LANGUAGE] - if DEFAULT_INTEGRATION_LANGUAGE in trans_local: + if trans_local and DEFAULT_INTEGRATION_LANGUAGE in trans_local: trans_cache.update( trans_local[DEFAULT_INTEGRATION_LANGUAGE]) trans_data: dict[str, str] = {} @@ -892,11 +903,11 @@ class MIoTSpecParser: return MIoTSpecInstance.load(specs=cache_result) # Retry three times for index in range(3): - try: - return await self.__parse(urn=urn) - except Exception as err: # pylint: disable=broad-exception-caught - _LOGGER.error( - 'parse error, retry, %d, %s, %s', index, urn, err) + # try: + return await self.__parse(urn=urn) + # except Exception as err: # pylint: disable=broad-exception-caught + # _LOGGER.error( + # 'parse error, retry, %d, %s, %s', index, urn, err) return None async def refresh_async(self, urn_list: list[str]) -> int: @@ -1032,15 +1043,7 @@ class MIoTSpecParser: or property_['description'] or spec_prop.name) if 'value-range' in property_: - spec_prop.value_range = { - 'min': property_['value-range'][0], - 'max': property_['value-range'][1], - 'step': property_['value-range'][2] - } - spec_prop.precision = len(str( - property_['value-range'][2]).split( - '.')[1].rstrip('0')) if '.' in str( - property_['value-range'][2]) else 0 + spec_prop.value_range = property_['value-range'] elif 'value-list' in property_: v_list: list[dict] = property_['value-list'] for index, v in enumerate(v_list): diff --git a/custom_components/xiaomi_home/number.py b/custom_components/xiaomi_home/number.py index 53bc09c..29bd6b7 100644 --- a/custom_components/xiaomi_home/number.py +++ b/custom_components/xiaomi_home/number.py @@ -92,9 +92,9 @@ class Number(MIoTPropertyEntity, NumberEntity): self._attr_icon = self.spec.icon # Set value range if self._value_range: - self._attr_native_min_value = self._value_range['min'] - self._attr_native_max_value = self._value_range['max'] - self._attr_native_step = self._value_range['step'] + self._attr_native_min_value = self._value_range.min_ + self._attr_native_max_value = self._value_range.max_ + self._attr_native_step = self._value_range.step @property def native_value(self) -> Optional[float]: diff --git a/custom_components/xiaomi_home/sensor.py b/custom_components/xiaomi_home/sensor.py index 39b3bdb..45472d7 100644 --- a/custom_components/xiaomi_home/sensor.py +++ b/custom_components/xiaomi_home/sensor.py @@ -115,8 +115,8 @@ class Sensor(MIoTPropertyEntity, SensorEntity): """Return the current value of the sensor.""" if self._value_range and isinstance(self._value, (int, float)): if ( - self._value < self._value_range['min'] - or self._value > self._value_range['max'] + self._value < self._value_range.min_ + or self._value > self._value_range.max_ ): _LOGGER.info( '%s, data exception, out of range, %s, %s', diff --git a/custom_components/xiaomi_home/water_heater.py b/custom_components/xiaomi_home/water_heater.py index aa7fe67..3a1a2fd 100644 --- a/custom_components/xiaomi_home/water_heater.py +++ b/custom_components/xiaomi_home/water_heater.py @@ -115,7 +115,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity): self._prop_on = prop # temperature if prop.name == 'temperature': - if isinstance(prop.value_range, dict): + if prop.value_range: if ( self._attr_temperature_unit is None and prop.external_unit @@ -128,9 +128,14 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity): self.entity_id) # target-temperature if prop.name == 'target-temperature': - self._attr_min_temp = prop.value_range['min'] - self._attr_max_temp = prop.value_range['max'] - self._attr_precision = prop.value_range['step'] + if not prop.value_range: + _LOGGER.error( + 'invalid target-temperature value_range format, %s', + self.entity_id) + continue + self._attr_min_temp = prop.value_range.min_ + self._attr_max_temp = prop.value_range.max_ + self._attr_precision = prop.value_range.step if self._attr_temperature_unit is None and prop.external_unit: self._attr_temperature_unit = prop.external_unit self._attr_supported_features |= (