Compare commits

..

No commits in common. "55c204887aa783fd06decc640415c3ba593e69af" and "131563d594dd80efb7ac09e026b9fc0999258514" have entirely different histories.

11 changed files with 39 additions and 140 deletions

View File

@ -1,15 +1,5 @@
# CHANGELOG # CHANGELOG
## v0.1.5b0
### Added
- Add missing parameter state_class [#101](https://github.com/XiaoMi/ha_xiaomi_home/pull/101)
### Changed
- Make git update guide more accurate [#561](https://github.com/XiaoMi/ha_xiaomi_home/pull/561)
### Fixed
- Limit *light.mode count (value-range) [#535](https://github.com/XiaoMi/ha_xiaomi_home/pull/535)
- Update miot cloud raise error msg [#551](https://github.com/XiaoMi/ha_xiaomi_home/pull/551)
- Fix table header misplacement [#554](https://github.com/XiaoMi/ha_xiaomi_home/pull/554)
## v0.1.4 ## v0.1.4
### Added ### Added
- Refactor miot network, add network detection logic, improve devices filter logic. [458](https://github.com/XiaoMi/ha_xiaomi_home/pull/458) [#191](https://github.com/XiaoMi/ha_xiaomi_home/pull/191) - Refactor miot network, add network detection logic, improve devices filter logic. [458](https://github.com/XiaoMi/ha_xiaomi_home/pull/458) [#191](https://github.com/XiaoMi/ha_xiaomi_home/pull/191)

View File

@ -26,7 +26,6 @@ For example, update to version v1.0.0
```bash ```bash
cd config/ha_xiaomi_home cd config/ha_xiaomi_home
git fetch
git checkout v1.0.0 git checkout v1.0.0
./install.sh /config ./install.sh /config
``` ```
@ -141,7 +140,7 @@ In MIoT-Spec-V2 protocol, a product is defined as a device. A device contains se
- Property - Property
| access | format | value-list | value-range | Entity in Home Assistant | | format | access | value-list | value-range | Entity in Home Assistant |
| ------------ | --------------------- | ------------ | ----------- | ------------------------ | | ------------ | --------------------- | ------------ | ----------- | ------------------------ |
| writable | string | - | - | Text | | writable | string | - | - | Text |
| writable | bool | - | - | Switch | | writable | bool | - | - | Switch |
@ -351,7 +350,7 @@ The instance code is the code of the MIoT-Spec-V2 instance, which is in the form
``` ```
service:<siid> # service service:<siid> # service
service:<siid>:property:<piid> # property service:<siid>:property:<piid> # property
service:<siid>:property:<piid>:valuelist:<index> # The index of a value in the value-list of a property service:<siid>:property:<piid>:valuelist:<value> # the value in value-list of a property
service:<siid>:event:<eiid> # event service:<siid>:event:<eiid> # event
service:<siid>:action:<aiid> # action service:<siid>:action:<aiid> # action
``` ```

View File

@ -92,9 +92,9 @@ class Fan(MIoTServiceEntity, FanEntity):
_prop_mode: Optional[MIoTSpecProperty] _prop_mode: Optional[MIoTSpecProperty]
_prop_horizontal_swing: Optional[MIoTSpecProperty] _prop_horizontal_swing: Optional[MIoTSpecProperty]
_speed_min: int _speed_min: Optional[int]
_speed_max: int _speed_max: Optional[int]
_speed_step: int _speed_step: Optional[int]
_speed_names: Optional[list] _speed_names: Optional[list]
_speed_name_map: Optional[dict[int, str]] _speed_name_map: Optional[dict[int, str]]
_mode_list: Optional[dict[Any, Any]] _mode_list: Optional[dict[Any, Any]]
@ -131,21 +131,14 @@ class Fan(MIoTServiceEntity, FanEntity):
self._speed_min = prop.value_range['min'] self._speed_min = prop.value_range['min']
self._speed_max = prop.value_range['max'] self._speed_max = prop.value_range['max']
self._speed_step = prop.value_range['step'] self._speed_step = prop.value_range['step']
self._attr_speed_count = int(( self._attr_speed_count = (self._speed_max - self._speed_min
self._speed_max - self._speed_min)/self._speed_step)+1 )/self._speed_step+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 ( elif isinstance(prop.value_list, list) and prop.value_list:
self._prop_fan_level is None
and isinstance(prop.value_list, list)
and prop.value_list
):
# Fan level with value-list # Fan level with value-list
# Fan level with value-range is prior to fan level with self._speed_name_map = {item['value']: item['description']
# value-list when a fan has both fan level properties. for item in prop.value_list}
self._speed_name_map = {
item['value']: item['description']
for item in prop.value_list}
self._speed_names = list(self._speed_name_map.values()) self._speed_names = list(self._speed_name_map.values())
self._attr_speed_count = len(prop.value_list) self._attr_speed_count = len(prop.value_list)
self._attr_supported_features |= FanEntityFeature.SET_SPEED self._attr_supported_features |= FanEntityFeature.SET_SPEED
@ -194,15 +187,14 @@ class Fan(MIoTServiceEntity, FanEntity):
# percentage # percentage
if percentage: if percentage:
if self._speed_names: if self._speed_names:
speed = percentage_to_ordered_list_item( speed = percentage_to_ordered_list_item(self._speed_names,
self._speed_names, percentage) percentage)
speed_value = self.get_map_value( speed_value = self.get_map_value(map_=self._speed_name_map,
map_=self._speed_name_map, description=speed) description=speed)
await self.set_property_async( await self.set_property_async(prop=self._prop_fan_level,
prop=self._prop_fan_level, value=speed_value) value=speed_value)
else: else:
await self.set_property_async( await self.set_property_async(prop=self._prop_fan_level,
prop=self._prop_fan_level,
value=int(percentage_to_ranged_value( value=int(percentage_to_ranged_value(
low_high_range=(self._speed_min, self._speed_max), low_high_range=(self._speed_min, self._speed_max),
percentage=percentage))) percentage=percentage)))
@ -224,15 +216,14 @@ class Fan(MIoTServiceEntity, FanEntity):
"""Set the percentage of the fan speed.""" """Set the percentage of the fan speed."""
if percentage > 0: if percentage > 0:
if self._speed_names: if self._speed_names:
speed = percentage_to_ordered_list_item( speed = percentage_to_ordered_list_item(self._speed_names,
self._speed_names, percentage) percentage)
speed_value = self.get_map_value( speed_value = self.get_map_value(map_=self._speed_name_map,
map_=self._speed_name_map, description=speed) description=speed)
await self.set_property_async( await self.set_property_async(prop=self._prop_fan_level,
prop=self._prop_fan_level, value=speed_value) value=speed_value)
else: else:
await self.set_property_async( await self.set_property_async(prop=self._prop_fan_level,
prop=self._prop_fan_level,
value=int(percentage_to_ranged_value( value=int(percentage_to_ranged_value(
low_high_range=(self._speed_min, self._speed_max), low_high_range=(self._speed_min, self._speed_max),
percentage=percentage))) percentage=percentage)))
@ -278,8 +269,8 @@ class Fan(MIoTServiceEntity, FanEntity):
if fan_level is None: if fan_level is None:
return None return None
if self._speed_names: if self._speed_names:
return ordered_list_item_to_percentage( return ordered_list_item_to_percentage(self._speed_names,
self._speed_names, self._speed_name_map[fan_level]) self._speed_name_map[fan_level])
else: else:
return ranged_value_to_percentage( return ranged_value_to_percentage(
low_high_range=(self._speed_min, self._speed_max), low_high_range=(self._speed_min, self._speed_max),

View File

@ -95,7 +95,6 @@ async def async_setup_entry(
class Light(MIoTServiceEntity, LightEntity): class Light(MIoTServiceEntity, LightEntity):
"""Light entities for Xiaomi Home.""" """Light entities for Xiaomi Home."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
_VALUE_RANGE_MODE_COUNT_MAX = 30
_prop_on: Optional[MIoTSpecProperty] _prop_on: Optional[MIoTSpecProperty]
_prop_brightness: Optional[MIoTSpecProperty] _prop_brightness: Optional[MIoTSpecProperty]
_prop_color_temp: Optional[MIoTSpecProperty] _prop_color_temp: Optional[MIoTSpecProperty]
@ -148,13 +147,13 @@ class Light(MIoTServiceEntity, LightEntity):
self._attr_supported_features |= LightEntityFeature.EFFECT self._attr_supported_features |= LightEntityFeature.EFFECT
self._prop_mode = prop self._prop_mode = prop
else: else:
_LOGGER.info( _LOGGER.error(
'invalid brightness format, %s', self.entity_id) 'invalid brightness format, %s', self.entity_id)
continue continue
# color-temperature # color-temperature
if prop.name == 'color-temperature': if prop.name == 'color-temperature':
if not isinstance(prop.value_range, dict): if not isinstance(prop.value_range, dict):
_LOGGER.info( _LOGGER.error(
'invalid color-temperature value_range format, %s', 'invalid color-temperature value_range format, %s',
self.entity_id) self.entity_id)
continue continue
@ -180,29 +179,16 @@ class Light(MIoTServiceEntity, LightEntity):
for item in prop.value_list} for item in prop.value_list}
elif isinstance(prop.value_range, dict): elif isinstance(prop.value_range, dict):
mode_list = {} mode_list = {}
if ( for value in range(
int(( prop.value_range['min'], prop.value_range['max']):
prop.value_range['max'] mode_list[value] = f'{value}'
- prop.value_range['min']
) / prop.value_range['step'])
> self._VALUE_RANGE_MODE_COUNT_MAX
):
_LOGGER.info(
'too many mode values, %s, %s, %s',
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']):
mode_list[value] = f'mode {value}'
if mode_list: if mode_list:
self._mode_list = mode_list self._mode_list = mode_list
self._attr_effect_list = list(self._mode_list.values()) 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:
_LOGGER.info('invalid mode format, %s', self.entity_id) _LOGGER.error('invalid mode format, %s', self.entity_id)
continue continue
if not self._attr_supported_color_modes: if not self._attr_supported_color_modes:

View File

@ -25,7 +25,7 @@
"cryptography", "cryptography",
"psutil" "psutil"
], ],
"version": "v0.1.5b0", "version": "v0.1.4",
"zeroconf": [ "zeroconf": [
"_miot-central._tcp.local." "_miot-central._tcp.local."
] ]

View File

@ -166,7 +166,7 @@ class MIoTOauthClient:
key in res_obj['result'] key in res_obj['result']
for key in ['access_token', 'refresh_token', 'expires_in']) for key in ['access_token', 'refresh_token', 'expires_in'])
): ):
raise MIoTOauthError(f'invalid http response, {res_str}') raise MIoTOauthError(f'invalid http response, {http_res.text}')
return { return {
**res_obj['result'], **res_obj['result'],

View File

@ -515,15 +515,9 @@ class MIoTDevice:
prop.icon = self.icon_convert(prop.unit) prop.icon = self.icon_convert(prop.unit)
device_class = SPEC_PROP_TRANS_MAP['properties'][prop_name][ device_class = SPEC_PROP_TRANS_MAP['properties'][prop_name][
'device_class'] 'device_class']
result = {'platform': platform, 'device_class': device_class} prop.platform = device_class
# optional:
if 'optional' in SPEC_PROP_TRANS_MAP['properties'][prop_name]: return {'platform': platform, 'device_class': device_class}
optional = SPEC_PROP_TRANS_MAP['properties'][prop_name]['optional']
if 'state_class' in optional:
result['state_class'] = optional['state_class']
if not prop.unit and 'unit_of_measurement' in optional:
result['unit_of_measurement'] = optional['unit_of_measurement']
return result
def spec_transform(self) -> None: def spec_transform(self) -> None:
"""Parse service, property, event, action from device spec.""" """Parse service, property, event, action from device spec."""
@ -550,13 +544,6 @@ class MIoTDevice:
if prop_entity: if prop_entity:
prop.platform = prop_entity['platform'] prop.platform = prop_entity['platform']
prop.device_class = prop_entity['device_class'] prop.device_class = prop_entity['device_class']
if 'state_class' in prop_entity:
prop.state_class = prop_entity['state_class']
if 'unit_of_measurement' in prop_entity:
prop.external_unit = self.unit_convert(
prop_entity['unit_of_measurement'])
prop.icon = self.icon_convert(
prop_entity['unit_of_measurement'])
# general conversion # general conversion
if not prop.platform: if not prop.platform:
if prop.writable: if prop.writable:

View File

@ -79,7 +79,6 @@ class MIoTSpecBase:
# External params # External params
platform: str platform: str
device_class: Any device_class: Any
state_class: Any
icon: str icon: str
external_unit: Any external_unit: Any
@ -97,7 +96,6 @@ class MIoTSpecBase:
self.platform = None self.platform = None
self.device_class = None self.device_class = None
self.state_class = None
self.icon = None self.icon = None
self.external_unit = None self.external_unit = None

View File

@ -46,16 +46,8 @@ off Xiaomi or its affiliates' products.
Conversion rules of MIoT-Spec-V2 instance to Home Assistant entity. Conversion rules of MIoT-Spec-V2 instance to Home Assistant entity.
""" """
from homeassistant.components.sensor import SensorDeviceClass from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.components.sensor import SensorStateClass
from homeassistant.components.event import EventDeviceClass from homeassistant.components.event import EventDeviceClass
from homeassistant.const import (
UnitOfEnergy,
UnitOfPower,
UnitOfElectricCurrent,
UnitOfElectricPotential,
)
# pylint: disable=pointless-string-statement # pylint: disable=pointless-string-statement
"""SPEC_DEVICE_TRANS_MAP """SPEC_DEVICE_TRANS_MAP
{ {
@ -333,11 +325,7 @@ SPEC_SERVICE_TRANS_MAP: dict[str, dict | str] = {
'properties': { 'properties': {
'<property instance name>':{ '<property instance name>':{
'device_class': str, 'device_class': str,
'entity': str, 'entity': str
'optional':{
'state_class': str,
'unit_of_measurement': str
}
} }
} }
} }
@ -393,11 +381,7 @@ SPEC_PROP_TRANS_MAP: dict[str, dict | str] = {
}, },
'voltage': { 'voltage': {
'device_class': SensorDeviceClass.VOLTAGE, 'device_class': SensorDeviceClass.VOLTAGE,
'entity': 'sensor', 'entity': 'sensor'
'optional': {
'state_class': SensorStateClass.MEASUREMENT,
'unit_of_measurement': UnitOfElectricPotential.VOLT
}
}, },
'illumination': { 'illumination': {
'device_class': SensorDeviceClass.ILLUMINANCE, 'device_class': SensorDeviceClass.ILLUMINANCE,
@ -407,38 +391,6 @@ SPEC_PROP_TRANS_MAP: dict[str, dict | str] = {
'device_class': SensorDeviceClass.DURATION, 'device_class': SensorDeviceClass.DURATION,
'entity': 'sensor' 'entity': 'sensor'
}, },
'electric-power': {
'device_class': SensorDeviceClass.POWER,
'entity': 'sensor',
'optional': {
'state_class': SensorStateClass.MEASUREMENT,
'unit_of_measurement': UnitOfPower.WATT
}
},
'electric-current': {
'device_class': SensorDeviceClass.CURRENT,
'entity': 'sensor',
'optional': {
'state_class': SensorStateClass.MEASUREMENT,
'unit_of_measurement': UnitOfElectricCurrent.AMPERE
}
},
'power-consumption': {
'device_class': SensorDeviceClass.ENERGY,
'entity': 'sensor',
'optional': {
'state_class': SensorStateClass.TOTAL_INCREASING,
'unit_of_measurement': UnitOfEnergy.KILO_WATT_HOUR
}
},
'total-battery': {
'device_class': SensorDeviceClass.ENERGY,
'entity': 'sensor',
'optional': {
'state_class': SensorStateClass.TOTAL_INCREASING,
'unit_of_measurement': UnitOfEnergy.KILO_WATT_HOUR
}
},
'has-someone-duration': 'no-one-determine-time', 'has-someone-duration': 'no-one-determine-time',
'no-one-duration': 'no-one-determine-time' 'no-one-duration': 'no-one-determine-time'
} }

View File

@ -106,9 +106,6 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
# Set icon # Set icon
if spec.icon: if spec.icon:
self._attr_icon = spec.icon self._attr_icon = spec.icon
# Set state_class
if spec.state_class:
self._attr_state_class = spec.state_class
@property @property
def native_value(self) -> Any: def native_value(self) -> Any:

View File

@ -26,7 +26,6 @@ cd ha_xiaomi_home
```bash ```bash
cd config/ha_xiaomi_home cd config/ha_xiaomi_home
git fetch
git checkout v1.0.0 git checkout v1.0.0
./install.sh /config ./install.sh /config
``` ```