mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2026-01-18 16:10:44 +08:00
Compare commits
6 Commits
449a7f0639
...
1597329f16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1597329f16 | ||
|
|
d5c5e387c2 | ||
|
|
e6750bb746 | ||
|
|
1b87381f43 | ||
|
|
506bd9f52e | ||
|
|
7c0caa9df7 |
@ -1,4 +1,12 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
## v0.4.5
|
||||||
|
### Changed
|
||||||
|
- Ignore mdns REMOVED package. [#1296](https://github.com/XiaoMi/ha_xiaomi_home/pull/1296)
|
||||||
|
- Format value type first, then evaluate by expression, and set precision at last. [#1516](https://github.com/XiaoMi/ha_xiaomi_home/pull/1516)
|
||||||
|
### Fixed
|
||||||
|
- Fix xiaomi.derh.lite temperature precision. [#1505](https://github.com/XiaoMi/ha_xiaomi_home/pull/1505)
|
||||||
|
- Fix xiaomi.waterpuri.s1200g filter property unit, lxzn.valve.02 electricity property unit, xiaomi.aircondition.c24 power consumption device class, and cuco.plug.cp7pd power consumption and power value precision. [#1517](https://github.com/XiaoMi/ha_xiaomi_home/pull/1517)
|
||||||
|
|
||||||
## v0.4.4
|
## v0.4.4
|
||||||
### Added
|
### Added
|
||||||
- Add Turkish language support. [#1468](https://github.com/XiaoMi/ha_xiaomi_home/pull/1468)
|
- Add Turkish language support. [#1468](https://github.com/XiaoMi/ha_xiaomi_home/pull/1468)
|
||||||
|
|||||||
@ -384,6 +384,7 @@ Example:
|
|||||||
- Contribution Guidelines: [English](./CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
|
- Contribution Guidelines: [English](./CONTRIBUTING.md) | [简体中文](./doc/CONTRIBUTING_zh.md)
|
||||||
- [ChangeLog](./CHANGELOG.md)
|
- [ChangeLog](./CHANGELOG.md)
|
||||||
- Development Documents: https://developers.home-assistant.io/docs/creating_component_index
|
- Development Documents: https://developers.home-assistant.io/docs/creating_component_index
|
||||||
|
- [FAQ](https://github.com/XiaoMi/ha_xiaomi_home/wiki)
|
||||||
|
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
|
|||||||
@ -349,3 +349,101 @@ async def async_remove_config_entry_device(
|
|||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
'remove device, %s, %s', identifiers[1], device_entry.id)
|
'remove device, %s, %s', identifiers[1], device_entry.id)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||||
|
"""Migrate old entry."""
|
||||||
|
_LOGGER.debug(
|
||||||
|
'Migrating configuration from version %s.%s',
|
||||||
|
config_entry.version,
|
||||||
|
config_entry.minor_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
if config_entry.version > 1:
|
||||||
|
# This means the user has downgraded from a future version
|
||||||
|
return False
|
||||||
|
|
||||||
|
if config_entry.version == 1:
|
||||||
|
await _migrate_v1_to_v2(hass, config_entry)
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
'Migration to configuration version %s.%s successful',
|
||||||
|
config_entry.version,
|
||||||
|
config_entry.minor_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def _migrate_v1_to_v2(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||||
|
def ha_persistent_notify(
|
||||||
|
notify_id: str, title: Optional[str] = None,
|
||||||
|
message: Optional[str] = None
|
||||||
|
) -> None:
|
||||||
|
"""Send messages in Notifications dialog box."""
|
||||||
|
if title:
|
||||||
|
persistent_notification.async_create(
|
||||||
|
hass=hass, message=message or '',
|
||||||
|
title=title, notification_id=notify_id)
|
||||||
|
else:
|
||||||
|
persistent_notification.async_dismiss(
|
||||||
|
hass=hass, notification_id=notify_id)
|
||||||
|
|
||||||
|
entry_id = config_entry.entry_id
|
||||||
|
entry_data = dict(config_entry.data)
|
||||||
|
|
||||||
|
ha_persistent_notify(
|
||||||
|
notify_id=f'{entry_id}.oauth_error', title=None, message=None)
|
||||||
|
|
||||||
|
miot_client: MIoTClient = await get_miot_instance_async(
|
||||||
|
hass=hass, entry_id=entry_id,
|
||||||
|
entry_data=entry_data,
|
||||||
|
persistent_notify=ha_persistent_notify)
|
||||||
|
# Spec parser
|
||||||
|
spec_parser = MIoTSpecParser(
|
||||||
|
lang=entry_data.get(
|
||||||
|
'integration_language', DEFAULT_INTEGRATION_LANGUAGE),
|
||||||
|
storage=miot_client.miot_storage,
|
||||||
|
loop=miot_client.main_loop
|
||||||
|
)
|
||||||
|
await spec_parser.init_async()
|
||||||
|
# Manufacturer
|
||||||
|
manufacturer: DeviceManufacturer = DeviceManufacturer(
|
||||||
|
storage=miot_client.miot_storage,
|
||||||
|
loop=miot_client.main_loop)
|
||||||
|
await manufacturer.init_async()
|
||||||
|
er = entity_registry.async_get(hass)
|
||||||
|
for _, info in miot_client.device_list.items():
|
||||||
|
spec_instance = await spec_parser.parse(urn=info['urn'])
|
||||||
|
if not isinstance(spec_instance, MIoTSpecInstance):
|
||||||
|
continue
|
||||||
|
device: MIoTDevice = MIoTDevice(
|
||||||
|
miot_client=miot_client,
|
||||||
|
device_info={
|
||||||
|
**info, 'manufacturer': manufacturer.get_name(
|
||||||
|
info.get('manufacturer', ''))},
|
||||||
|
spec_instance=spec_instance)
|
||||||
|
device.spec_transform()
|
||||||
|
|
||||||
|
# Update unique_id
|
||||||
|
for platform, entities in device.entity_list.items():
|
||||||
|
for entity in entities:
|
||||||
|
if not isinstance(entity.spec, MIoTSpecService):
|
||||||
|
continue
|
||||||
|
old_unique_id = device.gen_service_entity_id_v1(
|
||||||
|
ha_domain=DOMAIN,
|
||||||
|
siid=entity.spec.iid,
|
||||||
|
)
|
||||||
|
entity_id = er.async_get_entity_id(
|
||||||
|
platform, DOMAIN, old_unique_id
|
||||||
|
)
|
||||||
|
if entity_id is None:
|
||||||
|
continue
|
||||||
|
new_unique_id = device.gen_service_entity_id(
|
||||||
|
ha_domain=DOMAIN,
|
||||||
|
siid=entity.spec.iid,
|
||||||
|
description=entity.spec.description,
|
||||||
|
)
|
||||||
|
er.async_update_entity(entity_id, new_unique_id=new_unique_id)
|
||||||
|
|
||||||
|
hass.config_entries.async_update_entry(config_entry, version=2)
|
||||||
|
|||||||
@ -108,7 +108,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Xiaomi Home config flow."""
|
"""Xiaomi Home config flow."""
|
||||||
# pylint: disable=unused-argument, inconsistent-quotes
|
# pylint: disable=unused-argument, inconsistent-quotes
|
||||||
VERSION = 1
|
VERSION = 2
|
||||||
MINOR_VERSION = 1
|
MINOR_VERSION = 1
|
||||||
DEFAULT_AREA_NAME_RULE = 'room'
|
DEFAULT_AREA_NAME_RULE = 'room'
|
||||||
_main_loop: asyncio.AbstractEventLoop
|
_main_loop: asyncio.AbstractEventLoop
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
"cryptography",
|
"cryptography",
|
||||||
"psutil"
|
"psutil"
|
||||||
],
|
],
|
||||||
"version": "v0.4.4",
|
"version": "v0.4.5",
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
"_miot-central._tcp.local."
|
"_miot-central._tcp.local."
|
||||||
]
|
]
|
||||||
|
|||||||
@ -345,6 +345,11 @@ class MIoTDevice:
|
|||||||
f'{ha_domain}.{self._model_strs[0][:9]}_{self.did_tag}_'
|
f'{ha_domain}.{self._model_strs[0][:9]}_{self.did_tag}_'
|
||||||
f'{self._model_strs[-1][:20]}')
|
f'{self._model_strs[-1][:20]}')
|
||||||
|
|
||||||
|
def gen_service_entity_id_v1(self, ha_domain: str, siid: int) -> str:
|
||||||
|
return (
|
||||||
|
f'{ha_domain}.{self._model_strs[0][:9]}_{self.did_tag}_'
|
||||||
|
f'{self._model_strs[-1][:20]}_s_{siid}')
|
||||||
|
|
||||||
def gen_service_entity_id(self, ha_domain: str, siid: int,
|
def gen_service_entity_id(self, ha_domain: str, siid: int,
|
||||||
description: str) -> str:
|
description: str) -> str:
|
||||||
return (
|
return (
|
||||||
@ -748,6 +753,7 @@ class MIoTDevice:
|
|||||||
'w': UnitOfPower.WATT,
|
'w': UnitOfPower.WATT,
|
||||||
'W': UnitOfPower.WATT,
|
'W': UnitOfPower.WATT,
|
||||||
'kW': UnitOfPower.KILO_WATT,
|
'kW': UnitOfPower.KILO_WATT,
|
||||||
|
'Wh': UnitOfEnergy.WATT_HOUR,
|
||||||
'kWh': UnitOfEnergy.KILO_WATT_HOUR,
|
'kWh': UnitOfEnergy.KILO_WATT_HOUR,
|
||||||
'A': UnitOfElectricCurrent.AMPERE,
|
'A': UnitOfElectricCurrent.AMPERE,
|
||||||
'mA': UnitOfElectricCurrent.MILLIAMPERE,
|
'mA': UnitOfElectricCurrent.MILLIAMPERE,
|
||||||
@ -1040,6 +1046,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
f'set property failed, property is None, '
|
f'set property failed, property is None, '
|
||||||
f'{self.entity_id}, {self.name}')
|
f'{self.entity_id}, {self.name}')
|
||||||
value = prop.value_format(value)
|
value = prop.value_format(value)
|
||||||
|
value = prop.value_precision(value)
|
||||||
if prop not in self.entity_data.props:
|
if prop not in self.entity_data.props:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f'set property failed, unknown property, '
|
f'set property failed, unknown property, '
|
||||||
@ -1077,9 +1084,11 @@ class MIoTServiceEntity(Entity):
|
|||||||
'get property failed, not readable, %s, %s, %s',
|
'get property failed, not readable, %s, %s, %s',
|
||||||
self.entity_id, self.name, prop.name)
|
self.entity_id, self.name, prop.name)
|
||||||
return None
|
return None
|
||||||
result = prop.value_format(
|
value: Any = prop.value_format(
|
||||||
await self.miot_device.miot_client.get_prop_async(
|
await self.miot_device.miot_client.get_prop_async(
|
||||||
did=self.miot_device.did, siid=prop.service.iid, piid=prop.iid))
|
did=self.miot_device.did, siid=prop.service.iid, piid=prop.iid))
|
||||||
|
value = prop.eval_expr(value)
|
||||||
|
result = prop.value_precision(value)
|
||||||
if result != self._prop_value_map[prop]:
|
if result != self._prop_value_map[prop]:
|
||||||
self._prop_value_map[prop] = result
|
self._prop_value_map[prop] = result
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@ -1110,7 +1119,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
continue
|
continue
|
||||||
value: Any = prop.value_format(params['value'])
|
value: Any = prop.value_format(params['value'])
|
||||||
value = prop.eval_expr(value)
|
value = prop.eval_expr(value)
|
||||||
value = prop.value_format(value)
|
value = prop.value_precision(value)
|
||||||
self._prop_value_map[prop] = value
|
self._prop_value_map[prop] = value
|
||||||
if prop in self._prop_changed_subs:
|
if prop in self._prop_changed_subs:
|
||||||
self._prop_changed_subs[prop](prop, value)
|
self._prop_changed_subs[prop](prop, value)
|
||||||
@ -1258,6 +1267,7 @@ class MIoTPropertyEntity(Entity):
|
|||||||
f'set property failed, not writable, '
|
f'set property failed, not writable, '
|
||||||
f'{self.entity_id}, {self.name}')
|
f'{self.entity_id}, {self.name}')
|
||||||
value = self.spec.value_format(value)
|
value = self.spec.value_format(value)
|
||||||
|
value = self.spec.value_precision(value)
|
||||||
try:
|
try:
|
||||||
await self.miot_device.miot_client.set_prop_async(
|
await self.miot_device.miot_client.set_prop_async(
|
||||||
did=self.miot_device.did, siid=self.spec.service.iid,
|
did=self.miot_device.did, siid=self.spec.service.iid,
|
||||||
@ -1275,16 +1285,19 @@ class MIoTPropertyEntity(Entity):
|
|||||||
'get property failed, not readable, %s, %s',
|
'get property failed, not readable, %s, %s',
|
||||||
self.entity_id, self.name)
|
self.entity_id, self.name)
|
||||||
return None
|
return None
|
||||||
return self.spec.value_format(
|
value: Any = self.spec.value_format(
|
||||||
await self.miot_device.miot_client.get_prop_async(
|
await self.miot_device.miot_client.get_prop_async(
|
||||||
did=self.miot_device.did, siid=self.spec.service.iid,
|
did=self.miot_device.did, siid=self.spec.service.iid,
|
||||||
piid=self.spec.iid))
|
piid=self.spec.iid))
|
||||||
|
value = self.spec.eval_expr(value)
|
||||||
|
result = self.spec.value_precision(value)
|
||||||
|
return result
|
||||||
|
|
||||||
def __on_value_changed(self, params: dict, ctx: Any) -> None:
|
def __on_value_changed(self, params: dict, ctx: Any) -> None:
|
||||||
_LOGGER.debug('property changed, %s', params)
|
_LOGGER.debug('property changed, %s', params)
|
||||||
value: Any = self.spec.value_format(params['value'])
|
value: Any = self.spec.value_format(params['value'])
|
||||||
value = self.spec.eval_expr(value)
|
value = self.spec.eval_expr(value)
|
||||||
self._value = self.spec.value_format(value)
|
self._value = self.spec.value_precision(value)
|
||||||
if not self._pending_write_ha_state_timer:
|
if not self._pending_write_ha_state_timer:
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|||||||
@ -599,15 +599,25 @@ class MIoTSpecProperty(_MIoTSpecBase):
|
|||||||
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
|
||||||
|
if isinstance(value, str):
|
||||||
|
if self.format_ == int:
|
||||||
|
value = int(float(value))
|
||||||
|
elif self.format_ == float:
|
||||||
|
value = float(value)
|
||||||
|
if self.format_ == bool:
|
||||||
|
return bool(value in [True, 1, 'True', 'true', '1'])
|
||||||
|
return value
|
||||||
|
|
||||||
|
def value_precision(self, value: Any) -> Any:
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
if self.format_ == float:
|
||||||
|
return round(value, self.precision)
|
||||||
if self.format_ == int:
|
if self.format_ == int:
|
||||||
if self.value_range is None:
|
if self.value_range is None:
|
||||||
return int(round(value))
|
return int(round(value))
|
||||||
return int(
|
return int(
|
||||||
round(value / self.value_range.step) * self.value_range.step)
|
round(value / self.value_range.step) * self.value_range.step)
|
||||||
if self.format_ == float:
|
|
||||||
return round(value, self.precision)
|
|
||||||
if self.format_ == bool:
|
|
||||||
return bool(value in [True, 1, 'True', 'true', '1'])
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def dump(self) -> dict:
|
def dump(self) -> dict:
|
||||||
|
|||||||
@ -5,6 +5,11 @@
|
|||||||
"service:003:property:001:valuelist:001": "Dry"
|
"service:003:property:001:valuelist:001": "Dry"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"urn:miot-spec-v2:device:electronic-valve:0000A0A7:lxzn-02": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"service:004:property:001": "功率过高-阈值设置"
|
||||||
|
}
|
||||||
|
},
|
||||||
"urn:miot-spec-v2:device:electronic-valve:0000A0A7:ykcn-cbcs": {
|
"urn:miot-spec-v2:device:electronic-valve:0000A0A7:ykcn-cbcs": {
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"service:004:property:001": "功率过高-阈值设置",
|
"service:004:property:001": "功率过高-阈值设置",
|
||||||
|
|||||||
@ -26,6 +26,7 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1:
|
|||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1
|
||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:1:
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:1:
|
||||||
prop.8.6:
|
prop.8.6:
|
||||||
|
name: power-consumption
|
||||||
unit: kWh
|
unit: kWh
|
||||||
prop.10.6:
|
prop.10.6:
|
||||||
unit: none
|
unit: none
|
||||||
@ -144,6 +145,25 @@ urn:miot-spec-v2:device:dehumidifier:0000A02D:xiaomi-lite:1:
|
|||||||
- -30
|
- -30
|
||||||
- 100
|
- 100
|
||||||
- 0.1
|
- 0.1
|
||||||
|
urn:miot-spec-v2:device:electronic-valve:0000A0A7:lxzn-02:1:0000C833:
|
||||||
|
prop.3.1:
|
||||||
|
format: float
|
||||||
|
value-range:
|
||||||
|
- 0
|
||||||
|
- 999999
|
||||||
|
- 0.01
|
||||||
|
expr: (src_value/100)
|
||||||
|
prop.3.2:
|
||||||
|
unit: mA
|
||||||
|
prop.3.3:
|
||||||
|
format: float
|
||||||
|
value-range:
|
||||||
|
- 0
|
||||||
|
- 65535
|
||||||
|
- 0.1
|
||||||
|
expr: (src_value/10)
|
||||||
|
prop.4.1:
|
||||||
|
unit: kW
|
||||||
urn:miot-spec-v2:device:electronic-valve:0000A0A7:sanmei-s1:1:
|
urn:miot-spec-v2:device:electronic-valve:0000A0A7:sanmei-s1:1:
|
||||||
prop.3.1:
|
prop.3.1:
|
||||||
format: float
|
format: float
|
||||||
@ -370,6 +390,19 @@ urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2:
|
|||||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2d:1:
|
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2d:1:
|
||||||
prop.3.2:
|
prop.3.2:
|
||||||
unit: mA
|
unit: mA
|
||||||
|
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp7pd:1:
|
||||||
|
prop.11.1:
|
||||||
|
unit: Wh
|
||||||
|
value-range:
|
||||||
|
- 0
|
||||||
|
- 65535
|
||||||
|
- 0.001
|
||||||
|
expr: (src_value*1000)
|
||||||
|
prop.11.4:
|
||||||
|
value-range:
|
||||||
|
- 0
|
||||||
|
- 10000
|
||||||
|
- 0.01
|
||||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1:
|
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1:
|
||||||
prop.11.1:
|
prop.11.1:
|
||||||
format: float
|
format: float
|
||||||
@ -490,6 +523,24 @@ urn:miot-spec-v2:device:water-purifier:0000A013:roswan-lte01:1:0000D05A:
|
|||||||
unit: ppm
|
unit: ppm
|
||||||
prop.4.2:
|
prop.4.2:
|
||||||
unit: ppm
|
unit: ppm
|
||||||
|
urn:miot-spec-v2:device:water-purifier:0000A013:xiaomi-s1200g:1:0000D05A:
|
||||||
|
prop.3.2:
|
||||||
|
unit: days
|
||||||
|
prop.3.3:
|
||||||
|
unit: days
|
||||||
|
prop.3.4:
|
||||||
|
unit: L
|
||||||
|
prop.3.5:
|
||||||
|
unit: L
|
||||||
|
prop.5.2:
|
||||||
|
unit: days
|
||||||
|
prop.5.3:
|
||||||
|
unit: days
|
||||||
|
prop.5.4:
|
||||||
|
unit: L
|
||||||
|
prop.5.5:
|
||||||
|
unit: L
|
||||||
|
urn:miot-spec-v2:device:water-purifier:0000A013:xiaomi-s1200g:2:0000D05A: urn:miot-spec-v2:device:water-purifier:0000A013:xiaomi-s1200g:1:0000D05A
|
||||||
urn:miot-spec-v2:device:water-purifier:0000A013:yunmi-s20:1:
|
urn:miot-spec-v2:device:water-purifier:0000A013:yunmi-s20:1:
|
||||||
prop.4.1:
|
prop.4.1:
|
||||||
unit: ppm
|
unit: ppm
|
||||||
|
|||||||
@ -110,7 +110,7 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
|
|||||||
self._attr_native_unit_of_measurement = list(
|
self._attr_native_unit_of_measurement = list(
|
||||||
unit_sets)[0] if unit_sets else None
|
unit_sets)[0] if unit_sets else None
|
||||||
# Set suggested precision
|
# Set suggested precision
|
||||||
if spec.format_ in {int, float} and spec.expr is None:
|
if spec.format_ == float:
|
||||||
self._attr_suggested_display_precision = spec.precision
|
self._attr_suggested_display_precision = spec.precision
|
||||||
# Set state_class
|
# Set state_class
|
||||||
if spec.state_class:
|
if spec.state_class:
|
||||||
|
|||||||
@ -386,6 +386,7 @@ siid、piid、eiid、aiid、value 均为十进制三位整数。
|
|||||||
- 贡献指南: [English](../CONTRIBUTING.md) | [简体中文](./CONTRIBUTING_zh.md)
|
- 贡献指南: [English](../CONTRIBUTING.md) | [简体中文](./CONTRIBUTING_zh.md)
|
||||||
- [更新日志](../CHANGELOG.md)
|
- [更新日志](../CHANGELOG.md)
|
||||||
- 开发文档: https://developers.home-assistant.io/docs/creating_component_index
|
- 开发文档: https://developers.home-assistant.io/docs/creating_component_index
|
||||||
|
- [常见问题](https://github.com/XiaoMi/ha_xiaomi_home/wiki)
|
||||||
|
|
||||||
## 目录结构
|
## 目录结构
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user