mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2025-04-02 07:45:31 +08:00
Compare commits
66 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7d9250914c | ||
|
a09289ef90 | ||
|
b0428dc95a | ||
|
19ed04f2f5 | ||
|
e174a73f52 | ||
|
a1aa1c024f | ||
|
372e635681 | ||
|
3759aa9a1b | ||
|
60d054cf19 | ||
|
6680d9e8cb | ||
|
0ef8cb6370 | ||
|
8f0a69c611 | ||
|
8be0fa5d61 | ||
|
07cb4ed193 | ||
|
5c46504d0e | ||
|
97d89b3a04 | ||
|
4482d257dc | ||
|
d0387be15b | ||
|
27cf1085bd | ||
|
e69448f2eb | ||
|
7901607648 | ||
|
5adcb7ce00 | ||
|
672e5b3f5d | ||
|
417af787c4 | ||
|
6f058bf392 | ||
|
52485d8c7a | ||
|
48554ec0f7 | ||
|
0ce94f7316 | ||
|
20b0004746 | ||
|
57422ddf0d | ||
|
2e60962e94 | ||
|
52fd6371ab | ||
|
8778b00c3a | ||
|
3c16f0ffbb | ||
|
3a5b641ec7 | ||
|
8fb6f9065e | ||
|
1022838eb8 | ||
|
ef56448dbb | ||
|
bf116e13a4 | ||
|
75e44f4f93 | ||
|
2881948076 | ||
|
1cdcb785b5 | ||
|
e0eb06144f | ||
|
72d8977e6e | ||
|
3b89536bda | ||
|
045528fbf2 | ||
|
5903c9a5a8 | ||
|
9ceca34b28 | ||
|
152933a223 | ||
|
6557b22a52 | ||
|
5d4b975f85 | ||
|
0566546a99 | ||
|
c0d100ce2b | ||
|
ce7ce7af4b | ||
|
d65fe32a98 | ||
|
50be2c5df9 | ||
|
b75cafb184 | ||
|
5438698a6e | ||
|
da90e099d1 | ||
|
2703d68920 | ||
|
b6147de1b8 | ||
|
621ca8002b | ||
|
40a75bef28 | ||
|
196e19d10a | ||
|
310029d8ed | ||
|
365f4e57d8 |
26
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
26
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -1,4 +1,4 @@
|
||||
name: Bug report / 报告问题
|
||||
name: Bug Report / 报告问题
|
||||
description: Create a report to help us improve. / 报告问题以帮助我们改进
|
||||
body:
|
||||
- type: input
|
||||
@ -33,12 +33,36 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Reproduce Time / 问题复现的时间点
|
||||
description: |
|
||||
> Year-month-day, 24-hour time.
|
||||
> 年-月-日,24小时制。
|
||||
placeholder: "2025-01-01 17:00:00"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Home Assistant Logs / 系统日志
|
||||
description: |
|
||||
> Please [set the log level](https://github.com/XiaoMi/ha_xiaomi_home/blob/main/CONTRIBUTING.md#reporting-bugs) to `debug` and try to reproduce the problem.
|
||||
> [Settings > System > Logs > DOWNLOAD FULL LOG](https://my.home-assistant.io/redirect/logs) > Filter `xiaomi_home`
|
||||
> If you are concerned about privacy, you can send the log to ha_xiaomi_home@xiaomi.com . The mail body should include the link to this issue.
|
||||
> 请将[日志级别设置](https://github.com/XiaoMi/ha_xiaomi_home/blob/main/doc/CONTRIBUTING_zh.md#%E6%88%91%E5%8F%AF%E4%BB%A5%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE)为 `debug` 并尝试复现问题。
|
||||
> [设置 > 系统 > 日志 > 下载完整日志](https://my.home-assistant.io/redirect/logs) > 筛选 `xiaomi_home`
|
||||
> 如果您担心隐私问题,可将日志发送至 ha_xiaomi_home@xiaomi.com ,邮件正文附上此问题的链接。
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Log Timezone / 日志时区
|
||||
description: |
|
||||
> The [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) of the timestamp in the log.
|
||||
> 日志所用时间戳的[时区](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)。
|
||||
placeholder: "Asia/Shanghai"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
|
2
.github/workflows/test.yaml
vendored
2
.github/workflows/test.yaml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pytest pytest-asyncio pytest-dependency zeroconf paho.mqtt psutil cryptography
|
||||
pip install pytest pytest-asyncio pytest-dependency zeroconf paho.mqtt psutil cryptography slugify
|
||||
|
||||
- name: Check rule format with pytest
|
||||
run: |
|
||||
|
125
CHANGELOG.md
125
CHANGELOG.md
@ -1,4 +1,123 @@
|
||||
# CHANGELOG
|
||||
## v0.2.4
|
||||
### Added
|
||||
- Convert the submersion-state, the contact-state and the occupancy-status property to the binary_sensor entity. [#905](https://github.com/XiaoMi/ha_xiaomi_home/pull/905)
|
||||
|
||||
### Changed
|
||||
- suittc.airrtc.wk168 mode descriptions are set to strings of numbers from 1 to 16. [#921](https://github.com/XiaoMi/ha_xiaomi_home/pull/921)
|
||||
- Do not set _attr_suggested_display_precision when the spec.expr is set in spec_modify.yaml [#929](https://github.com/XiaoMi/ha_xiaomi_home/pull/929)
|
||||
- Set "unknown event msg" log to info level.
|
||||
### Fixed
|
||||
- hhcc.plantmonitor.v1 soil moisture and soil ec icon and unit. [#927](https://github.com/XiaoMi/ha_xiaomi_home/pull/27)
|
||||
- cuco.plug.cp2 voltage and power value ratio.
|
||||
- cgllc.airmonitor.s1 unit ppb.
|
||||
- roswan.waterpuri.lte01 tds unit.
|
||||
- lumi.relay.c2acn01 power consumption unit
|
||||
- xiaomi.bhf_light.s1 fan level of ventilation.
|
||||
|
||||
## v0.2.3
|
||||
### Changed
|
||||
- Specify the service name and the property name during the climate entity's on/off feature initialization. [#899](https://github.com/XiaoMi/ha_xiaomi_home/pull/899)
|
||||
- Remove the useless total-battery property from `SPEC_PROP_TRANS_MAP`.
|
||||
### Fixed
|
||||
- Fix the hvac mode setting error when changing the preset mode of the ptc-bath-heater.
|
||||
- Fix the ambiguous descriptions of yeelink.bhf_light.v10 ptc-bath-heater mode value-list.
|
||||
- Fix the power consumption value of chuangmi.plug.212a01. [#910](https://github.com/XiaoMi/ha_xiaomi_home/pull/910)
|
||||
|
||||
## v0.2.2
|
||||
This version has modified the conversion rules of the climate entity, which will have effect on the devices with the ptc-bath-heater, the air-conditioner and the air-fresh service. After updating, you need to restart Home Assistant and check `xiaomi_home > CONFIGURE >
|
||||
Update entity conversion rules > NEXT` to reload the integration.
|
||||
|
||||
这个版本修改了浴霸、空调、新风机的实体转换规则,更新之后需要重启 Home Assistant,并且勾选 `xiaomi_home > 配置 > 更新实体转换规则 > 下一步` 重新加载集成。
|
||||
### Added
|
||||
- Add conversion rules for the air-conditioner service and the air-fresh service. [#879](https://github.com/XiaoMi/ha_xiaomi_home/pull/879)
|
||||
### Changed
|
||||
- Convert the mode of the ptc bath heater to the preset mode of the climate entity. [#874](https://github.com/XiaoMi/ha_xiaomi_home/pull/874)
|
||||
- Use Home Assistant default icon when device_class is set. [#855](https://github.com/XiaoMi/ha_xiaomi_home/pull/855)
|
||||
### Fixed
|
||||
- Fix xiaomi.aircondition.m9 humidity-range unit. [#878](https://github.com/XiaoMi/ha_xiaomi_home/pull/878)
|
||||
- Fix MIoT-Spec-V2 conflicts of xiaomi.fan.p5 and mike.bhf_light.2. [#866](https://github.com/XiaoMi/ha_xiaomi_home/pull/866)
|
||||
|
||||
## v0.2.1
|
||||
### Added
|
||||
- Add the preset mode for the thermostat. [#833](https://github.com/XiaoMi/ha_xiaomi_home/pull/833)
|
||||
### Changed
|
||||
- Change paho-mqtt version to adapt Home Assistant 2025.03. [#839](https://github.com/XiaoMi/ha_xiaomi_home/pull/839)
|
||||
- Revert to use multi_lang.json. [#834](https://github.com/XiaoMi/ha_xiaomi_home/pull/834)
|
||||
### Fixed
|
||||
- Fix the opening and the closing status of linp.wopener.wd1lb. [#826](https://github.com/XiaoMi/ha_xiaomi_home/pull/826)
|
||||
- Fix the format type of the wind-reverse property. [#810](https://github.com/XiaoMi/ha_xiaomi_home/pull/810)
|
||||
- Fix the fan-level property without value-list but with value-range. [#808](https://github.com/XiaoMi/ha_xiaomi_home/pull/808)
|
||||
|
||||
## v0.2.0
|
||||
This version has modified some default units of sensors. After updating, it may cause Home Assistant to pop up some compatibility warnings. You can re-add the integration to resolve it.
|
||||
|
||||
这个版本修改了一些传感器默认单位,更新后会导致 Home Assistant 弹出一些兼容性提示,您可以重新添加集成解决。
|
||||
|
||||
### Added
|
||||
- Add prop trans rule for surge-power. [#595](https://github.com/XiaoMi/ha_xiaomi_home/pull/595)
|
||||
- Support modify spec and value conversion. [#663](https://github.com/XiaoMi/ha_xiaomi_home/pull/663)
|
||||
- Support the electric blanket. [#781](https://github.com/XiaoMi/ha_xiaomi_home/pull/781)
|
||||
- Add device with motor-control service as cover entity. [#688](https://github.com/XiaoMi/ha_xiaomi_home/pull/688)
|
||||
### Changed
|
||||
- Update README file. [#681](https://github.com/XiaoMi/ha_xiaomi_home/pull/681) [#747](https://github.com/XiaoMi/ha_xiaomi_home/pull/747)
|
||||
- Update CONTRIBUTING.md. [#681](https://github.com/XiaoMi/ha_xiaomi_home/pull/681)
|
||||
- Refactor climate.py. [#614](https://github.com/XiaoMi/ha_xiaomi_home/pull/614)
|
||||
### Fixed
|
||||
- Fix variable name or comment errors & fix test_lan error. [#678](https://github.com/XiaoMi/ha_xiaomi_home/pull/678) [#690](https://github.com/XiaoMi/ha_xiaomi_home/pull/690)
|
||||
- Fix water heater error & some type error. [#684](https://github.com/XiaoMi/ha_xiaomi_home/pull/684)
|
||||
- Fix fan level with value-list & fan reverse [#689](https://github.com/XiaoMi/ha_xiaomi_home/pull/689)
|
||||
- Fix sensor display precision [#708](https://github.com/XiaoMi/ha_xiaomi_home/pull/708)
|
||||
- Fix event:motion-detected without arguments [#712](https://github.com/XiaoMi/ha_xiaomi_home/pull/712)
|
||||
|
||||
## v0.1.5b2
|
||||
### Added
|
||||
- Support binary sensors to be displayed as text sensor entities and binary sensor entities. [#592](https://github.com/XiaoMi/ha_xiaomi_home/pull/592)
|
||||
- Add miot cloud test case. [#620](https://github.com/XiaoMi/ha_xiaomi_home/pull/620)
|
||||
- Add test case for user cert. [#638](https://github.com/XiaoMi/ha_xiaomi_home/pull/638)
|
||||
- Add mips test case & Change mips reconnect logic. [#641](https://github.com/XiaoMi/ha_xiaomi_home/pull/641)
|
||||
- Support remove device. [#622](https://github.com/XiaoMi/ha_xiaomi_home/pull/622)
|
||||
- Support italian translation. [#183](https://github.com/XiaoMi/ha_xiaomi_home/pull/183)
|
||||
### Changed
|
||||
- Refactor miot spec. [#592](https://github.com/XiaoMi/ha_xiaomi_home/pull/592)
|
||||
- Refactor miot mips & fix type errors. [#365](https://github.com/XiaoMi/ha_xiaomi_home/pull/365)
|
||||
- Using logging for test case log print. [#636](https://github.com/XiaoMi/ha_xiaomi_home/pull/636)
|
||||
- Add power properties trans. [#571](https://github.com/XiaoMi/ha_xiaomi_home/pull/571)
|
||||
- Move web page to html. [#627](https://github.com/XiaoMi/ha_xiaomi_home/pull/627)
|
||||
### Fixed
|
||||
- Fix miot cloud and mdns error. [#637](https://github.com/XiaoMi/ha_xiaomi_home/pull/637)
|
||||
- Fix type error
|
||||
|
||||
## v0.1.5b1
|
||||
This version will cause some Xiaomi routers that do not support access (#564) to become unavailable. You can update the device list in the configuration or delete it manually.
|
||||
### Added
|
||||
- Fan entity support direction ctrl [#556](https://github.com/XiaoMi/ha_xiaomi_home/pull/556)
|
||||
### Changed
|
||||
- Filter miwifi.* devices and xiaomi.router.rd03 [#564](https://github.com/XiaoMi/ha_xiaomi_home/pull/564)
|
||||
### Fixed
|
||||
- Fix multi ha instance login [#560](https://github.com/XiaoMi/ha_xiaomi_home/pull/560)
|
||||
- Fix fan speed [#464](https://github.com/XiaoMi/ha_xiaomi_home/pull/464)
|
||||
- The number of profile models updated from 660 to 823. [#583](https://github.com/XiaoMi/ha_xiaomi_home/pull/583)
|
||||
|
||||
## 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
|
||||
### 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)
|
||||
### Changed
|
||||
- Remove tev dependency for lan control & fixs. [#333](https://github.com/XiaoMi/ha_xiaomi_home/pull/333)
|
||||
- Use yaml to parse action params. [#447](https://github.com/XiaoMi/ha_xiaomi_home/pull/447)
|
||||
- Update issue template. [#445](https://github.com/XiaoMi/ha_xiaomi_home/pull/445)
|
||||
- Remove duplicate dependency(aiohttp) [#390](https://github.com/XiaoMi/ha_xiaomi_home/pull/390)
|
||||
### Fixed
|
||||
|
||||
## v0.1.4b1
|
||||
### Added
|
||||
@ -42,10 +161,10 @@
|
||||
### Changed
|
||||
### Fixed
|
||||
- Fix humidifier trans rule. https://github.com/XiaoMi/ha_xiaomi_home/issues/59
|
||||
- Fix get homeinfo error. https://github.com/XiaoMi/ha_xiaomi_home/issues/22
|
||||
- Fix get homeinfo error. https://github.com/XiaoMi/ha_xiaomi_home/issues/22
|
||||
- Fix air-conditioner switch on. https://github.com/XiaoMi/ha_xiaomi_home/issues/37 https://github.com/XiaoMi/ha_xiaomi_home/issues/16
|
||||
- Fix invalid cover status. https://github.com/XiaoMi/ha_xiaomi_home/issues/11 https://github.com/XiaoMi/ha_xiaomi_home/issues/85
|
||||
- Water heater entity add STATE_OFF. https://github.com/XiaoMi/ha_xiaomi_home/issues/105 https://github.com/XiaoMi/ha_xiaomi_home/issues/17
|
||||
- Fix invalid cover status. https://github.com/XiaoMi/ha_xiaomi_home/issues/11 https://github.com/XiaoMi/ha_xiaomi_home/issues/85
|
||||
- Water heater entity add STATE_OFF. https://github.com/XiaoMi/ha_xiaomi_home/issues/105 https://github.com/XiaoMi/ha_xiaomi_home/issues/17
|
||||
|
||||
## v0.1.0
|
||||
### Added
|
||||
|
@ -98,6 +98,9 @@ footer: Optional. The footer is the place to reference GitHub issues and PRs tha
|
||||
|
||||
When contributing to this project, you agree that your contributions will be licensed under the project's [LICENSE](../LICENSE.md).
|
||||
|
||||
|
||||
When you submit your first pull request, GitHub Action will prompt you to sign the Contributor License Agreement (CLA). Only after you sign the CLA, your pull request will be merged.
|
||||
|
||||
## How to Get Help
|
||||
|
||||
If you need help or have questions, feel free to ask in [discussions](https://github.com/XiaoMi/ha_xiaomi_home/discussions/) on GitHub.
|
||||
|
@ -26,6 +26,7 @@ For example, update to version v1.0.0
|
||||
|
||||
```bash
|
||||
cd config/ha_xiaomi_home
|
||||
git fetch
|
||||
git checkout v1.0.0
|
||||
./install.sh /config
|
||||
```
|
||||
@ -140,7 +141,7 @@ In MIoT-Spec-V2 protocol, a product is defined as a device. A device contains se
|
||||
|
||||
- Property
|
||||
|
||||
| format | access | value-list | value-range | Entity in Home Assistant |
|
||||
| access | format | value-list | value-range | Entity in Home Assistant |
|
||||
| ------------ | --------------------- | ------------ | ----------- | ------------------------ |
|
||||
| writable | string | - | - | Text |
|
||||
| writable | bool | - | - | Switch |
|
||||
@ -350,7 +351,7 @@ The instance code is the code of the MIoT-Spec-V2 instance, which is in the form
|
||||
```
|
||||
service:<siid> # service
|
||||
service:<siid>:property:<piid> # property
|
||||
service:<siid>:property:<piid>:valuelist:<value> # the value in value-list of a property
|
||||
service:<siid>:property:<piid>:valuelist:<index> # The index of a value in the value-list of a property
|
||||
service:<siid>:event:<eiid> # event
|
||||
service:<siid>:action:<aiid> # action
|
||||
```
|
||||
|
@ -54,6 +54,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.helpers import device_registry, entity_registry
|
||||
|
||||
from .miot.common import slugify_did
|
||||
from .miot.miot_storage import (
|
||||
DeviceManufacturer, MIoTStorage, MIoTCert)
|
||||
from .miot.miot_spec import (
|
||||
@ -92,7 +93,7 @@ async def async_setup_entry(
|
||||
"""Send messages in Notifications dialog box."""
|
||||
if title:
|
||||
persistent_notification.async_create(
|
||||
hass=hass, message=message,
|
||||
hass=hass, message=message or '',
|
||||
title=title, notification_id=notify_id)
|
||||
else:
|
||||
persistent_notification.async_dismiss(
|
||||
@ -125,9 +126,8 @@ async def async_setup_entry(
|
||||
miot_devices: list[MIoTDevice] = []
|
||||
er = entity_registry.async_get(hass=hass)
|
||||
for did, info in miot_client.device_list.items():
|
||||
spec_instance: MIoTSpecInstance = await spec_parser.parse(
|
||||
urn=info['urn'])
|
||||
if spec_instance is None:
|
||||
spec_instance = await spec_parser.parse(urn=info['urn'])
|
||||
if not isinstance(spec_instance, MIoTSpecInstance):
|
||||
_LOGGER.error('spec content is None, %s, %s', did, info)
|
||||
continue
|
||||
device: MIoTDevice = MIoTDevice(
|
||||
@ -155,7 +155,8 @@ async def async_setup_entry(
|
||||
for entity in filter_entities:
|
||||
device.entity_list[platform].remove(entity)
|
||||
entity_id = device.gen_service_entity_id(
|
||||
ha_domain=platform, siid=entity.spec.iid)
|
||||
ha_domain=platform,
|
||||
siid=entity.spec.iid) # type: ignore
|
||||
if er.async_get(entity_id_or_uuid=entity_id):
|
||||
er.async_remove(entity_id=entity_id)
|
||||
if platform in device.prop_list:
|
||||
@ -208,12 +209,7 @@ async def async_setup_entry(
|
||||
if er.async_get(entity_id_or_uuid=entity_id):
|
||||
er.async_remove(entity_id=entity_id)
|
||||
# Action debug
|
||||
if miot_client.action_debug:
|
||||
if 'notify' in device.action_list:
|
||||
# Add text entity for debug action
|
||||
device.action_list['action_text'] = (
|
||||
device.action_list['notify'])
|
||||
else:
|
||||
if not miot_client.action_debug:
|
||||
# Remove text entity for debug action
|
||||
for action in device.action_list.get('notify', []):
|
||||
entity_id = device.gen_action_entity_id(
|
||||
@ -221,6 +217,21 @@ async def async_setup_entry(
|
||||
siid=action.service.iid, aiid=action.iid)
|
||||
if er.async_get(entity_id_or_uuid=entity_id):
|
||||
er.async_remove(entity_id=entity_id)
|
||||
# Binary sensor display
|
||||
if not miot_client.display_binary_bool:
|
||||
for prop in device.prop_list.get('binary_sensor', []):
|
||||
entity_id = device.gen_prop_entity_id(
|
||||
ha_domain='binary_sensor', spec_name=prop.name,
|
||||
siid=prop.service.iid, piid=prop.iid)
|
||||
if er.async_get(entity_id_or_uuid=entity_id):
|
||||
er.async_remove(entity_id=entity_id)
|
||||
if not miot_client.display_binary_text:
|
||||
for prop in device.prop_list.get('binary_sensor', []):
|
||||
entity_id = device.gen_prop_entity_id(
|
||||
ha_domain='sensor', spec_name=prop.name,
|
||||
siid=prop.service.iid, piid=prop.iid)
|
||||
if er.async_get(entity_id_or_uuid=entity_id):
|
||||
er.async_remove(entity_id=entity_id)
|
||||
|
||||
hass.data[DOMAIN]['devices'][config_entry.entry_id] = miot_devices
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
@ -237,7 +248,7 @@ async def async_setup_entry(
|
||||
device_entry = dr.async_get_device(
|
||||
identifiers={(
|
||||
DOMAIN,
|
||||
MIoTDevice.gen_did_tag(
|
||||
slugify_did(
|
||||
cloud_server=config_entry.data['cloud_server'],
|
||||
did=did))},
|
||||
connections=None)
|
||||
@ -308,3 +319,32 @@ async def async_remove_entry(
|
||||
await miot_cert.remove_user_cert_async()
|
||||
await miot_cert.remove_user_key_async()
|
||||
return True
|
||||
|
||||
|
||||
async def async_remove_config_entry_device(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
device_entry: device_registry.DeviceEntry
|
||||
) -> bool:
|
||||
"""Remove the device."""
|
||||
miot_client: MIoTClient = await get_miot_instance_async(
|
||||
hass=hass, entry_id=config_entry.entry_id)
|
||||
|
||||
if len(device_entry.identifiers) != 1:
|
||||
_LOGGER.error(
|
||||
'remove device failed, invalid identifiers, %s, %s',
|
||||
device_entry.id, device_entry.identifiers)
|
||||
return False
|
||||
identifiers = list(device_entry.identifiers)[0]
|
||||
if identifiers[0] != DOMAIN:
|
||||
_LOGGER.error(
|
||||
'remove device failed, invalid domain, %s, %s',
|
||||
device_entry.id, device_entry.identifiers)
|
||||
return False
|
||||
|
||||
# Remove device
|
||||
await miot_client.remove_device2_async(did_tag=identifiers[1])
|
||||
device_registry.async_get(hass).async_remove_device(device_entry.id)
|
||||
_LOGGER.info(
|
||||
'remove device, %s, %s', identifiers[1], device_entry.id)
|
||||
return True
|
||||
|
@ -68,9 +68,10 @@ async def async_setup_entry(
|
||||
|
||||
new_entities = []
|
||||
for miot_device in device_list:
|
||||
for prop in miot_device.prop_list.get('binary_sensor', []):
|
||||
new_entities.append(BinarySensor(
|
||||
miot_device=miot_device, spec=prop))
|
||||
if miot_device.miot_client.display_binary_bool:
|
||||
for prop in miot_device.prop_list.get('binary_sensor', []):
|
||||
new_entities.append(BinarySensor(
|
||||
miot_device=miot_device, spec=prop))
|
||||
|
||||
if new_entities:
|
||||
async_add_entities(new_entities)
|
||||
@ -88,4 +89,8 @@ class BinarySensor(MIoTPropertyEntity, BinarySensorEntity):
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""On/Off state. True if the binary sensor is on, False otherwise."""
|
||||
if self.spec.name == 'contact-state':
|
||||
return self._value is False
|
||||
elif self.spec.name == 'occupancy-status':
|
||||
return bool(self._value)
|
||||
return self._value is True
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -47,30 +47,24 @@ Cover entities for Xiaomi Home.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
CoverDeviceClass
|
||||
)
|
||||
from homeassistant.components.cover import (ATTR_POSITION, CoverEntity,
|
||||
CoverEntityFeature,
|
||||
CoverDeviceClass)
|
||||
|
||||
from .miot.miot_spec import MIoTSpecProperty
|
||||
from .miot.miot_device import MIoTDevice, MIoTEntityData, MIoTServiceEntity
|
||||
from .miot.miot_device import MIoTDevice, MIoTEntityData, MIoTServiceEntity
|
||||
from .miot.const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback) -> None:
|
||||
"""Set up a config entry."""
|
||||
device_list: list[MIoTDevice] = hass.data[DOMAIN]['devices'][
|
||||
config_entry.entry_id]
|
||||
@ -82,8 +76,12 @@ async def async_setup_entry(
|
||||
data.spec.device_class = CoverDeviceClass.CURTAIN
|
||||
elif data.spec.name == 'window-opener':
|
||||
data.spec.device_class = CoverDeviceClass.WINDOW
|
||||
new_entities.append(
|
||||
Cover(miot_device=miot_device, entity_data=data))
|
||||
elif data.spec.name == 'motor-controller':
|
||||
data.spec.device_class = CoverDeviceClass.SHUTTER
|
||||
elif data.spec.name == 'airer':
|
||||
data.spec.device_class = CoverDeviceClass.BLIND
|
||||
new_entities.append(Cover(miot_device=miot_device,
|
||||
entity_data=data))
|
||||
|
||||
if new_entities:
|
||||
async_add_entities(new_entities)
|
||||
@ -97,18 +95,20 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
||||
_prop_motor_value_close: Optional[int]
|
||||
_prop_motor_value_pause: Optional[int]
|
||||
_prop_status: Optional[MIoTSpecProperty]
|
||||
_prop_status_opening: Optional[int]
|
||||
_prop_status_closing: Optional[int]
|
||||
_prop_status_stop: Optional[int]
|
||||
_prop_status_opening: Optional[list[int]]
|
||||
_prop_status_closing: Optional[list[int]]
|
||||
_prop_status_stop: Optional[list[int]]
|
||||
_prop_status_closed: Optional[list[int]]
|
||||
_prop_current_position: Optional[MIoTSpecProperty]
|
||||
_prop_target_position: Optional[MIoTSpecProperty]
|
||||
_prop_position_value_min: Optional[int]
|
||||
_prop_position_value_max: Optional[int]
|
||||
_prop_position_value_range: Optional[int]
|
||||
_prop_pos_closing: bool
|
||||
_prop_pos_opening: bool
|
||||
|
||||
def __init__(
|
||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||
) -> None:
|
||||
def __init__(self, miot_device: MIoTDevice,
|
||||
entity_data: MIoTEntityData) -> None:
|
||||
"""Initialize the Cover."""
|
||||
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||
self._attr_device_class = entity_data.spec.device_class
|
||||
@ -120,94 +120,132 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
||||
self._prop_motor_value_close = None
|
||||
self._prop_motor_value_pause = None
|
||||
self._prop_status = None
|
||||
self._prop_status_opening = None
|
||||
self._prop_status_closing = None
|
||||
self._prop_status_stop = None
|
||||
self._prop_status_opening = []
|
||||
self._prop_status_closing = []
|
||||
self._prop_status_stop = []
|
||||
self._prop_status_closed = []
|
||||
self._prop_current_position = None
|
||||
self._prop_target_position = None
|
||||
self._prop_position_value_min = None
|
||||
self._prop_position_value_max = None
|
||||
self._prop_position_value_range = None
|
||||
self._prop_pos_closing = False
|
||||
self._prop_pos_opening = False
|
||||
|
||||
# properties
|
||||
for prop in entity_data.props:
|
||||
if prop.name == 'motor-control':
|
||||
if (
|
||||
not isinstance(prop.value_list, list)
|
||||
or not prop.value_list
|
||||
):
|
||||
_LOGGER.error(
|
||||
'motor-control value_list is None, %s', self.entity_id)
|
||||
if not prop.value_list:
|
||||
_LOGGER.error('motor-control value_list is None, %s',
|
||||
self.entity_id)
|
||||
continue
|
||||
for item in prop.value_list:
|
||||
if item['name'].lower() in ['open']:
|
||||
for item in prop.value_list.items:
|
||||
if item.name in {'open', 'up'}:
|
||||
self._attr_supported_features |= (
|
||||
CoverEntityFeature.OPEN)
|
||||
self._prop_motor_value_open = item['value']
|
||||
elif item['name'].lower() in ['close']:
|
||||
self._prop_motor_value_open = item.value
|
||||
elif item.name in {'close', 'down'}:
|
||||
self._attr_supported_features |= (
|
||||
CoverEntityFeature.CLOSE)
|
||||
self._prop_motor_value_close = item['value']
|
||||
elif item['name'].lower() in ['pause']:
|
||||
self._prop_motor_value_close = item.value
|
||||
elif item.name in {'pause', 'stop'}:
|
||||
self._attr_supported_features |= (
|
||||
CoverEntityFeature.STOP)
|
||||
self._prop_motor_value_pause = item['value']
|
||||
self._prop_motor_value_pause = item.value
|
||||
self._prop_motor_control = prop
|
||||
elif prop.name == 'status':
|
||||
if (
|
||||
not isinstance(prop.value_list, list)
|
||||
or not prop.value_list
|
||||
):
|
||||
_LOGGER.error(
|
||||
'status value_list is None, %s', self.entity_id)
|
||||
if not prop.value_list:
|
||||
_LOGGER.error('status value_list is None, %s',
|
||||
self.entity_id)
|
||||
continue
|
||||
for item in prop.value_list:
|
||||
if item['name'].lower() in ['opening', 'open']:
|
||||
self._prop_status_opening = item['value']
|
||||
elif item['name'].lower() in ['closing', 'close']:
|
||||
self._prop_status_closing = item['value']
|
||||
elif item['name'].lower() in ['stop', 'pause']:
|
||||
self._prop_status_stop = item['value']
|
||||
for item in prop.value_list.items:
|
||||
if item.name in {'opening', 'open', 'up'}:
|
||||
self._prop_status_opening.append(item.value)
|
||||
elif item.name in {'closing', 'close', 'down'}:
|
||||
self._prop_status_closing.append(item.value)
|
||||
elif item.name in {'stop', 'stopped', 'pause'}:
|
||||
self._prop_status_stop.append(item.value)
|
||||
elif item.name in {'closed'}:
|
||||
self._prop_status_closed.append(item.value)
|
||||
self._prop_status = prop
|
||||
elif prop.name == 'current-position':
|
||||
if not prop.value_range:
|
||||
_LOGGER.error(
|
||||
'invalid current-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_range = (prop.value_range.max_ -
|
||||
prop.value_range.min_)
|
||||
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_range = (
|
||||
self._prop_position_value_max -
|
||||
self._prop_position_value_min)
|
||||
self._prop_position_value_min = prop.value_range.min_
|
||||
self._prop_position_value_max = prop.value_range.max_
|
||||
self._prop_position_value_range = (prop.value_range.max_ -
|
||||
prop.value_range.min_)
|
||||
self._attr_supported_features |= CoverEntityFeature.SET_POSITION
|
||||
self._prop_target_position = prop
|
||||
# For the device that has the current position property but no status
|
||||
# property, the current position property will be used to determine the
|
||||
# opening and the closing status.
|
||||
if (self._prop_status is None) and (self._prop_current_position
|
||||
is not None):
|
||||
self.sub_prop_changed(self._prop_current_position,
|
||||
self._position_changed_handler)
|
||||
|
||||
def _position_changed_handler(self, prop: MIoTSpecProperty,
|
||||
ctx: Any) -> None:
|
||||
self._prop_pos_closing = False
|
||||
self._prop_pos_opening = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_open_cover(self, **kwargs) -> None:
|
||||
"""Open the cover."""
|
||||
await self.set_property_async(
|
||||
self._prop_motor_control, self._prop_motor_value_open)
|
||||
current = None if (self._prop_current_position
|
||||
is None) else self.get_prop_value(
|
||||
prop=self._prop_current_position)
|
||||
if (current is not None) and (current < self._prop_position_value_max):
|
||||
self._prop_pos_opening = True
|
||||
self._prop_pos_closing = False
|
||||
await self.set_property_async(self._prop_motor_control,
|
||||
self._prop_motor_value_open)
|
||||
|
||||
async def async_close_cover(self, **kwargs) -> None:
|
||||
"""Close the cover."""
|
||||
await self.set_property_async(
|
||||
self._prop_motor_control, self._prop_motor_value_close)
|
||||
current = None if (self._prop_current_position
|
||||
is None) else self.get_prop_value(
|
||||
prop=self._prop_current_position)
|
||||
if (current is not None) and (current > self._prop_position_value_min):
|
||||
self._prop_pos_opening = False
|
||||
self._prop_pos_closing = True
|
||||
await self.set_property_async(self._prop_motor_control,
|
||||
self._prop_motor_value_close)
|
||||
|
||||
async def async_stop_cover(self, **kwargs) -> None:
|
||||
"""Stop the cover."""
|
||||
await self.set_property_async(
|
||||
self._prop_motor_control, self._prop_motor_value_pause)
|
||||
self._prop_pos_opening = False
|
||||
self._prop_pos_closing = False
|
||||
await self.set_property_async(self._prop_motor_control,
|
||||
self._prop_motor_value_pause)
|
||||
|
||||
async def async_set_cover_position(self, **kwargs) -> None:
|
||||
"""Set the position of the cover."""
|
||||
pos = kwargs.get(ATTR_POSITION, None)
|
||||
if pos is None:
|
||||
return None
|
||||
pos = round(pos*self._prop_position_value_range/100)
|
||||
return await self.set_property_async(
|
||||
prop=self._prop_target_position, value=pos)
|
||||
current = self.current_cover_position
|
||||
if current is not None:
|
||||
self._prop_pos_opening = pos > current
|
||||
self._prop_pos_closing = pos < current
|
||||
pos = round(pos * self._prop_position_value_range / 100)
|
||||
await self.set_property_async(prop=self._prop_target_position,
|
||||
value=pos)
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> Optional[int]:
|
||||
@ -215,28 +253,47 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
||||
|
||||
0: the cover is closed, 100: the cover is fully opened, None: unknown.
|
||||
"""
|
||||
if self._prop_current_position is None:
|
||||
# Assume that the current position is the same as the target
|
||||
# position when the current position is not defined in the device's
|
||||
# MIoT-Spec-V2.
|
||||
if self._prop_target_position is None:
|
||||
return None
|
||||
self._prop_pos_opening = False
|
||||
self._prop_pos_closing = False
|
||||
return self.get_prop_value(prop=self._prop_target_position)
|
||||
pos = self.get_prop_value(prop=self._prop_current_position)
|
||||
if pos is None:
|
||||
return None
|
||||
return round(pos*100/self._prop_position_value_range)
|
||||
return None if pos is None else round(pos * 100 /
|
||||
self._prop_position_value_range)
|
||||
|
||||
@property
|
||||
def is_opening(self) -> Optional[bool]:
|
||||
"""Return if the cover is opening."""
|
||||
if self._prop_status is None:
|
||||
return None
|
||||
return self.get_prop_value(
|
||||
prop=self._prop_status) == self._prop_status_opening
|
||||
if self._prop_status and self._prop_status_opening:
|
||||
return (self.get_prop_value(prop=self._prop_status)
|
||||
in self._prop_status_opening)
|
||||
# The status has higher priority when determining whether the cover
|
||||
# is opening.
|
||||
return self._prop_pos_opening
|
||||
|
||||
@property
|
||||
def is_closing(self) -> Optional[bool]:
|
||||
"""Return if the cover is closing."""
|
||||
if self._prop_status is None:
|
||||
return None
|
||||
return self.get_prop_value(
|
||||
prop=self._prop_status) == self._prop_status_closing
|
||||
if self._prop_status and self._prop_status_closing:
|
||||
return (self.get_prop_value(prop=self._prop_status)
|
||||
in self._prop_status_closing)
|
||||
# The status has higher priority when determining whether the cover
|
||||
# is closing.
|
||||
return self._prop_pos_closing
|
||||
|
||||
@property
|
||||
def is_closed(self) -> Optional[bool]:
|
||||
"""Return if the cover is closed."""
|
||||
return self.get_prop_value(prop=self._prop_current_position) == 0
|
||||
if self.current_cover_position is not None:
|
||||
return self.current_cover_position == 0
|
||||
# The current position is prior to the status when determining
|
||||
# whether the cover is closed.
|
||||
if self._prop_status and self._prop_status_closed:
|
||||
return (self.get_prop_value(prop=self._prop_status)
|
||||
in self._prop_status_closed)
|
||||
return None
|
||||
|
@ -46,6 +46,7 @@ off Xiaomi or its affiliates' products.
|
||||
Event entities for Xiaomi Home.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -57,6 +58,8 @@ from .miot.miot_spec import MIoTSpecEvent
|
||||
from .miot.miot_device import MIoTDevice, MIoTEventEntity
|
||||
from .miot.const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@ -85,6 +88,9 @@ class Event(MIoTEventEntity, EventEntity):
|
||||
# Set device_class
|
||||
self._attr_device_class = spec.device_class
|
||||
|
||||
def on_event_occurred(self, name: str, arguments: list[dict[int, Any]]):
|
||||
def on_event_occurred(
|
||||
self, name: str, arguments: dict[str, Any] | None = None
|
||||
) -> None:
|
||||
"""An event is occurred."""
|
||||
_LOGGER.debug('%s, attributes: %s', name, str(arguments))
|
||||
self._trigger_event(event_type=name, event_attributes=arguments)
|
||||
|
@ -52,10 +52,17 @@ import logging
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||
from homeassistant.components.fan import (
|
||||
FanEntity,
|
||||
FanEntityFeature,
|
||||
DIRECTION_FORWARD,
|
||||
DIRECTION_REVERSE
|
||||
)
|
||||
from homeassistant.util.percentage import (
|
||||
percentage_to_ranged_value,
|
||||
ranged_value_to_percentage
|
||||
ranged_value_to_percentage,
|
||||
ordered_list_item_to_percentage,
|
||||
percentage_to_ordered_list_item
|
||||
)
|
||||
|
||||
from .miot.miot_spec import MIoTSpecProperty
|
||||
@ -85,15 +92,20 @@ async def async_setup_entry(
|
||||
class Fan(MIoTServiceEntity, FanEntity):
|
||||
"""Fan entities for Xiaomi Home."""
|
||||
# pylint: disable=unused-argument
|
||||
_prop_on: Optional[MIoTSpecProperty]
|
||||
_prop_on: MIoTSpecProperty
|
||||
_prop_fan_level: Optional[MIoTSpecProperty]
|
||||
_prop_mode: Optional[MIoTSpecProperty]
|
||||
_prop_horizontal_swing: Optional[MIoTSpecProperty]
|
||||
_prop_wind_reverse: Optional[MIoTSpecProperty]
|
||||
_prop_wind_reverse_forward: Any
|
||||
_prop_wind_reverse_reverse: Any
|
||||
|
||||
_speed_min: Optional[int]
|
||||
_speed_max: Optional[int]
|
||||
_speed_step: Optional[int]
|
||||
_mode_list: Optional[dict[Any, Any]]
|
||||
_speed_min: int
|
||||
_speed_max: int
|
||||
_speed_step: int
|
||||
_speed_names: Optional[list]
|
||||
_speed_name_map: Optional[dict[int, str]]
|
||||
_mode_map: Optional[dict[Any, Any]]
|
||||
|
||||
def __init__(
|
||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||
@ -101,16 +113,23 @@ class Fan(MIoTServiceEntity, FanEntity):
|
||||
"""Initialize the Fan."""
|
||||
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||
self._attr_preset_modes = []
|
||||
self._attr_current_direction = None
|
||||
self._attr_supported_features = FanEntityFeature(0)
|
||||
|
||||
self._prop_on = None
|
||||
# _prop_on is required
|
||||
self._prop_fan_level = None
|
||||
self._prop_mode = None
|
||||
self._prop_horizontal_swing = None
|
||||
self._prop_wind_reverse = None
|
||||
self._prop_wind_reverse_forward = None
|
||||
self._prop_wind_reverse_reverse = None
|
||||
self._speed_min = 65535
|
||||
self._speed_max = 0
|
||||
self._speed_step = 1
|
||||
self._mode_list = None
|
||||
self._speed_names = []
|
||||
self._speed_name_map = {}
|
||||
|
||||
self._mode_map = None
|
||||
|
||||
# properties
|
||||
for prop in entity_data.props:
|
||||
@ -119,59 +138,63 @@ 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._attr_speed_count = self._speed_max - self._speed_min+1
|
||||
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 = int((
|
||||
self._speed_max - self._speed_min)/self._speed_step)+1
|
||||
self._attr_supported_features |= FanEntityFeature.SET_SPEED
|
||||
self._prop_fan_level = prop
|
||||
elif (
|
||||
self._prop_fan_level is None
|
||||
and isinstance(prop.value_list, list)
|
||||
and prop.value_list
|
||||
):
|
||||
# Fan level with value-list
|
||||
for item in prop.value_list:
|
||||
self._speed_min = min(self._speed_min, item['value'])
|
||||
self._speed_max = max(self._speed_max, item['value'])
|
||||
self._attr_speed_count = self._speed_max - self._speed_min+1
|
||||
# Fan level with value-range is prior to fan level with
|
||||
# value-list when a fan has both fan level properties.
|
||||
self._speed_name_map = prop.value_list.to_map()
|
||||
self._speed_names = list(self._speed_name_map.values())
|
||||
self._attr_speed_count = len(self._speed_names)
|
||||
self._attr_supported_features |= FanEntityFeature.SET_SPEED
|
||||
self._prop_fan_level = prop
|
||||
elif prop.name == 'mode':
|
||||
if (
|
||||
not isinstance(prop.value_list, list)
|
||||
or not prop.value_list
|
||||
):
|
||||
if not prop.value_list:
|
||||
_LOGGER.error(
|
||||
'mode value_list is None, %s', self.entity_id)
|
||||
continue
|
||||
self._mode_list = {
|
||||
item['value']: item['description']
|
||||
for item in prop.value_list}
|
||||
self._attr_preset_modes = list(self._mode_list.values())
|
||||
self._mode_map = prop.value_list.to_map()
|
||||
self._attr_preset_modes = list(self._mode_map.values())
|
||||
self._attr_supported_features |= FanEntityFeature.PRESET_MODE
|
||||
self._prop_mode = prop
|
||||
elif prop.name == 'horizontal-swing':
|
||||
self._attr_supported_features |= FanEntityFeature.OSCILLATE
|
||||
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
|
||||
elif prop.name == 'wind-reverse':
|
||||
if prop.format_ == bool:
|
||||
self._prop_wind_reverse_forward = False
|
||||
self._prop_wind_reverse_reverse = True
|
||||
elif prop.value_list:
|
||||
for item in prop.value_list.items:
|
||||
if item.name in {'foreward', 'forward'}:
|
||||
self._prop_wind_reverse_forward = item.value
|
||||
elif item.name in {'reversal', 'reverse'}:
|
||||
self._prop_wind_reverse_reverse = item.value
|
||||
if (
|
||||
self._prop_wind_reverse_forward is None
|
||||
or self._prop_wind_reverse_reverse is None
|
||||
):
|
||||
# NOTICE: Value may be 0 or False
|
||||
_LOGGER.error(
|
||||
'invalid wind-reverse, %s', self.entity_id)
|
||||
continue
|
||||
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
||||
self._prop_wind_reverse = prop
|
||||
|
||||
async def async_turn_on(
|
||||
self, percentage: int = None, preset_mode: str = None, **kwargs: Any
|
||||
self, percentage: Optional[int] = None,
|
||||
preset_mode: Optional[str] = None, **kwargs: Any
|
||||
) -> None:
|
||||
"""Turn the fan on.
|
||||
|
||||
@ -182,14 +205,25 @@ class Fan(MIoTServiceEntity, FanEntity):
|
||||
await self.set_property_async(prop=self._prop_on, value=True)
|
||||
# percentage
|
||||
if percentage:
|
||||
await self.set_property_async(
|
||||
prop=self._prop_fan_level,
|
||||
value=int(percentage*self._attr_speed_count/100))
|
||||
if self._speed_names:
|
||||
await self.set_property_async(
|
||||
prop=self._prop_fan_level,
|
||||
value=self.get_map_key(
|
||||
map_=self._speed_name_map,
|
||||
value=percentage_to_ordered_list_item(
|
||||
self._speed_names, percentage)))
|
||||
else:
|
||||
await self.set_property_async(
|
||||
prop=self._prop_fan_level,
|
||||
value=int(percentage_to_ranged_value(
|
||||
low_high_range=(self._speed_min, self._speed_max),
|
||||
percentage=percentage)))
|
||||
# preset_mode
|
||||
if preset_mode:
|
||||
await self.set_property_async(
|
||||
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:
|
||||
"""Turn the fan off."""
|
||||
@ -202,11 +236,19 @@ class Fan(MIoTServiceEntity, FanEntity):
|
||||
async def async_set_percentage(self, percentage: int) -> None:
|
||||
"""Set the percentage of the fan speed."""
|
||||
if percentage > 0:
|
||||
await self.set_property_async(
|
||||
prop=self._prop_fan_level,
|
||||
value=int(percentage_to_ranged_value(
|
||||
low_high_range=(self._speed_min, self._speed_max),
|
||||
percentage=percentage)))
|
||||
if self._speed_names:
|
||||
await self.set_property_async(
|
||||
prop=self._prop_fan_level,
|
||||
value=self.get_map_key(
|
||||
map_=self._speed_name_map,
|
||||
value=percentage_to_ordered_list_item(
|
||||
self._speed_names, percentage)))
|
||||
else:
|
||||
await self.set_property_async(
|
||||
prop=self._prop_fan_level,
|
||||
value=int(percentage_to_ranged_value(
|
||||
low_high_range=(self._speed_min, self._speed_max),
|
||||
percentage=percentage)))
|
||||
if not self.is_on:
|
||||
# If the fan is off, turn it on.
|
||||
await self.set_property_async(prop=self._prop_on, value=True)
|
||||
@ -217,10 +259,19 @@ class Fan(MIoTServiceEntity, FanEntity):
|
||||
"""Set the preset mode."""
|
||||
await self.set_property_async(
|
||||
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:
|
||||
"""Set the direction of the fan."""
|
||||
if not self._prop_wind_reverse:
|
||||
return
|
||||
await self.set_property_async(
|
||||
prop=self._prop_wind_reverse,
|
||||
value=(
|
||||
self._prop_wind_reverse_reverse
|
||||
if direction == DIRECTION_REVERSE
|
||||
else self._prop_wind_reverse_forward))
|
||||
|
||||
async def async_oscillate(self, oscillating: bool) -> None:
|
||||
"""Oscillate the fan."""
|
||||
@ -238,17 +289,33 @@ class Fan(MIoTServiceEntity, FanEntity):
|
||||
"""Return the current preset mode,
|
||||
e.g., auto, smart, eco, favorite."""
|
||||
return (
|
||||
self.__get_mode_description(
|
||||
self.get_map_value(
|
||||
map_=self._mode_map,
|
||||
key=self.get_prop_value(prop=self._prop_mode))
|
||||
if self._prop_mode else None)
|
||||
|
||||
@property
|
||||
def current_direction(self) -> Optional[str]:
|
||||
"""Return the current direction of the fan."""
|
||||
if not self._prop_wind_reverse:
|
||||
return None
|
||||
return DIRECTION_REVERSE if self.get_prop_value(
|
||||
prop=self._prop_wind_reverse
|
||||
) == self._prop_wind_reverse_reverse else DIRECTION_FORWARD
|
||||
|
||||
@property
|
||||
def percentage(self) -> Optional[int]:
|
||||
"""Return the current percentage of the fan speed."""
|
||||
fan_level = self.get_prop_value(prop=self._prop_fan_level)
|
||||
return ranged_value_to_percentage(
|
||||
low_high_range=(self._speed_min, self._speed_max),
|
||||
value=fan_level) if fan_level else None
|
||||
if fan_level is None:
|
||||
return None
|
||||
if self._speed_names and self._speed_name_map:
|
||||
return ordered_list_item_to_percentage(
|
||||
self._speed_names, self._speed_name_map[fan_level])
|
||||
else:
|
||||
return ranged_value_to_percentage(
|
||||
low_high_range=(self._speed_min, self._speed_max),
|
||||
value=fan_level)
|
||||
|
||||
@property
|
||||
def oscillating(self) -> Optional[bool]:
|
||||
@ -257,8 +324,3 @@ class Fan(MIoTServiceEntity, FanEntity):
|
||||
self.get_prop_value(
|
||||
prop=self._prop_horizontal_swing)
|
||||
if self._prop_horizontal_swing else None)
|
||||
|
||||
@property
|
||||
def percentage_step(self) -> float:
|
||||
"""Return the step of the fan speed."""
|
||||
return self._speed_step
|
||||
|
@ -97,7 +97,7 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
||||
_prop_target_humidity: Optional[MIoTSpecProperty]
|
||||
_prop_humidity: Optional[MIoTSpecProperty]
|
||||
|
||||
_mode_list: dict[Any, Any]
|
||||
_mode_map: dict[Any, Any]
|
||||
|
||||
def __init__(
|
||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||
@ -110,7 +110,7 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
||||
self._prop_mode = None
|
||||
self._prop_target_humidity = None
|
||||
self._prop_humidity = None
|
||||
self._mode_list = None
|
||||
self._mode_map = None
|
||||
|
||||
# properties
|
||||
for prop in entity_data.props:
|
||||
@ -119,28 +119,23 @@ 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':
|
||||
if (
|
||||
not isinstance(prop.value_list, list)
|
||||
or not prop.value_list
|
||||
):
|
||||
if not prop.value_list:
|
||||
_LOGGER.error(
|
||||
'mode value_list is None, %s', self.entity_id)
|
||||
continue
|
||||
self._mode_list = {
|
||||
item['value']: item['description']
|
||||
for item in prop.value_list}
|
||||
self._mode_map = prop.value_list.to_map()
|
||||
self._attr_available_modes = list(
|
||||
self._mode_list.values())
|
||||
self._mode_map.values())
|
||||
self._attr_supported_features |= HumidifierEntityFeature.MODES
|
||||
self._prop_mode = prop
|
||||
# relative-humidity
|
||||
@ -163,7 +158,8 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
||||
async def async_set_mode(self, mode: str) -> None:
|
||||
"""Set new target preset mode."""
|
||||
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
|
||||
def is_on(self) -> Optional[bool]:
|
||||
@ -183,20 +179,6 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
||||
@property
|
||||
def mode(self) -> Optional[str]:
|
||||
"""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))
|
||||
|
||||
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
|
||||
|
@ -95,6 +95,7 @@ async def async_setup_entry(
|
||||
class Light(MIoTServiceEntity, LightEntity):
|
||||
"""Light entities for Xiaomi Home."""
|
||||
# pylint: disable=unused-argument
|
||||
_VALUE_RANGE_MODE_COUNT_MAX = 30
|
||||
_prop_on: Optional[MIoTSpecProperty]
|
||||
_prop_brightness: Optional[MIoTSpecProperty]
|
||||
_prop_color_temp: Optional[MIoTSpecProperty]
|
||||
@ -102,7 +103,7 @@ class Light(MIoTServiceEntity, LightEntity):
|
||||
_prop_mode: Optional[MIoTSpecProperty]
|
||||
|
||||
_brightness_scale: Optional[tuple[int, int]]
|
||||
_mode_list: Optional[dict[Any, Any]]
|
||||
_mode_map: Optional[dict[Any, Any]]
|
||||
|
||||
def __init__(
|
||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||
@ -121,7 +122,7 @@ class Light(MIoTServiceEntity, LightEntity):
|
||||
self._prop_color = None
|
||||
self._prop_mode = None
|
||||
self._brightness_scale = None
|
||||
self._mode_list = None
|
||||
self._mode_map = None
|
||||
|
||||
# properties
|
||||
for prop in entity_data.props:
|
||||
@ -130,35 +131,32 @@ 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
|
||||
and isinstance(prop.value_list, list)
|
||||
self._mode_map is None
|
||||
and prop.value_list
|
||||
):
|
||||
# For value-list brightness
|
||||
self._mode_list = {
|
||||
item['value']: item['description']
|
||||
for item in prop.value_list}
|
||||
self._attr_effect_list = list(self._mode_list.values())
|
||||
self._mode_map = prop.value_list.to_map()
|
||||
self._attr_effect_list = list(self._mode_map.values())
|
||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||
self._prop_mode = prop
|
||||
else:
|
||||
_LOGGER.error(
|
||||
_LOGGER.info(
|
||||
'invalid brightness format, %s', self.entity_id)
|
||||
continue
|
||||
# color-temperature
|
||||
if prop.name == 'color-temperature':
|
||||
if not isinstance(prop.value_range, dict):
|
||||
_LOGGER.error(
|
||||
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
|
||||
@ -170,25 +168,33 @@ class Light(MIoTServiceEntity, LightEntity):
|
||||
# mode
|
||||
if prop.name == 'mode':
|
||||
mode_list = None
|
||||
if (
|
||||
isinstance(prop.value_list, list)
|
||||
and prop.value_list
|
||||
):
|
||||
mode_list = {
|
||||
item['value']: item['description']
|
||||
for item in prop.value_list}
|
||||
elif isinstance(prop.value_range, dict):
|
||||
if prop.value_list:
|
||||
mode_list = prop.value_list.to_map()
|
||||
elif prop.value_range:
|
||||
mode_list = {}
|
||||
for value in range(
|
||||
prop.value_range['min'], prop.value_range['max']):
|
||||
mode_list[value] = f'{value}'
|
||||
if (
|
||||
int((
|
||||
prop.value_range.max_
|
||||
- prop.value_range.min_
|
||||
) / prop.value_range.step)
|
||||
> self._VALUE_RANGE_MODE_COUNT_MAX
|
||||
):
|
||||
_LOGGER.error(
|
||||
'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:
|
||||
self._mode_list = mode_list
|
||||
self._attr_effect_list = list(self._mode_list.values())
|
||||
self._mode_map = mode_list
|
||||
self._attr_effect_list = list(self._mode_map.values())
|
||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||
self._prop_mode = prop
|
||||
else:
|
||||
_LOGGER.error('invalid mode format, %s', self.entity_id)
|
||||
_LOGGER.info('invalid mode format, %s', self.entity_id)
|
||||
continue
|
||||
|
||||
if not self._attr_supported_color_modes:
|
||||
@ -199,21 +205,6 @@ class Light(MIoTServiceEntity, LightEntity):
|
||||
self._attr_supported_color_modes.add(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
|
||||
def is_on(self) -> Optional[bool]:
|
||||
"""Return if the light is on."""
|
||||
@ -250,7 +241,8 @@ class Light(MIoTServiceEntity, LightEntity):
|
||||
@property
|
||||
def effect(self) -> Optional[str]:
|
||||
"""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))
|
||||
|
||||
async def async_turn_on(self, **kwargs) -> None:
|
||||
@ -258,23 +250,25 @@ class Light(MIoTServiceEntity, LightEntity):
|
||||
|
||||
Shall set attributes in kwargs if applicable.
|
||||
"""
|
||||
result: bool = False
|
||||
# on
|
||||
# Dirty logic for lumi.gateway.mgl03 indicator light
|
||||
value_on = True if self._prop_on.format_ == 'bool' else 1
|
||||
result = await self.set_property_async(
|
||||
prop=self._prop_on, value=value_on)
|
||||
if self._prop_on:
|
||||
value_on = True if self._prop_on.format_ == bool else 1
|
||||
await self.set_property_async(
|
||||
prop=self._prop_on, value=value_on)
|
||||
# brightness
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
brightness = brightness_to_value(
|
||||
self._brightness_scale, kwargs[ATTR_BRIGHTNESS])
|
||||
result = await self.set_property_async(
|
||||
prop=self._prop_brightness, value=brightness)
|
||||
await self.set_property_async(
|
||||
prop=self._prop_brightness, value=brightness,
|
||||
write_ha_state=False)
|
||||
# color-temperature
|
||||
if ATTR_COLOR_TEMP_KELVIN in kwargs:
|
||||
result = await self.set_property_async(
|
||||
await self.set_property_async(
|
||||
prop=self._prop_color_temp,
|
||||
value=kwargs[ATTR_COLOR_TEMP_KELVIN])
|
||||
value=kwargs[ATTR_COLOR_TEMP_KELVIN],
|
||||
write_ha_state=False)
|
||||
self._attr_color_mode = ColorMode.COLOR_TEMP
|
||||
# rgb color
|
||||
if ATTR_RGB_COLOR in kwargs:
|
||||
@ -282,18 +276,23 @@ class Light(MIoTServiceEntity, LightEntity):
|
||||
g = kwargs[ATTR_RGB_COLOR][1]
|
||||
b = kwargs[ATTR_RGB_COLOR][2]
|
||||
rgb = (r << 16) | (g << 8) | b
|
||||
result = await self.set_property_async(
|
||||
prop=self._prop_color, value=rgb)
|
||||
await self.set_property_async(
|
||||
prop=self._prop_color, value=rgb,
|
||||
write_ha_state=False)
|
||||
self._attr_color_mode = ColorMode.RGB
|
||||
# mode
|
||||
if ATTR_EFFECT in kwargs:
|
||||
result = await self.set_property_async(
|
||||
await self.set_property_async(
|
||||
prop=self._prop_mode,
|
||||
value=self.__get_mode_value(description=kwargs[ATTR_EFFECT]))
|
||||
return result
|
||||
value=self.get_map_key(
|
||||
map_=self._mode_map, value=kwargs[ATTR_EFFECT]),
|
||||
write_ha_state=False)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs) -> None:
|
||||
"""Turn the light off."""
|
||||
if not self._prop_on:
|
||||
return
|
||||
# Dirty logic for lumi.gateway.mgl03 indicator light
|
||||
value_on = False if self._prop_on.format_ == 'bool' else 0
|
||||
return await self.set_property_async(prop=self._prop_on, value=value_on)
|
||||
value_on = False if self._prop_on.format_ == bool else 0
|
||||
await self.set_property_async(prop=self._prop_on, value=value_on)
|
||||
|
@ -20,14 +20,13 @@
|
||||
],
|
||||
"requirements": [
|
||||
"construct>=2.10.56",
|
||||
"paho-mqtt<=2.0.0",
|
||||
"paho-mqtt",
|
||||
"numpy",
|
||||
"cryptography",
|
||||
"psutil",
|
||||
"aiohttp[speedups]"
|
||||
"psutil"
|
||||
],
|
||||
"version": "v0.1.4b1",
|
||||
"version": "v0.2.4",
|
||||
"zeroconf": [
|
||||
"_miot-central._tcp.local."
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -45,13 +45,17 @@ off Xiaomi or its affiliates' products.
|
||||
|
||||
Common utilities.
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
from os import path
|
||||
import random
|
||||
from typing import Any, Optional
|
||||
import hashlib
|
||||
from urllib.parse import urlencode
|
||||
from urllib.request import Request, urlopen
|
||||
from paho.mqtt.matcher import MQTTMatcher
|
||||
import yaml
|
||||
from slugify import slugify
|
||||
|
||||
MIOT_ROOT_PATH: str = path.dirname(path.abspath(__file__))
|
||||
|
||||
@ -84,6 +88,21 @@ def randomize_int(value: int, ratio: float) -> int:
|
||||
return int(value * (1 - ratio + random.random()*2*ratio))
|
||||
|
||||
|
||||
def randomize_float(value: float, ratio: float) -> float:
|
||||
"""Randomize a float value."""
|
||||
return value * (1 - ratio + random.random()*2*ratio)
|
||||
|
||||
|
||||
def slugify_name(name: str, separator: str = '_') -> str:
|
||||
"""Slugify a name."""
|
||||
return slugify(name, separator=separator)
|
||||
|
||||
|
||||
def slugify_did(cloud_server: str, did: str) -> str:
|
||||
"""Slugify a device id."""
|
||||
return slugify(f'{cloud_server}_{did}', separator='_')
|
||||
|
||||
|
||||
class MIoTMatcher(MQTTMatcher):
|
||||
"""MIoT Pub/Sub topic matcher."""
|
||||
|
||||
@ -102,3 +121,68 @@ class MIoTMatcher(MQTTMatcher):
|
||||
return self[topic]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class MIoTHttp:
|
||||
"""MIoT Common HTTP API."""
|
||||
@staticmethod
|
||||
def get(
|
||||
url: str, params: Optional[dict] = None, headers: Optional[dict] = None
|
||||
) -> Optional[str]:
|
||||
full_url = url
|
||||
if params:
|
||||
encoded_params = urlencode(params)
|
||||
full_url = f'{url}?{encoded_params}'
|
||||
request = Request(full_url, method='GET', headers=headers or {})
|
||||
content: Optional[bytes] = None
|
||||
with urlopen(request) as response:
|
||||
content = response.read()
|
||||
return str(content, 'utf-8') if content else None
|
||||
|
||||
@staticmethod
|
||||
def get_json(
|
||||
url: str, params: Optional[dict] = None, headers: Optional[dict] = None
|
||||
) -> Optional[dict]:
|
||||
response = MIoTHttp.get(url, params, headers)
|
||||
return json.loads(response) if response else None
|
||||
|
||||
@staticmethod
|
||||
def post(
|
||||
url: str, data: Optional[dict] = None, headers: Optional[dict] = None
|
||||
) -> Optional[str]:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def post_json(
|
||||
url: str, data: Optional[dict] = None, headers: Optional[dict] = None
|
||||
) -> Optional[dict]:
|
||||
response = MIoTHttp.post(url, data, headers)
|
||||
return json.loads(response) if response else None
|
||||
|
||||
@staticmethod
|
||||
async def get_async(
|
||||
url: str, params: Optional[dict] = None, headers: Optional[dict] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> Optional[str]:
|
||||
# TODO: Use aiohttp
|
||||
ev_loop = loop or asyncio.get_running_loop()
|
||||
return await ev_loop.run_in_executor(
|
||||
None, MIoTHttp.get, url, params, headers)
|
||||
|
||||
@staticmethod
|
||||
async def get_json_async(
|
||||
url: str, params: Optional[dict] = None, headers: Optional[dict] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> Optional[dict]:
|
||||
ev_loop = loop or asyncio.get_running_loop()
|
||||
return await ev_loop.run_in_executor(
|
||||
None, MIoTHttp.get_json, url, params, headers)
|
||||
|
||||
@ staticmethod
|
||||
async def post_async(
|
||||
url: str, data: Optional[dict] = None, headers: Optional[dict] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> Optional[str]:
|
||||
ev_loop = loop or asyncio.get_running_loop()
|
||||
return await ev_loop.run_in_executor(
|
||||
None, MIoTHttp.post, url, data, headers)
|
||||
|
@ -103,6 +103,7 @@ INTEGRATION_LANGUAGES = {
|
||||
'en': 'English',
|
||||
'es': 'Español',
|
||||
'fr': 'Français',
|
||||
'it': 'Italiano',
|
||||
'ja': '日本語',
|
||||
'nl': 'Nederlands',
|
||||
'pt': 'Português',
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "aktivieren",
|
||||
"disable": "deaktivieren"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Textsensor-Entität",
|
||||
"bool": "Binärsensor-Entität"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "hinzufügen",
|
||||
"del": "nicht verfügbar",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Schnittstelle nicht verfügbar"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Authentifizierung erfolgreich",
|
||||
"content": "Bitte schließen Sie diese Seite und kehren Sie zur Kontoauthentifizierungsseite zurück, um auf „Weiter“ zu klicken.",
|
||||
"button": "Schließen"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Authentifizierung fehlgeschlagen",
|
||||
"content": "{error_msg}, bitte schließen Sie diese Seite und kehren Sie zur Kontoauthentifizierungsseite zurück, um den Authentifizierungslink erneut zu klicken.",
|
||||
"button": "Schließen"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Ungültige Antwortparameter ('code' oder 'state' Feld ist leer)",
|
||||
"-10101": "Übergebenes 'state' Feld stimmt nicht überein"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Ungültige Authentifizierungsinformationen, Cloud-Verbindung nicht verfügbar, bitte betreten Sie die Xiaomi Home-Integrationsseite und klicken Sie auf 'Optionen', um die Authentifizierung erneut durchzuführen",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "Enable",
|
||||
"disable": "Disable"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Text Sensor Entity",
|
||||
"bool": "Binary Sensor Entity"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "Add",
|
||||
"del": "Unavailable",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Interface unavailable"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Authentication Successful",
|
||||
"content": "Please close this page and return to the account authentication page to click 'Next'.",
|
||||
"button": "Close"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Authentication Failed",
|
||||
"content": "{error_msg}, please close this page and return to the account authentication page to click the authentication link again.",
|
||||
"button": "Close"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Invalid response parameters ('code' or 'state' field is empty)",
|
||||
"-10101": "Passed-in 'state' field mismatch"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Authentication information is invalid, cloud link will be unavailable, please enter the Xiaomi Home integration page, click 'Options' to re-authenticate",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "habilitar",
|
||||
"disable": "deshabilitar"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Entidad del sensor de texto",
|
||||
"bool": "Entidad del sensor binario"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "agregar",
|
||||
"del": "no disponible",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Interfaz no disponible"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Autenticación exitosa",
|
||||
"content": "Por favor, cierre esta página y regrese a la página de autenticación de la cuenta para hacer clic en 'Siguiente'.",
|
||||
"button": "Cerrar"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Autenticación fallida",
|
||||
"content": "{error_msg}, por favor, cierre esta página y regrese a la página de autenticación de la cuenta para hacer clic en el enlace de autenticación nuevamente.",
|
||||
"button": "Cerrar"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Parámetros de respuesta inválidos ('code' o 'state' está vacío)",
|
||||
"-10101": "El campo 'state' proporcionado no coincide"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "La información de autenticación es inválida, la conexión en la nube no estará disponible, por favor, vaya a la página de integración de Xiaomi Home, haga clic en 'Opciones' para volver a autenticar",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "activer",
|
||||
"disable": "désactiver"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Entité du capteur de texte",
|
||||
"bool": "Entité du capteur binaire"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "Ajouter",
|
||||
"del": "Supprimer",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Interface non disponible"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Authentification réussie",
|
||||
"content": "Veuillez fermer cette page et revenir à la page d'authentification du compte pour cliquer sur 'Suivant'.",
|
||||
"button": "Fermer"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Échec de l'authentification",
|
||||
"content": "{error_msg}, veuillez fermer cette page et revenir à la page d'authentification du compte pour cliquer à nouveau sur le lien d'authentification.",
|
||||
"button": "Fermer"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Paramètres de réponse invalides ('code' ou 'state' est vide)",
|
||||
"-10101": "Le champ 'state' transmis ne correspond pas"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Informations d'authentification non valides, le lien cloud ne sera pas disponible, veuillez accéder à la page d'intégration Xiaomi Home, cliquez sur \"Options\" pour vous réauthentifier",
|
||||
|
156
custom_components/xiaomi_home/miot/i18n/it.json
Normal file
156
custom_components/xiaomi_home/miot/i18n/it.json
Normal file
@ -0,0 +1,156 @@
|
||||
{
|
||||
"config": {
|
||||
"other": {
|
||||
"devices": "Dispositivi",
|
||||
"found_central_gateway": ", Trovato Gateway Hub Centrale Locale",
|
||||
"without_room": "Nessuna stanza assegnata",
|
||||
"no_display": "Non visualizzare"
|
||||
},
|
||||
"control_mode": {
|
||||
"auto": "Auto",
|
||||
"cloud": "Cloud"
|
||||
},
|
||||
"statistics_logic": {
|
||||
"or": "Logica OR",
|
||||
"and": "Logica AND"
|
||||
},
|
||||
"filter_mode": {
|
||||
"exclude": "Escludere",
|
||||
"include": "Includere"
|
||||
},
|
||||
"connect_type": {
|
||||
"0": "WiFi",
|
||||
"1": "Dispositivo yunyi",
|
||||
"2": "Dispositivo Cloud",
|
||||
"3": "ZigBee",
|
||||
"4": "webSocket",
|
||||
"5": "Dispositivo Virtuale",
|
||||
"6": "BLE",
|
||||
"7": "AP Locale",
|
||||
"8": "WiFi+BLE",
|
||||
"9": "Altro",
|
||||
"10": "Plug-in Funzionale",
|
||||
"11": "Rete Cellulare",
|
||||
"12": "Cavo",
|
||||
"13": "NB-IoT",
|
||||
"14": "Accesso cloud di terze parti",
|
||||
"15": "Dispositivo di controllo remoto a infrarossi",
|
||||
"16": "BLE-Mesh",
|
||||
"17": "Gruppo di Dispositivi Virtuali",
|
||||
"18": "Sottodispositivo Gateway",
|
||||
"19": "Sottodispositivo Gateway di livello di sicurezza",
|
||||
"22": "PLC",
|
||||
"23": "Solo Cavo",
|
||||
"24": "Matter",
|
||||
"25": "WiFi+Rete Cellulare"
|
||||
},
|
||||
"room_name_rule": {
|
||||
"none": "Non sincronizzare",
|
||||
"home_room": "Nome Casa e Nome Stanza (Camera da Letto Xiaomi Home)",
|
||||
"room": "Nome Stanza (Camera da Letto)",
|
||||
"home": "Nome Casa (Xiaomi Home)"
|
||||
},
|
||||
"option_status": {
|
||||
"enable": "Abilita",
|
||||
"disable": "Disabilita"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Entità Sensore Testo",
|
||||
"bool": "Entità Sensore Binario"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "Aggiungere",
|
||||
"del": "Non disponibile",
|
||||
"offline": "Offline"
|
||||
},
|
||||
"lan_ctrl_config": {
|
||||
"notice_net_dup": "\r\n**[Avviso]** Rilevate più schede di rete che potrebbero essere connesse alla stessa rete. Si prega di prestare attenzione alla selezione.",
|
||||
"net_unavailable": "Interfaccia non disponibile"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Autenticazione Avvenuta",
|
||||
"content": "Chiudere questa pagina e tornare alla pagina di autenticazione dell'account per fare clic su 'Avanti'.",
|
||||
"button": "Chiudi"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Autenticazione Fallita",
|
||||
"content": "{error_msg}, chiudere questa pagina e tornare alla pagina di autenticazione dell'account per fare clic nuovamente sul link di autenticazione.",
|
||||
"button": "Chiudi"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Parametri di risposta non validi (i campi 'code' o 'state' sono vuoti)",
|
||||
"-10101": "Campo 'state' passato non corrispondente"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Le informazioni di autenticazione non sono valide, il collegamento al cloud non sarà disponibile, si prega di accedere alla pagina di integrazione Xiaomi Home e cliccare 'Opzioni' per ri-autenticarsi",
|
||||
"invalid_device_cache": "Le informazioni memorizzate nella cache del dispositivo sono anomale, si prega di accedere alla pagina di integrazione Xiaomi Home e cliccare 'Opzioni->Aggiorna elenco dispositivi' per aggiornare la cache locale",
|
||||
"invalid_cert_info": "Certificato utente non valido, il collegamento centrale locale non sarà disponibile, si prega di accedere alla pagina di integrazione Xiaomi Home e cliccare 'Opzioni' per ri-autenticarsi",
|
||||
"device_cloud_error": "Si è verificata un'eccezione durante l'ottenimento delle informazioni del dispositivo dal cloud, si prega di controllare la connessione alla rete locale",
|
||||
"xiaomi_home_error_title": "Errore di Integrazione Xiaomi Home",
|
||||
"xiaomi_home_error": "Rilevato errore per **{nick_name}({uid}, {cloud_server})**, si prega di accedere alla pagina delle opzioni per riconfigurare.\n\n**Messaggio di errore**: \n{message}",
|
||||
"device_list_changed_title": "Modifiche all'elenco dispositivi Xiaomi Home",
|
||||
"device_list_changed": "Rilevato cambiamento nelle informazioni del dispositivo per **{nick_name}({uid}, {cloud_server})**, si prega di accedere alla pagina delle opzioni di integrazione, cliccare `Opzioni->Aggiorna elenco dispositivi` per aggiornare le informazioni locali dei dispositivi.\n\nStato corrente della rete: {network_status}\n{message}\n",
|
||||
"device_list_add": "\n**{count} nuovi dispositivi:** \n{message}",
|
||||
"device_list_del": "\n**{count} dispositivi non disponibili:** \n{message}",
|
||||
"device_list_offline": "\n**{count} dispositivi offline:** \n{message}",
|
||||
"network_status_online": "Online",
|
||||
"network_status_offline": "Offline",
|
||||
"device_exec_error": "Errore di esecuzione"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"common": {
|
||||
"-10000": "Errore sconosciuto",
|
||||
"-10001": "Servizio non disponibile",
|
||||
"-10002": "Parametro non valido",
|
||||
"-10003": "Risorse insufficienti",
|
||||
"-10004": "Errore interno",
|
||||
"-10005": "Permessi insufficienti",
|
||||
"-10006": "Timeout di esecuzione",
|
||||
"-10007": "Dispositivo offline o inesistente",
|
||||
"-10020": "Non autorizzato (OAuth2)",
|
||||
"-10030": "Token non valido (HTTP)",
|
||||
"-10040": "Formato messaggio non valido",
|
||||
"-10050": "Certificato non valido",
|
||||
"-704000000": "Errore sconosciuto",
|
||||
"-704010000": "Non autorizzato (il dispositivo potrebbe essere stato eliminato)",
|
||||
"-704014006": "Descrizione del dispositivo non trovata",
|
||||
"-704030013": "Proprietà non leggibile",
|
||||
"-704030023": "Proprietà non scrivibile",
|
||||
"-704030033": "Proprietà non sottoscrivibile",
|
||||
"-704040002": "Servizio inesistente",
|
||||
"-704040003": "Proprietà inesistente",
|
||||
"-704040004": "Evento inesistente",
|
||||
"-704040005": "Azione inesistente",
|
||||
"-704040999": "Funzione non online",
|
||||
"-704042001": "Dispositivo inesistente",
|
||||
"-704042011": "Dispositivo offline",
|
||||
"-704053036": "Timeout operazione del dispositivo",
|
||||
"-704053100": "Il dispositivo non può eseguire questa operazione nello stato attuale",
|
||||
"-704083036": "Timeout operazione del dispositivo",
|
||||
"-704090001": "Dispositivo inesistente",
|
||||
"-704220008": "ID non valido",
|
||||
"-704220025": "Conteggio parametri azione non corrispondente",
|
||||
"-704220035": "Errore del parametro azione",
|
||||
"-704220043": "Errore valore proprietà",
|
||||
"-704222034": "Errore valore di ritorno dell'azione",
|
||||
"-705004000": "Errore sconosciuto",
|
||||
"-705004501": "Errore sconosciuto",
|
||||
"-705201013": "Proprietà non leggibile",
|
||||
"-705201015": "Errore di esecuzione azione",
|
||||
"-705201023": "Proprietà non scrivibile",
|
||||
"-705201033": "Proprietà non sottoscrivibile",
|
||||
"-706012000": "Errore sconosciuto",
|
||||
"-706012013": "Proprietà non leggibile",
|
||||
"-706012015": "Errore di esecuzione azione",
|
||||
"-706012023": "Proprietà non scrivibile",
|
||||
"-706012033": "Proprietà non sottoscrivibile",
|
||||
"-706012043": "Errore valore proprietà",
|
||||
"-706014006": "Descrizione del dispositivo non trovata"
|
||||
}
|
||||
}
|
||||
}
|
@ -54,6 +54,10 @@
|
||||
"enable": "有効",
|
||||
"disable": "無効"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "テキストセンサーエンティティ",
|
||||
"bool": "バイナリセンサーエンティティ"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "追加",
|
||||
"del": "利用不可",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "インターフェースが利用できません"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "認証成功",
|
||||
"content": "このページを閉じて、アカウント認証ページに戻り、「次へ」をクリックしてください。",
|
||||
"button": "閉じる"
|
||||
},
|
||||
"fail": {
|
||||
"title": "認証失敗",
|
||||
"content": "{error_msg}、このページを閉じて、アカウント認証ページに戻り、再度認証リンクをクリックしてください。",
|
||||
"button": "閉じる"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "無効な応答パラメータ('code'または'state'フィールドが空です)",
|
||||
"-10101": "渡された'state'フィールドが一致しません"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "認証情報が無効です。クラウドリンクは利用できません。Xiaomi Home統合ページに入り、[オプション]をクリックして再認証してください",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "Inschakelen",
|
||||
"disable": "Uitschakelen"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Tekstsensor-entiteit",
|
||||
"bool": "Binairesensor-entiteit"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "Toevoegen",
|
||||
"del": "Niet beschikbaar",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Interface niet beschikbaar"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Authenticatie geslaagd",
|
||||
"content": "Sluit deze pagina en ga terug naar de accountauthenticatiepagina om op 'Volgende' te klikken.",
|
||||
"button": "Sluiten"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Authenticatie mislukt",
|
||||
"content": "{error_msg}, sluit deze pagina en ga terug naar de accountauthenticatiepagina om opnieuw op de authenticatielink te klikken.",
|
||||
"button": "Sluiten"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Ongeldige antwoordparameters ('code' of 'state' veld is leeg)",
|
||||
"-10101": "Doorgegeven 'state' veld komt niet overeen"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Authenticatie-informatie is ongeldig, cloudverbinding zal niet beschikbaar zijn. Ga naar de Xiaomi Home-integratiepagina en klik op 'Opties' om opnieuw te verifiëren.",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "habilitado",
|
||||
"disable": "desabilitado"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Entidade do sensor de texto",
|
||||
"bool": "Entidade do sensor binário"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "adicionar",
|
||||
"del": "indisponível",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Interface indisponível"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Autenticação bem-sucedida",
|
||||
"content": "Por favor, feche esta página e volte para a página de autenticação da conta para clicar em 'Próximo'.",
|
||||
"button": "Fechar"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Falha na autenticação",
|
||||
"content": "{error_msg}, por favor, feche esta página e volte para a página de autenticação da conta para clicar no link de autenticação novamente.",
|
||||
"button": "Fechar"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Parâmetros de resposta inválidos ('code' ou 'state' está vazio)",
|
||||
"-10101": "O campo 'state' fornecido não corresponde"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Informações de autenticação inválidas, a conexão com a nuvem estará indisponível. Vá para a página de integração do Xiaomi Home e clique em 'Opções' para reautenticar.",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "Habilitar",
|
||||
"disable": "Desabilitar"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Entidade do sensor de texto",
|
||||
"bool": "Entidade do sensor binário"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "Adicionar",
|
||||
"del": "Indisponível",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Interface indisponível"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Autenticação bem-sucedida",
|
||||
"content": "Por favor, feche esta página e volte para a página de autenticação da conta para clicar em 'Seguinte'.",
|
||||
"button": "Fechar"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Falha na autenticação",
|
||||
"content": "{error_msg}, por favor, feche esta página e volte para a página de autenticação da conta para clicar no link de autenticação novamente.",
|
||||
"button": "Fechar"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Parâmetros de resposta inválidos ('code' ou 'state' está vazio)",
|
||||
"-10101": "O campo 'state' fornecido não corresponde"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Informações de autenticação inválidas, a conexão na nuvem ficará indisponível. Por favor, acesse a página de integração do Xiaomi Home e clique em 'Opções' para autenticar novamente.",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "Включить",
|
||||
"disable": "Отключить"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "Сущность текстового датчика",
|
||||
"bool": "Сущность бинарного датчика"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "Добавить",
|
||||
"del": "Недоступно",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "Интерфейс недоступен"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "Аутентификация успешна",
|
||||
"content": "Пожалуйста, закройте эту страницу и вернитесь на страницу аутентификации учетной записи, чтобы нажать 'Далее'.",
|
||||
"button": "Закрыть"
|
||||
},
|
||||
"fail": {
|
||||
"title": "Аутентификация не удалась",
|
||||
"content": "{error_msg}, пожалуйста, закройте эту страницу и вернитесь на страницу аутентификации учетной записи, чтобы снова нажать на ссылку аутентификации.",
|
||||
"button": "Закрыть"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "Недействительные параметры ответа ('code' или 'state' поле пусто)",
|
||||
"-10101": "Переданное поле 'state' не совпадает"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "Информация об аутентификации недействительна, облако будет недоступно, пожалуйста, войдите на страницу интеграции Xiaomi Home, нажмите 'Опции' для повторной аутентификации",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "启用",
|
||||
"disable": "禁用"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "文本传感器实体",
|
||||
"bool": "二进制传感器实体"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "新增",
|
||||
"del": "不可用",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "接口不可用"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "认证成功",
|
||||
"content": "请关闭此页面,返回账号认证页面点击“下一步”",
|
||||
"button": "关闭"
|
||||
},
|
||||
"fail": {
|
||||
"title": "认证失败",
|
||||
"content": "{error_msg},请关闭此页面,返回账号认证页面重新点击认链接进行认证。",
|
||||
"button": "关闭"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "无效的响应参数(“code”或者“state”字段为空)",
|
||||
"-10101": "传入“state”字段不一致"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "认证信息失效,云端链路将不可用,请进入 Xiaomi Home 集成页面,点击“选项”重新认证",
|
||||
|
@ -54,6 +54,10 @@
|
||||
"enable": "啟用",
|
||||
"disable": "禁用"
|
||||
},
|
||||
"binary_mode": {
|
||||
"text": "文本傳感器實體",
|
||||
"bool": "二進制傳感器實體"
|
||||
},
|
||||
"device_state": {
|
||||
"add": "新增",
|
||||
"del": "不可用",
|
||||
@ -64,6 +68,22 @@
|
||||
"net_unavailable": "接口不可用"
|
||||
}
|
||||
},
|
||||
"oauth2": {
|
||||
"success": {
|
||||
"title": "認證成功",
|
||||
"content": "請關閉此頁面,返回帳號認證頁面點擊“下一步”",
|
||||
"button": "關閉"
|
||||
},
|
||||
"fail": {
|
||||
"title": "認證失敗",
|
||||
"content": "{error_msg},請關閉此頁面,返回帳號認證頁面重新點擊認鏈接進行認證。",
|
||||
"button": "關閉"
|
||||
},
|
||||
"error_msg": {
|
||||
"-10100": "無效的響應參數(“code”或者“state”字段為空)",
|
||||
"-10101": "傳入的“state”字段不一致"
|
||||
}
|
||||
},
|
||||
"miot": {
|
||||
"client": {
|
||||
"invalid_oauth_info": "認證信息失效,雲端鏈路將不可用,請進入 Xiaomi Home 集成頁面,點擊“選項”重新認證",
|
||||
|
@ -18,6 +18,10 @@
|
||||
ts: 1603967572
|
||||
1245.airpurifier.dl01:
|
||||
ts: 1607502661
|
||||
17216.magic_touch.d150:
|
||||
ts: 1575097876
|
||||
17216.magic_touch.d152:
|
||||
ts: 1575097876
|
||||
17216.massage.ec1266a:
|
||||
ts: 1615881124
|
||||
397.light.hallight:
|
||||
@ -56,6 +60,10 @@ bj352.airmonitor.m30:
|
||||
ts: 1686644541
|
||||
bj352.waterpuri.s100cm:
|
||||
ts: 1615795630
|
||||
bymiot.gateway.v1:
|
||||
ts: 1575097876
|
||||
bymiot.gateway.v2:
|
||||
ts: 1575097876
|
||||
cgllc.airmonitor.b1:
|
||||
ts: 1676339912
|
||||
cgllc.airmonitor.s1:
|
||||
@ -64,6 +72,8 @@ cgllc.clock.cgc1:
|
||||
ts: 1686644422
|
||||
cgllc.clock.dove:
|
||||
ts: 1619607474
|
||||
cgllc.gateway.s1:
|
||||
ts: 1575097876
|
||||
cgllc.magnet.hodor:
|
||||
ts: 1724329476
|
||||
cgllc.motion.cgpr1:
|
||||
@ -120,8 +130,14 @@ chuangmi.cateye.ipc018:
|
||||
ts: 1632735241
|
||||
chuangmi.cateye.ipc508:
|
||||
ts: 1633677521
|
||||
chuangmi.door.hmi508:
|
||||
ts: 1611733437
|
||||
chuangmi.door.hmi515:
|
||||
ts: 1640334316
|
||||
chuangmi.gateway.ipc011:
|
||||
ts: 1575097876
|
||||
chuangmi.ir.v2:
|
||||
ts: 1575097876
|
||||
chuangmi.lock.hmi501:
|
||||
ts: 1614742147
|
||||
chuangmi.lock.hmi501b01:
|
||||
@ -142,10 +158,18 @@ chuangmi.plug.v1:
|
||||
ts: 1621925183
|
||||
chuangmi.plug.v3:
|
||||
ts: 1644480255
|
||||
chuangmi.plug.vtl_v1:
|
||||
ts: 1575097876
|
||||
chuangmi.radio.v1:
|
||||
ts: 1531108800
|
||||
chuangmi.radio.v2:
|
||||
ts: 1531108800
|
||||
chuangmi.remote.h102a03:
|
||||
ts: 1575097876
|
||||
chuangmi.remote.h102c01:
|
||||
ts: 1575097876
|
||||
chuangmi.remote.v2:
|
||||
ts: 1575097876
|
||||
chunmi.cooker.eh1:
|
||||
ts: 1607339278
|
||||
chunmi.cooker.eh402:
|
||||
@ -204,6 +228,8 @@ dmaker.airfresh.t2017:
|
||||
ts: 1686731233
|
||||
dmaker.fan.p5:
|
||||
ts: 1655793784
|
||||
doco.fcb.docov001:
|
||||
ts: 1575097876
|
||||
dsm.lock.h3:
|
||||
ts: 1615283790
|
||||
dsm.lock.q3:
|
||||
@ -218,6 +244,30 @@ fawad.airrtc.fwd20011:
|
||||
ts: 1610607149
|
||||
fbs.airmonitor.pth02:
|
||||
ts: 1686644918
|
||||
fengmi.projector.fm05:
|
||||
ts: 1575097876
|
||||
fengmi.projector.fm15:
|
||||
ts: 1575097876
|
||||
fengmi.projector.fm154k:
|
||||
ts: 1575097876
|
||||
fengmi.projector.l166:
|
||||
ts: 1650352923
|
||||
fengmi.projector.l176:
|
||||
ts: 1649936204
|
||||
fengmi.projector.l246:
|
||||
ts: 1575097876
|
||||
fengmi.projector.m055:
|
||||
ts: 1652839826
|
||||
fengmi.projector.m055d:
|
||||
ts: 1654067980
|
||||
fengyu.intercom.beebird:
|
||||
ts: 1575097876
|
||||
fengyu.intercom.sharkv1:
|
||||
ts: 1575097876
|
||||
fotile.hood.emd1tmi:
|
||||
ts: 1607483642
|
||||
guoshi.other.sem01:
|
||||
ts: 1602662080
|
||||
hannto.printer.anise:
|
||||
ts: 1618989537
|
||||
hannto.printer.honey:
|
||||
@ -226,14 +276,26 @@ hannto.printer.honey1s:
|
||||
ts: 1614332725
|
||||
hfjh.fishbowl.v1:
|
||||
ts: 1615278556
|
||||
hhcc.bleflowerpot.v2:
|
||||
ts: 1575097876
|
||||
hhcc.plantmonitor.v1:
|
||||
ts: 1664163526
|
||||
hith.foot_bath.q2:
|
||||
ts: 1531108800
|
||||
hmpace.bracelet.v4:
|
||||
ts: 1575097876
|
||||
hmpace.scales.mibfs:
|
||||
ts: 1575097876
|
||||
hmpace.scales.miscale2:
|
||||
ts: 1575097876
|
||||
huohe.lock.m1:
|
||||
ts: 1635410938
|
||||
huoman.litter_box.co1:
|
||||
ts: 1687165034
|
||||
hutlon.lock.v0001:
|
||||
ts: 1634799698
|
||||
idelan.aircondition.g1:
|
||||
ts: 1575097876
|
||||
idelan.aircondition.v1:
|
||||
ts: 1614666973
|
||||
idelan.aircondition.v2:
|
||||
@ -248,14 +310,22 @@ ikea.light.led1537r6:
|
||||
ts: 1605162872
|
||||
ikea.light.led1545g12:
|
||||
ts: 1605162937
|
||||
ikea.light.led1546g12:
|
||||
ts: 1575097876
|
||||
ikea.light.led1623g12:
|
||||
ts: 1605163009
|
||||
ikea.light.led1649c5:
|
||||
ts: 1605163064
|
||||
ikea.light.led1650r5:
|
||||
ts: 1575097876
|
||||
imibar.cooker.mbihr3:
|
||||
ts: 1624620659
|
||||
imou99.camera.tp2:
|
||||
ts: 1531108800
|
||||
inovel.projector.me2:
|
||||
ts: 1575097876
|
||||
iracc.aircondition.d19:
|
||||
ts: 1609914362
|
||||
isa.camera.df3:
|
||||
ts: 1531108800
|
||||
isa.camera.hl5:
|
||||
@ -266,18 +336,34 @@ isa.camera.isc5:
|
||||
ts: 1531108800
|
||||
isa.camera.isc5c1:
|
||||
ts: 1621238175
|
||||
isa.camera.qf3:
|
||||
ts: 1575097876
|
||||
isa.cateye.hldb6:
|
||||
ts: 1575097876
|
||||
isa.magnet.dw2hl:
|
||||
ts: 1638274655
|
||||
jieman.magic_touch.js78:
|
||||
ts: 1575097876
|
||||
jiqid.mistory.ipen1:
|
||||
ts: 1575097876
|
||||
jiqid.mistory.pro:
|
||||
ts: 1531108800
|
||||
jiqid.mistory.v1:
|
||||
ts: 1531108800
|
||||
jiqid.mistudy.v2:
|
||||
ts: 1610612349
|
||||
jiqid.robot.cube:
|
||||
ts: 1575097876
|
||||
jiwu.lock.jwp01:
|
||||
ts: 1614752632
|
||||
jyaiot.cm.ccj01:
|
||||
ts: 1611824545
|
||||
k0918.toothbrush.kid01:
|
||||
ts: 1575097876
|
||||
kejia.airer.th001:
|
||||
ts: 1575097876
|
||||
ksmb.treadmill.k12:
|
||||
ts: 1575097876
|
||||
ksmb.treadmill.v1:
|
||||
ts: 1611211447
|
||||
ksmb.treadmill.v2:
|
||||
@ -390,6 +476,8 @@ loock.lock.xfvl10:
|
||||
ts: 1632814256
|
||||
loock.safe.v1:
|
||||
ts: 1619607755
|
||||
lumi.acpartner.mcn02:
|
||||
ts: 1655791626
|
||||
lumi.acpartner.v1:
|
||||
ts: 1531108800
|
||||
lumi.acpartner.v2:
|
||||
@ -462,6 +550,8 @@ lumi.lock.acn02:
|
||||
ts: 1623928631
|
||||
lumi.lock.acn03:
|
||||
ts: 1614752574
|
||||
lumi.lock.aq1:
|
||||
ts: 1612518044
|
||||
lumi.lock.bacn01:
|
||||
ts: 1614741699
|
||||
lumi.lock.bmcn02:
|
||||
@ -482,6 +572,8 @@ lumi.lock.mcn007:
|
||||
ts: 1650446757
|
||||
lumi.lock.mcn01:
|
||||
ts: 1679881881
|
||||
lumi.lock.v1:
|
||||
ts: 1575097876
|
||||
lumi.lock.wbmcn1:
|
||||
ts: 1619422072
|
||||
lumi.motion.bmgl01:
|
||||
@ -510,14 +602,20 @@ lumi.sensor_86sw1.v1:
|
||||
ts: 1609311038
|
||||
lumi.sensor_86sw2.v1:
|
||||
ts: 1608795035
|
||||
lumi.sensor_cube.aqgl01:
|
||||
ts: 1575097876
|
||||
lumi.sensor_ht.v1:
|
||||
ts: 1621239877
|
||||
lumi.sensor_magnet.aq2:
|
||||
ts: 1641112867
|
||||
lumi.sensor_magnet.v1:
|
||||
ts: 1606120416
|
||||
lumi.sensor_magnet.v2:
|
||||
ts: 1641113779
|
||||
lumi.sensor_motion.aq2:
|
||||
ts: 1676433994
|
||||
lumi.sensor_motion.v1:
|
||||
ts: 1605093075
|
||||
lumi.sensor_motion.v2:
|
||||
ts: 1672818550
|
||||
lumi.sensor_natgas.v1:
|
||||
@ -530,6 +628,8 @@ lumi.sensor_switch.aq2:
|
||||
ts: 1615256430
|
||||
lumi.sensor_switch.aq3:
|
||||
ts: 1607399487
|
||||
lumi.sensor_switch.v1:
|
||||
ts: 1606874434
|
||||
lumi.sensor_switch.v2:
|
||||
ts: 1609310683
|
||||
lumi.sensor_wleak.aq1:
|
||||
@ -574,6 +674,20 @@ miaomiaoce.sensor_ht.t1:
|
||||
ts: 1616057242
|
||||
miaomiaoce.sensor_ht.t2:
|
||||
ts: 1636603553
|
||||
miaomiaoce.thermo.t01:
|
||||
ts: 1575097876
|
||||
midea.aircondition.v1:
|
||||
ts: 1575097876
|
||||
midea.aircondition.xa1:
|
||||
ts: 1575097876
|
||||
midea.aircondition.xa2:
|
||||
ts: 1575097876
|
||||
midr.rv_mirror.m2:
|
||||
ts: 1575097876
|
||||
midr.rv_mirror.m5:
|
||||
ts: 1575097876
|
||||
midr.rv_mirror.v1:
|
||||
ts: 1575097876
|
||||
miir.aircondition.ir01:
|
||||
ts: 1531108800
|
||||
miir.aircondition.ir02:
|
||||
@ -612,6 +726,8 @@ minij.washer.v5:
|
||||
ts: 1622792196
|
||||
minij.washer.v8:
|
||||
ts: 1615777868
|
||||
minuo.tracker.lm001:
|
||||
ts: 1575097876
|
||||
miot.light.plato2:
|
||||
ts: 1685518142
|
||||
miot.light.plato3:
|
||||
@ -624,18 +740,32 @@ mmgg.feeder.snack:
|
||||
ts: 1607503182
|
||||
moyu.washer.s1hm:
|
||||
ts: 1624620888
|
||||
mrbond.airer.m0:
|
||||
ts: 1575097876
|
||||
mrbond.airer.m1pro:
|
||||
ts: 1646393746
|
||||
mrbond.airer.m1s:
|
||||
ts: 1646393874
|
||||
mrbond.airer.m1super:
|
||||
ts: 1575097876
|
||||
msj.f_washer.m1:
|
||||
ts: 1614914340
|
||||
mxiang.cateye.mdb10:
|
||||
ts: 1616140362
|
||||
mxiang.cateye.xmcatt1:
|
||||
ts: 1616140207
|
||||
nhy.airrtc.v1:
|
||||
ts: 1575097876
|
||||
ninebot.scooter.v1:
|
||||
ts: 1602662395
|
||||
ninebot.scooter.v6:
|
||||
ts: 1575097876
|
||||
nuwa.robot.minikiwi:
|
||||
ts: 1575097876
|
||||
nwt.derh.wdh318efw1:
|
||||
ts: 1611822375
|
||||
onemore.wifispeaker.sm4:
|
||||
ts: 1575097876
|
||||
opple.light.bydceiling:
|
||||
ts: 1608187619
|
||||
opple.light.fanlight:
|
||||
@ -646,6 +776,8 @@ opple.remote.5pb112:
|
||||
ts: 1627453840
|
||||
opple.remote.5pb113:
|
||||
ts: 1636599905
|
||||
orion.wifispeaker.cm1:
|
||||
ts: 1575097876
|
||||
ows.towel_w.mj1x0:
|
||||
ts: 1610604939
|
||||
philips.light.bceiling1:
|
||||
@ -696,6 +828,8 @@ pwzn.relay.apple:
|
||||
ts: 1611217196
|
||||
pwzn.relay.banana:
|
||||
ts: 1646647255
|
||||
qicyc.bike.tdp02z:
|
||||
ts: 1575097876
|
||||
qike.bhf_light.qk201801:
|
||||
ts: 1608174909
|
||||
qmi.powerstrip.v1:
|
||||
@ -726,8 +860,32 @@ roborock.vacuum.t6:
|
||||
ts: 1619423841
|
||||
rockrobo.vacuum.v1:
|
||||
ts: 1531108800
|
||||
roidmi.carairpuri.pro:
|
||||
ts: 1575097876
|
||||
roidmi.carairpuri.v1:
|
||||
ts: 1575097876
|
||||
roidmi.cleaner.f8pro:
|
||||
ts: 1575097876
|
||||
roidmi.cleaner.v1:
|
||||
ts: 1575097876
|
||||
roidmi.cleaner.v2:
|
||||
ts: 1638514177
|
||||
roidmi.cleaner.v382:
|
||||
ts: 1575097876
|
||||
roidmi.vacuum.v1:
|
||||
ts: 1575097876
|
||||
rokid.robot.me:
|
||||
ts: 1575097876
|
||||
rokid.robot.mini:
|
||||
ts: 1575097876
|
||||
rokid.robot.pebble:
|
||||
ts: 1575097876
|
||||
rokid.robot.pebble2:
|
||||
ts: 1575097876
|
||||
roome.bhf_light.yf6002:
|
||||
ts: 1531108800
|
||||
rotai.magic_touch.sx300:
|
||||
ts: 1602662578
|
||||
rotai.massage.rt5728:
|
||||
ts: 1610607000
|
||||
rotai.massage.rt5850:
|
||||
@ -738,22 +896,42 @@ rotai.massage.rt5863:
|
||||
ts: 1611827937
|
||||
rotai.massage.rt5870:
|
||||
ts: 1632376570
|
||||
runmi.suitcase.v1:
|
||||
ts: 1575097876
|
||||
scishare.coffee.s1102:
|
||||
ts: 1611824402
|
||||
shjszn.gateway.c1:
|
||||
ts: 1575097876
|
||||
shjszn.lock.c1:
|
||||
ts: 1575097876
|
||||
shjszn.lock.kx:
|
||||
ts: 1575097876
|
||||
shuii.humidifier.jsq001:
|
||||
ts: 1575097876
|
||||
shuii.humidifier.jsq002:
|
||||
ts: 1606376290
|
||||
skyrc.feeder.dfeed:
|
||||
ts: 1626082349
|
||||
skyrc.pet_waterer.fre1:
|
||||
ts: 1608186812
|
||||
smith.w_soften.cxs05ta1:
|
||||
ts: 1575097876
|
||||
smith.waterheater.cxea1:
|
||||
ts: 1611826349
|
||||
smith.waterheater.cxeb1:
|
||||
ts: 1611826388
|
||||
smith.waterpuri.jnt600:
|
||||
ts: 1531108800
|
||||
soocare.toothbrush.m1:
|
||||
ts: 1575097876
|
||||
soocare.toothbrush.m1s:
|
||||
ts: 1610611310
|
||||
soocare.toothbrush.mc1:
|
||||
ts: 1575097876
|
||||
soocare.toothbrush.t501:
|
||||
ts: 1672192586
|
||||
soocare.toothbrush.x3:
|
||||
ts: 1575097876
|
||||
sxds.pillow.pillow02:
|
||||
ts: 1611222235
|
||||
syniot.curtain.syc1:
|
||||
@ -778,6 +956,10 @@ tokit.oven.tk32pro1:
|
||||
ts: 1617002408
|
||||
tokit.pre_cooker.tkih1:
|
||||
ts: 1607410832
|
||||
trios1.bleshoes.v02:
|
||||
ts: 1602662599
|
||||
txdd.wifispeaker.x1:
|
||||
ts: 1575097876
|
||||
viomi.aircondition.v10:
|
||||
ts: 1606375041
|
||||
viomi.aircondition.v21:
|
||||
@ -830,12 +1012,16 @@ viomi.fridge.u13:
|
||||
ts: 1614667152
|
||||
viomi.fridge.u15:
|
||||
ts: 1607505693
|
||||
viomi.fridge.u17:
|
||||
ts: 1575097876
|
||||
viomi.fridge.u18:
|
||||
ts: 1614655755
|
||||
viomi.fridge.u2:
|
||||
ts: 1531108800
|
||||
viomi.fridge.u24:
|
||||
ts: 1614667214
|
||||
viomi.fridge.u25:
|
||||
ts: 1575097876
|
||||
viomi.fridge.u4:
|
||||
ts: 1614667295
|
||||
viomi.fridge.u6:
|
||||
@ -992,6 +1178,82 @@ xiaomi.aircondition.ma6:
|
||||
ts: 1721629272
|
||||
xiaomi.aircondition.ma9:
|
||||
ts: 1721629362
|
||||
xiaomi.plc.v1:
|
||||
ts: 1575097876
|
||||
xiaomi.repeater.v1:
|
||||
ts: 1575097876
|
||||
xiaomi.repeater.v2:
|
||||
ts: 1575097876
|
||||
xiaomi.repeater.v3:
|
||||
ts: 1575097876
|
||||
xiaomi.router.d01:
|
||||
ts: 1575097876
|
||||
xiaomi.router.lv1:
|
||||
ts: 1575097876
|
||||
xiaomi.router.lv3:
|
||||
ts: 1575097876
|
||||
xiaomi.router.mv1:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r2100:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r3600:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r3a:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r3d:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r3g:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r3gv2:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r3gv2n:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r3p:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r4:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r4a:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r4ac:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r4c:
|
||||
ts: 1575097876
|
||||
xiaomi.router.r4cm:
|
||||
ts: 1575097876
|
||||
xiaomi.router.rm1800:
|
||||
ts: 1575097876
|
||||
xiaomi.router.v1:
|
||||
ts: 1575097876
|
||||
xiaomi.router.v2:
|
||||
ts: 1575097876
|
||||
xiaomi.router.v3:
|
||||
ts: 1575097876
|
||||
xiaomi.split_tv.b1:
|
||||
ts: 1575097876
|
||||
xiaomi.split_tv.v1:
|
||||
ts: 1575097876
|
||||
xiaomi.tv.b1:
|
||||
ts: 1661248580
|
||||
xiaomi.tv.h1:
|
||||
ts: 1575097876
|
||||
xiaomi.tv.i1:
|
||||
ts: 1661248572
|
||||
xiaomi.tv.v1:
|
||||
ts: 1670811870
|
||||
xiaomi.tvbox.b1:
|
||||
ts: 1694503508
|
||||
xiaomi.tvbox.i1:
|
||||
ts: 1694503515
|
||||
xiaomi.tvbox.v1:
|
||||
ts: 1694503501
|
||||
xiaomi.watch.band1:
|
||||
ts: 1575097876
|
||||
xiaomi.watch.band1A:
|
||||
ts: 1575097876
|
||||
xiaomi.watch.band1S:
|
||||
ts: 1575097876
|
||||
xiaomi.watch.band2:
|
||||
ts: 1575097876
|
||||
xiaomi.wifispeaker.l04m:
|
||||
ts: 1658817956
|
||||
xiaomi.wifispeaker.l06a:
|
||||
@ -1012,6 +1274,10 @@ xiaomi.wifispeaker.lx5a:
|
||||
ts: 1672299577
|
||||
xiaomi.wifispeaker.s12:
|
||||
ts: 1672299594
|
||||
xiaomi.wifispeaker.v1:
|
||||
ts: 1575097876
|
||||
xiaomi.wifispeaker.v3:
|
||||
ts: 1575097876
|
||||
xiaomi.wifispeaker.x08a:
|
||||
ts: 1672818945
|
||||
xiaomi.wifispeaker.x08c:
|
||||
@ -1028,6 +1294,44 @@ xiaovv.camera.xvd5:
|
||||
ts: 1531108800
|
||||
xiaovv.camera.xvsnowman:
|
||||
ts: 1531108800
|
||||
xiaoxun.robot.v1:
|
||||
ts: 1575097876
|
||||
xiaoxun.tracker.v1:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.sw306:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.sw560:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.sw705:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.sw710a2:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.sw760:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.sw900:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.sw960:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v1:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v10:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v11:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v2:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v3:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v4:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v5:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v7:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v8:
|
||||
ts: 1575097876
|
||||
xiaoxun.watch.v9:
|
||||
ts: 1575097876
|
||||
xjx.toilet.pro:
|
||||
ts: 1615965466
|
||||
xjx.toilet.pure:
|
||||
@ -1054,6 +1358,8 @@ yeelink.bhf_light.v3:
|
||||
ts: 1608790102
|
||||
yeelink.bhf_light.v5:
|
||||
ts: 1601292562
|
||||
yeelink.gateway.v1:
|
||||
ts: 1575097876
|
||||
yeelink.light.bslamp1:
|
||||
ts: 1703120679
|
||||
yeelink.light.bslamp2:
|
||||
@ -1192,6 +1498,10 @@ yunmi.kettle.r2:
|
||||
ts: 1606372087
|
||||
yunmi.kettle.r3:
|
||||
ts: 1637309534
|
||||
yunmi.kettle.v1:
|
||||
ts: 1575097876
|
||||
yunmi.kettle.v9:
|
||||
ts: 1602662686
|
||||
yunmi.plmachine.mg2:
|
||||
ts: 1611833658
|
||||
yunmi.waterpuri.c5:
|
||||
@ -1230,18 +1540,26 @@ yunmi.waterpurifier.v2:
|
||||
ts: 1632377061
|
||||
yunmi.waterpurifier.v3:
|
||||
ts: 1611221428
|
||||
yunyi.camera.v1:
|
||||
ts: 1575097876
|
||||
yyunyi.wopener.yypy24:
|
||||
ts: 1616741966
|
||||
yyzhn.gateway.yn181126:
|
||||
ts: 1610689325
|
||||
zdeer.ajh.a8:
|
||||
ts: 1531108800
|
||||
zdeer.ajh.a9:
|
||||
ts: 1531108800
|
||||
zdeer.ajh.ajb:
|
||||
ts: 1608276454
|
||||
zdeer.ajh.zda10:
|
||||
ts: 1531108800
|
||||
zdeer.ajh.zda9:
|
||||
ts: 1531108800
|
||||
zdeer.ajh.zjy:
|
||||
ts: 1531108800
|
||||
zhij.toothbrush.bv1:
|
||||
ts: 1575097876
|
||||
zhimi.aircondition.ma1:
|
||||
ts: 1615185265
|
||||
zhimi.aircondition.ma3:
|
||||
@ -1250,6 +1568,8 @@ zhimi.aircondition.ma4:
|
||||
ts: 1626334057
|
||||
zhimi.aircondition.v1:
|
||||
ts: 1610610931
|
||||
zhimi.aircondition.v2:
|
||||
ts: 1575097876
|
||||
zhimi.aircondition.va1:
|
||||
ts: 1609924720
|
||||
zhimi.aircondition.za1:
|
||||
@ -1276,8 +1596,12 @@ zhimi.airpurifier.sa2:
|
||||
ts: 1635820002
|
||||
zhimi.airpurifier.v1:
|
||||
ts: 1635855633
|
||||
zhimi.airpurifier.v2:
|
||||
ts: 1575097876
|
||||
zhimi.airpurifier.v3:
|
||||
ts: 1676339933
|
||||
zhimi.airpurifier.v5:
|
||||
ts: 1575097876
|
||||
zhimi.airpurifier.v6:
|
||||
ts: 1636978652
|
||||
zhimi.airpurifier.v7:
|
||||
@ -1318,3 +1642,5 @@ zimi.mosq.v1:
|
||||
ts: 1620728957
|
||||
zimi.powerstrip.v2:
|
||||
ts: 1620812714
|
||||
zimi.projector.v1:
|
||||
ts: 1575097876
|
||||
|
@ -59,7 +59,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.components import zeroconf
|
||||
|
||||
# pylint: disable=relative-beyond-top-level
|
||||
from .common import MIoTMatcher
|
||||
from .common import MIoTMatcher, slugify_did
|
||||
from .const import (
|
||||
DEFAULT_CTRL_MODE, DEFAULT_INTEGRATION_LANGUAGE, DEFAULT_NICK_NAME, DOMAIN,
|
||||
MIHOME_CERT_EXPIRE_MARGIN, NETWORK_REFRESH_INTERVAL,
|
||||
@ -150,7 +150,7 @@ class MIoTClient:
|
||||
# Device list update timestamp
|
||||
_device_list_update_ts: int
|
||||
|
||||
_sub_source_list: dict[str]
|
||||
_sub_source_list: dict[str, Optional[str]]
|
||||
_sub_tree: MIoTMatcher
|
||||
_sub_device_state: dict[str, MipsDeviceState]
|
||||
|
||||
@ -169,6 +169,10 @@ class MIoTClient:
|
||||
_show_devices_changed_notify_timer: Optional[asyncio.TimerHandle]
|
||||
# Display devices changed notify
|
||||
_display_devs_notify: list[str]
|
||||
_display_notify_content_hash: Optional[int]
|
||||
# Display binary mode
|
||||
_display_binary_text: bool
|
||||
_display_binary_bool: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -235,6 +239,11 @@ class MIoTClient:
|
||||
|
||||
self._display_devs_notify = entry_data.get(
|
||||
'display_devices_changed_notify', ['add', 'del', 'offline'])
|
||||
self._display_notify_content_hash = None
|
||||
self._display_binary_text = 'text' in entry_data.get(
|
||||
'display_binary_mode', ['text'])
|
||||
self._display_binary_bool = 'bool' in entry_data.get(
|
||||
'display_binary_mode', ['text'])
|
||||
|
||||
async def init_async(self) -> None:
|
||||
# Load user config and check
|
||||
@ -257,6 +266,7 @@ class MIoTClient:
|
||||
client_id=OAUTH2_CLIENT_ID,
|
||||
redirect_url=self._entry_data['oauth_redirect_url'],
|
||||
cloud_server=self._cloud_server,
|
||||
uuid=self._entry_data["uuid"],
|
||||
loop=self._main_loop)
|
||||
# MIoT http client instance
|
||||
self._http = MIoTHttpClient(
|
||||
@ -356,7 +366,7 @@ class MIoTClient:
|
||||
# Cloud mips
|
||||
self._mips_cloud.unsub_mips_state(
|
||||
key=f'{self._uid}-{self._cloud_server}')
|
||||
self._mips_cloud.disconnect()
|
||||
self._mips_cloud.deinit()
|
||||
# Cancel refresh cloud devices
|
||||
if self._refresh_cloud_devices_timer:
|
||||
self._refresh_cloud_devices_timer.cancel()
|
||||
@ -369,7 +379,7 @@ class MIoTClient:
|
||||
for mips in self._mips_local.values():
|
||||
mips.on_dev_list_changed = None
|
||||
mips.unsub_mips_state(key=mips.group_id)
|
||||
mips.disconnect()
|
||||
mips.deinit()
|
||||
if self._mips_local_state_changed_timers:
|
||||
for timer_item in (
|
||||
self._mips_local_state_changed_timers.values()):
|
||||
@ -468,6 +478,14 @@ class MIoTClient:
|
||||
def display_devices_changed_notify(self) -> list[str]:
|
||||
return self._display_devs_notify
|
||||
|
||||
@property
|
||||
def display_binary_text(self) -> bool:
|
||||
return self._display_binary_text
|
||||
|
||||
@property
|
||||
def display_binary_bool(self) -> bool:
|
||||
return self._display_binary_bool
|
||||
|
||||
@display_devices_changed_notify.setter
|
||||
def display_devices_changed_notify(self, value: list[str]) -> None:
|
||||
if set(value) == set(self._display_devs_notify):
|
||||
@ -542,7 +560,8 @@ class MIoTClient:
|
||||
return True
|
||||
except Exception as err:
|
||||
self.__show_client_error_notify(
|
||||
message=self._i18n.translate('miot.client.invalid_oauth_info'),
|
||||
message=self._i18n.translate(
|
||||
'miot.client.invalid_oauth_info'), # type: ignore
|
||||
notify_key='oauth_info')
|
||||
_LOGGER.error(
|
||||
'refresh oauth info error (%s, %s), %s, %s',
|
||||
@ -585,7 +604,8 @@ class MIoTClient:
|
||||
return True
|
||||
except MIoTClientError as error:
|
||||
self.__show_client_error_notify(
|
||||
message=self._i18n.translate('miot.client.invalid_cert_info'),
|
||||
message=self._i18n.translate(
|
||||
'miot.client.invalid_cert_info'), # type: ignore
|
||||
notify_key='user_cert')
|
||||
_LOGGER.error(
|
||||
'refresh user cert error, %s, %s',
|
||||
@ -600,7 +620,7 @@ class MIoTClient:
|
||||
# Priority local control
|
||||
if self._ctrl_mode == CtrlMode.AUTO:
|
||||
# Gateway control
|
||||
device_gw: dict = self._device_list_gateway.get(did, None)
|
||||
device_gw = self._device_list_gateway.get(did, None)
|
||||
if (
|
||||
device_gw and device_gw.get('online', False)
|
||||
and device_gw.get('specv2_access', False)
|
||||
@ -621,7 +641,7 @@ class MIoTClient:
|
||||
raise MIoTClientError(
|
||||
self.__get_exec_error_with_rc(rc=rc))
|
||||
# Lan control
|
||||
device_lan: dict = self._device_list_lan.get(did, None)
|
||||
device_lan = self._device_list_lan.get(did, None)
|
||||
if device_lan and device_lan.get('online', False):
|
||||
result = await self._miot_lan.set_prop_async(
|
||||
did=did, siid=siid, piid=piid, value=value)
|
||||
@ -637,7 +657,7 @@ class MIoTClient:
|
||||
# Cloud control
|
||||
device_cloud = self._device_list_cloud.get(did, None)
|
||||
if device_cloud and device_cloud.get('online', False):
|
||||
result: list = await self._http.set_prop_async(
|
||||
result = await self._http.set_prop_async(
|
||||
params=[
|
||||
{'did': did, 'siid': siid, 'piid': piid, 'value': value}
|
||||
])
|
||||
@ -726,7 +746,7 @@ class MIoTClient:
|
||||
if did not in self._device_list_cache:
|
||||
raise MIoTClientError(f'did not exist, {did}')
|
||||
|
||||
device_gw: dict = self._device_list_gateway.get(did, None)
|
||||
device_gw = self._device_list_gateway.get(did, None)
|
||||
# Priority local control
|
||||
if self._ctrl_mode == CtrlMode.AUTO:
|
||||
if (
|
||||
@ -762,7 +782,7 @@ class MIoTClient:
|
||||
self.__get_exec_error_with_rc(rc=rc))
|
||||
# Cloud control
|
||||
device_cloud = self._device_list_cloud.get(did, None)
|
||||
if device_cloud.get('online', False):
|
||||
if device_cloud and device_cloud.get('online', False):
|
||||
result: dict = await self._http.action_async(
|
||||
did=did, siid=siid, aiid=aiid, in_list=in_list)
|
||||
if result:
|
||||
@ -778,14 +798,15 @@ class MIoTClient:
|
||||
dids=[did]))
|
||||
raise MIoTClientError(
|
||||
self.__get_exec_error_with_rc(rc=rc))
|
||||
# Show error message
|
||||
# TODO: Show error message
|
||||
_LOGGER.error(
|
||||
'client action failed, %s.%d.%d', did, siid, aiid)
|
||||
return None
|
||||
return []
|
||||
|
||||
def sub_prop(
|
||||
self, did: str, handler: Callable[[dict, Any], None],
|
||||
siid: int = None, piid: int = None, handler_ctx: Any = None
|
||||
siid: Optional[int] = None, piid: Optional[int] = None,
|
||||
handler_ctx: Any = None
|
||||
) -> bool:
|
||||
if did not in self._device_list_cache:
|
||||
raise MIoTClientError(f'did not exist, {did}')
|
||||
@ -798,7 +819,9 @@ class MIoTClient:
|
||||
_LOGGER.debug('client sub prop, %s', topic)
|
||||
return True
|
||||
|
||||
def unsub_prop(self, did: str, siid: int = None, piid: int = None) -> bool:
|
||||
def unsub_prop(
|
||||
self, did: str, siid: Optional[int] = None, piid: Optional[int] = None
|
||||
) -> bool:
|
||||
topic = (
|
||||
f'{did}/p/'
|
||||
f'{"#" if siid is None or piid is None else f"{siid}/{piid}"}')
|
||||
@ -809,7 +832,8 @@ class MIoTClient:
|
||||
|
||||
def sub_event(
|
||||
self, did: str, handler: Callable[[dict, Any], None],
|
||||
siid: int = None, eiid: int = None, handler_ctx: Any = None
|
||||
siid: Optional[int] = None, eiid: Optional[int] = None,
|
||||
handler_ctx: Any = None
|
||||
) -> bool:
|
||||
if did not in self._device_list_cache:
|
||||
raise MIoTClientError(f'did not exist, {did}')
|
||||
@ -821,7 +845,9 @@ class MIoTClient:
|
||||
_LOGGER.debug('client sub event, %s', topic)
|
||||
return True
|
||||
|
||||
def unsub_event(self, did: str, siid: int = None, eiid: int = None) -> bool:
|
||||
def unsub_event(
|
||||
self, did: str, siid: Optional[int] = None, eiid: Optional[int] = None
|
||||
) -> bool:
|
||||
topic = (
|
||||
f'{did}/e/'
|
||||
f'{"#" if siid is None or eiid is None else f"{siid}/{eiid}"}')
|
||||
@ -847,8 +873,40 @@ class MIoTClient:
|
||||
_LOGGER.debug('client unsub device state, %s', did)
|
||||
return True
|
||||
|
||||
async def remove_device_async(self, did: str) -> None:
|
||||
if did not in self._device_list_cache:
|
||||
return
|
||||
sub_from = self._sub_source_list.pop(did, None)
|
||||
# Unsub
|
||||
if sub_from:
|
||||
if sub_from == 'cloud':
|
||||
self._mips_cloud.unsub_prop(did=did)
|
||||
self._mips_cloud.unsub_event(did=did)
|
||||
elif sub_from == 'lan':
|
||||
self._miot_lan.unsub_prop(did=did)
|
||||
self._miot_lan.unsub_event(did=did)
|
||||
elif sub_from in self._mips_local:
|
||||
mips = self._mips_local[sub_from]
|
||||
mips.unsub_prop(did=did)
|
||||
mips.unsub_event(did=did)
|
||||
# Storage
|
||||
await self._storage.save_async(
|
||||
domain='miot_devices',
|
||||
name=f'{self._uid}_{self._cloud_server}',
|
||||
data=self._device_list_cache)
|
||||
# Update notify
|
||||
self.__request_show_devices_changed_notify()
|
||||
|
||||
async def remove_device2_async(self, did_tag: str) -> None:
|
||||
for did in self._device_list_cache:
|
||||
d_tag = slugify_did(cloud_server=self._cloud_server, did=did)
|
||||
if did_tag == d_tag:
|
||||
await self.remove_device_async(did)
|
||||
break
|
||||
|
||||
def __get_exec_error_with_rc(self, rc: int) -> str:
|
||||
err_msg: str = self._i18n.translate(key=f'error.common.{rc}')
|
||||
err_msg: str = self._i18n.translate(
|
||||
key=f'error.common.{rc}') # type: ignore
|
||||
if not err_msg:
|
||||
err_msg = f'{self._i18n.translate(key="error.common.-10000")}, '
|
||||
err_msg += f'code={rc}'
|
||||
@ -1029,7 +1087,7 @@ class MIoTClient:
|
||||
if state_old == state_new:
|
||||
continue
|
||||
self._device_list_cache[did]['online'] = state_new
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(did, MIoTDeviceState.OFFLINE, sub.handler_ctx)
|
||||
self.__request_show_devices_changed_notify()
|
||||
@ -1039,8 +1097,8 @@ class MIoTClient:
|
||||
self, group_id: str, state: bool
|
||||
) -> None:
|
||||
_LOGGER.info('local mips state changed, %s, %s', group_id, state)
|
||||
mips: MipsLocalClient = self._mips_local.get(group_id, None)
|
||||
if mips is None:
|
||||
mips = self._mips_local.get(group_id, None)
|
||||
if not mips:
|
||||
_LOGGER.error(
|
||||
'local mips state changed, mips not exist, %s', group_id)
|
||||
return
|
||||
@ -1072,7 +1130,7 @@ class MIoTClient:
|
||||
if state_old == state_new:
|
||||
continue
|
||||
self._device_list_cache[did]['online'] = state_new
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(did, MIoTDeviceState.OFFLINE, sub.handler_ctx)
|
||||
self.__request_show_devices_changed_notify()
|
||||
@ -1089,7 +1147,7 @@ class MIoTClient:
|
||||
handler=self.__on_lan_device_state_changed)
|
||||
for did, info in (
|
||||
await self._miot_lan.get_dev_list_async()).items():
|
||||
self.__on_lan_device_state_changed(
|
||||
await self.__on_lan_device_state_changed(
|
||||
did=did, state=info, ctx=None)
|
||||
_LOGGER.info('lan device list, %s', self._device_list_lan)
|
||||
self._miot_lan.update_devices(devices={
|
||||
@ -1119,7 +1177,7 @@ class MIoTClient:
|
||||
if state_old == state_new:
|
||||
continue
|
||||
self._device_list_cache[did]['online'] = state_new
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(did, MIoTDeviceState.OFFLINE, sub.handler_ctx)
|
||||
self._device_list_lan = {}
|
||||
@ -1149,7 +1207,7 @@ class MIoTClient:
|
||||
if state_old == state_new:
|
||||
return
|
||||
self._device_list_cache[did]['online'] = state_new
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(
|
||||
did, MIoTDeviceState.ONLINE if state_new
|
||||
@ -1205,7 +1263,7 @@ class MIoTClient:
|
||||
if state_old == state_new:
|
||||
return
|
||||
self._device_list_cache[did]['online'] = state_new
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(
|
||||
did, MIoTDeviceState.ONLINE if state_new
|
||||
@ -1249,13 +1307,12 @@ class MIoTClient:
|
||||
async def __load_cache_device_async(self) -> None:
|
||||
"""Load device list from cache."""
|
||||
cache_list: Optional[dict[str, dict]] = await self._storage.load_async(
|
||||
domain='miot_devices',
|
||||
name=f'{self._uid}_{self._cloud_server}',
|
||||
type_=dict)
|
||||
domain='miot_devices', name=f'{self._uid}_{self._cloud_server}',
|
||||
type_=dict) # type: ignore
|
||||
if not cache_list:
|
||||
self.__show_client_error_notify(
|
||||
message=self._i18n.translate(
|
||||
'miot.client.invalid_device_cache'),
|
||||
'miot.client.invalid_device_cache'), # type: ignore
|
||||
notify_key='device_cache')
|
||||
raise MIoTClientError('load device list from cache error')
|
||||
else:
|
||||
@ -1294,7 +1351,7 @@ class MIoTClient:
|
||||
cloud_state_old: Optional[bool] = self._device_list_cloud.get(
|
||||
did, {}).get('online', None)
|
||||
cloud_state_new: Optional[bool] = None
|
||||
device_new: dict = cloud_list.pop(did, None)
|
||||
device_new = cloud_list.pop(did, None)
|
||||
if device_new:
|
||||
cloud_state_new = device_new.get('online', None)
|
||||
# Update cache device info
|
||||
@ -1319,7 +1376,7 @@ class MIoTClient:
|
||||
continue
|
||||
info['online'] = state_new
|
||||
# Call device state changed callback
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(
|
||||
did, MIoTDeviceState.ONLINE if state_new
|
||||
@ -1343,7 +1400,8 @@ class MIoTClient:
|
||||
home_ids=list(self._entry_data.get('home_selected', {}).keys()))
|
||||
if not result and 'devices' not in result:
|
||||
self.__show_client_error_notify(
|
||||
message=self._i18n.translate('miot.client.device_cloud_error'),
|
||||
message=self._i18n.translate(
|
||||
'miot.client.device_cloud_error'), # type: ignore
|
||||
notify_key='device_cloud')
|
||||
return
|
||||
else:
|
||||
@ -1373,8 +1431,7 @@ class MIoTClient:
|
||||
self, dids: list[str]
|
||||
) -> None:
|
||||
_LOGGER.debug('refresh cloud device with dids, %s', dids)
|
||||
cloud_list: dict[str, dict] = (
|
||||
await self._http.get_devices_with_dids_async(dids=dids))
|
||||
cloud_list = await self._http.get_devices_with_dids_async(dids=dids)
|
||||
if cloud_list is None:
|
||||
_LOGGER.error('cloud http get_dev_list_async failed, %s', dids)
|
||||
return
|
||||
@ -1413,11 +1470,11 @@ class MIoTClient:
|
||||
for did, info in self._device_list_cache.items():
|
||||
if did not in filter_dids:
|
||||
continue
|
||||
device_old: dict = self._device_list_gateway.get(did, None)
|
||||
device_old = self._device_list_gateway.get(did, None)
|
||||
gw_state_old = device_old.get(
|
||||
'online', False) if device_old else False
|
||||
gw_state_new: bool = False
|
||||
device_new: dict = gw_list.pop(did, None)
|
||||
device_new = gw_list.pop(did, None)
|
||||
if device_new:
|
||||
# Update gateway device info
|
||||
self._device_list_gateway[did] = {
|
||||
@ -1440,7 +1497,7 @@ class MIoTClient:
|
||||
if state_old == state_new:
|
||||
continue
|
||||
info['online'] = state_new
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(
|
||||
did, MIoTDeviceState.ONLINE if state_new
|
||||
@ -1465,7 +1522,7 @@ class MIoTClient:
|
||||
if state_old == state_new:
|
||||
continue
|
||||
self._device_list_cache[did]['online'] = state_new
|
||||
sub: MipsDeviceState = self._sub_device_state.get(did, None)
|
||||
sub = self._sub_device_state.get(did, None)
|
||||
if sub and sub.handler:
|
||||
sub.handler(
|
||||
did, MIoTDeviceState.ONLINE if state_new
|
||||
@ -1480,7 +1537,7 @@ class MIoTClient:
|
||||
'refresh gw devices with group_id, %s', group_id)
|
||||
# Remove timer
|
||||
self._mips_local_state_changed_timers.pop(group_id, None)
|
||||
mips: MipsLocalClient = self._mips_local.get(group_id, None)
|
||||
mips = self._mips_local.get(group_id, None)
|
||||
if not mips:
|
||||
_LOGGER.error('mips not exist, %s', group_id)
|
||||
return
|
||||
@ -1558,7 +1615,7 @@ class MIoTClient:
|
||||
None)
|
||||
self.__on_prop_msg(params=result, ctx=None)
|
||||
if request_list:
|
||||
_LOGGER.error(
|
||||
_LOGGER.info(
|
||||
'refresh props failed, cloud, %s',
|
||||
list(request_list.keys()))
|
||||
request_list = None
|
||||
@ -1614,7 +1671,7 @@ class MIoTClient:
|
||||
succeed_once = True
|
||||
if succeed_once:
|
||||
return True
|
||||
_LOGGER.error(
|
||||
_LOGGER.info(
|
||||
'refresh props failed, gw, %s', list(request_list.keys()))
|
||||
# Add failed request back to the list
|
||||
self._refresh_props_list.update(request_list)
|
||||
@ -1657,7 +1714,7 @@ class MIoTClient:
|
||||
succeed_once = True
|
||||
if succeed_once:
|
||||
return True
|
||||
_LOGGER.error(
|
||||
_LOGGER.info(
|
||||
'refresh props failed, lan, %s', list(request_list.keys()))
|
||||
# Add failed request back to the list
|
||||
self._refresh_props_list.update(request_list)
|
||||
@ -1689,10 +1746,10 @@ class MIoTClient:
|
||||
if self._refresh_props_timer:
|
||||
self._refresh_props_timer.cancel()
|
||||
self._refresh_props_timer = None
|
||||
_LOGGER.error('refresh props failed, retry count exceed')
|
||||
_LOGGER.info('refresh props failed, retry count exceed')
|
||||
return
|
||||
self._refresh_props_retry_count += 1
|
||||
_LOGGER.error(
|
||||
_LOGGER.info(
|
||||
'refresh props failed, retry, %s', self._refresh_props_retry_count)
|
||||
self._refresh_props_timer = self._main_loop.call_later(
|
||||
3, lambda: self._main_loop.create_task(
|
||||
@ -1700,13 +1757,14 @@ class MIoTClient:
|
||||
|
||||
@final
|
||||
def __show_client_error_notify(
|
||||
self, message: str, notify_key: str = ''
|
||||
self, message: Optional[str], notify_key: str = ''
|
||||
) -> None:
|
||||
if message:
|
||||
|
||||
self._persistence_notify(
|
||||
f'{DOMAIN}{self._uid}{self._cloud_server}{notify_key}error',
|
||||
self._i18n.translate(
|
||||
key='miot.client.xiaomi_home_error_title'),
|
||||
key='miot.client.xiaomi_home_error_title'), # type: ignore
|
||||
self._i18n.translate(
|
||||
key='miot.client.xiaomi_home_error',
|
||||
replace={
|
||||
@ -1714,8 +1772,7 @@ class MIoTClient:
|
||||
'nick_name', DEFAULT_NICK_NAME),
|
||||
'uid': self._uid,
|
||||
'cloud_server': self._cloud_server,
|
||||
'message': message
|
||||
}))
|
||||
'message': message})) # type: ignore
|
||||
else:
|
||||
self._persistence_notify(
|
||||
f'{DOMAIN}{self._uid}{self._cloud_server}{notify_key}error',
|
||||
@ -1781,27 +1838,34 @@ class MIoTClient:
|
||||
key='miot.client.device_list_add',
|
||||
replace={
|
||||
'count': count_add,
|
||||
'message': message_add})
|
||||
'message': message_add}) # type: ignore
|
||||
if 'del' in self._display_devs_notify and count_del:
|
||||
message += self._i18n.translate(
|
||||
key='miot.client.device_list_del',
|
||||
replace={
|
||||
'count': count_del,
|
||||
'message': message_del})
|
||||
'message': message_del}) # type: ignore
|
||||
if 'offline' in self._display_devs_notify and count_offline:
|
||||
message += self._i18n.translate(
|
||||
key='miot.client.device_list_offline',
|
||||
replace={
|
||||
'count': count_offline,
|
||||
'message': message_offline})
|
||||
'message': message_offline}) # type: ignore
|
||||
if message != '':
|
||||
msg_hash = hash(message)
|
||||
if msg_hash == self._display_notify_content_hash:
|
||||
# Notify content no change, return
|
||||
_LOGGER.debug(
|
||||
'device list changed notify content no change, return')
|
||||
return
|
||||
network_status = self._i18n.translate(
|
||||
key='miot.client.network_status_online'
|
||||
if self._network.network_status
|
||||
else 'miot.client.network_status_offline')
|
||||
self._persistence_notify(
|
||||
self.__gen_notify_key('dev_list_changed'),
|
||||
self._i18n.translate('miot.client.device_list_changed_title'),
|
||||
self._i18n.translate(
|
||||
'miot.client.device_list_changed_title'), # type: ignore
|
||||
self._i18n.translate(
|
||||
key='miot.client.device_list_changed',
|
||||
replace={
|
||||
@ -1810,8 +1874,8 @@ class MIoTClient:
|
||||
'uid': self._uid,
|
||||
'cloud_server': self._cloud_server,
|
||||
'network_status': network_status,
|
||||
'message': message
|
||||
}))
|
||||
'message': message})) # type: ignore
|
||||
self._display_notify_content_hash = msg_hash
|
||||
_LOGGER.debug(
|
||||
'show device list changed notify, add %s, del %s, offline %s',
|
||||
count_add, count_del, count_offline)
|
||||
@ -1840,73 +1904,73 @@ async def get_miot_instance_async(
|
||||
) -> MIoTClient:
|
||||
if entry_id is None:
|
||||
raise MIoTClientError('invalid entry_id')
|
||||
miot_client: MIoTClient = None
|
||||
if a := hass.data[DOMAIN].get('miot_clients', {}).get(entry_id, None):
|
||||
miot_client = hass.data[DOMAIN].get('miot_clients', {}).get(entry_id, None)
|
||||
if miot_client:
|
||||
_LOGGER.info('instance exist, %s', entry_id)
|
||||
miot_client = a
|
||||
else:
|
||||
if entry_data is None:
|
||||
raise MIoTClientError('entry data is None')
|
||||
# Get running loop
|
||||
loop: asyncio.AbstractEventLoop = asyncio.get_running_loop()
|
||||
if loop is None:
|
||||
raise MIoTClientError('loop is None')
|
||||
# MIoT network
|
||||
network: Optional[MIoTNetwork] = hass.data[DOMAIN].get(
|
||||
'miot_network', None)
|
||||
if not network:
|
||||
network = MIoTNetwork(loop=loop)
|
||||
hass.data[DOMAIN]['miot_network'] = network
|
||||
await network.init_async(
|
||||
refresh_interval=NETWORK_REFRESH_INTERVAL)
|
||||
_LOGGER.info('create miot_network instance')
|
||||
# MIoT storage
|
||||
storage: Optional[MIoTStorage] = hass.data[DOMAIN].get(
|
||||
'miot_storage', None)
|
||||
if not storage:
|
||||
storage = MIoTStorage(
|
||||
root_path=entry_data['storage_path'], loop=loop)
|
||||
hass.data[DOMAIN]['miot_storage'] = storage
|
||||
_LOGGER.info('create miot_storage instance')
|
||||
# MIoT service
|
||||
mips_service: Optional[MipsService] = hass.data[DOMAIN].get(
|
||||
'mips_service', None)
|
||||
if not mips_service:
|
||||
aiozc = await zeroconf.async_get_async_instance(hass)
|
||||
mips_service: MipsService = MipsService(aiozc=aiozc, loop=loop)
|
||||
hass.data[DOMAIN]['mips_service'] = mips_service
|
||||
await mips_service.init_async()
|
||||
_LOGGER.info('create mips_service instance')
|
||||
# MIoT lan
|
||||
miot_lan: Optional[MIoTLan] = hass.data[DOMAIN].get(
|
||||
'miot_lan', None)
|
||||
if not miot_lan:
|
||||
lan_config = (await storage.load_user_config_async(
|
||||
uid='global_config',
|
||||
cloud_server='all',
|
||||
keys=['net_interfaces', 'enable_subscribe'])) or {}
|
||||
miot_lan = MIoTLan(
|
||||
net_ifs=lan_config.get('net_interfaces', []),
|
||||
network=network,
|
||||
mips_service=mips_service,
|
||||
enable_subscribe=lan_config.get('enable_subscribe', False),
|
||||
loop=loop)
|
||||
hass.data[DOMAIN]['miot_lan'] = miot_lan
|
||||
_LOGGER.info('create miot_lan instance')
|
||||
# MIoT client
|
||||
miot_client = MIoTClient(
|
||||
entry_id=entry_id,
|
||||
entry_data=entry_data,
|
||||
return miot_client
|
||||
# Create new instance
|
||||
if not entry_data:
|
||||
raise MIoTClientError('entry data is None')
|
||||
# Get running loop
|
||||
loop: asyncio.AbstractEventLoop = asyncio.get_running_loop()
|
||||
if not loop:
|
||||
raise MIoTClientError('loop is None')
|
||||
# MIoT storage
|
||||
storage: Optional[MIoTStorage] = hass.data[DOMAIN].get(
|
||||
'miot_storage', None)
|
||||
if not storage:
|
||||
storage = MIoTStorage(
|
||||
root_path=entry_data['storage_path'], loop=loop)
|
||||
hass.data[DOMAIN]['miot_storage'] = storage
|
||||
_LOGGER.info('create miot_storage instance')
|
||||
global_config: dict = await storage.load_user_config_async(
|
||||
uid='global_config', cloud_server='all',
|
||||
keys=['network_detect_addr', 'net_interfaces', 'enable_subscribe'])
|
||||
# MIoT network
|
||||
network_detect_addr: dict = global_config.get('network_detect_addr', {})
|
||||
network: Optional[MIoTNetwork] = hass.data[DOMAIN].get(
|
||||
'miot_network', None)
|
||||
if not network:
|
||||
network = MIoTNetwork(
|
||||
ip_addr_list=network_detect_addr.get('ip', []),
|
||||
url_addr_list=network_detect_addr.get('url', []),
|
||||
refresh_interval=NETWORK_REFRESH_INTERVAL,
|
||||
loop=loop)
|
||||
hass.data[DOMAIN]['miot_network'] = network
|
||||
await network.init_async()
|
||||
_LOGGER.info('create miot_network instance')
|
||||
# MIoT service
|
||||
mips_service: Optional[MipsService] = hass.data[DOMAIN].get(
|
||||
'mips_service', None)
|
||||
if not mips_service:
|
||||
aiozc = await zeroconf.async_get_async_instance(hass)
|
||||
mips_service = MipsService(aiozc=aiozc, loop=loop)
|
||||
hass.data[DOMAIN]['mips_service'] = mips_service
|
||||
await mips_service.init_async()
|
||||
_LOGGER.info('create mips_service instance')
|
||||
# MIoT lan
|
||||
miot_lan: Optional[MIoTLan] = hass.data[DOMAIN].get('miot_lan', None)
|
||||
if not miot_lan:
|
||||
miot_lan = MIoTLan(
|
||||
net_ifs=global_config.get('net_interfaces', []),
|
||||
network=network,
|
||||
storage=storage,
|
||||
mips_service=mips_service,
|
||||
miot_lan=miot_lan,
|
||||
loop=loop
|
||||
)
|
||||
miot_client.persistent_notify = persistent_notify
|
||||
hass.data[DOMAIN]['miot_clients'].setdefault(entry_id, miot_client)
|
||||
_LOGGER.info(
|
||||
'new miot_client instance, %s, %s', entry_id, entry_data)
|
||||
await miot_client.init_async()
|
||||
|
||||
enable_subscribe=global_config.get('enable_subscribe', False),
|
||||
loop=loop)
|
||||
hass.data[DOMAIN]['miot_lan'] = miot_lan
|
||||
_LOGGER.info('create miot_lan instance')
|
||||
# MIoT client
|
||||
miot_client = MIoTClient(
|
||||
entry_id=entry_id,
|
||||
entry_data=entry_data,
|
||||
network=network,
|
||||
storage=storage,
|
||||
mips_service=mips_service,
|
||||
miot_lan=miot_lan,
|
||||
loop=loop
|
||||
)
|
||||
miot_client.persistent_notify = persistent_notify
|
||||
hass.data[DOMAIN]['miot_clients'].setdefault(entry_id, miot_client)
|
||||
_LOGGER.info('new miot_client instance, %s, %s', entry_id, entry_data)
|
||||
await miot_client.init_async()
|
||||
return miot_client
|
||||
|
@ -47,6 +47,7 @@ MIoT http client.
|
||||
"""
|
||||
import asyncio
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
@ -75,10 +76,12 @@ class MIoTOauthClient:
|
||||
_oauth_host: str
|
||||
_client_id: int
|
||||
_redirect_url: str
|
||||
_device_id: str
|
||||
_state: str
|
||||
|
||||
def __init__(
|
||||
self, client_id: str, redirect_url: str, cloud_server: str,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
uuid: str, loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> None:
|
||||
self._main_loop = loop or asyncio.get_running_loop()
|
||||
if client_id is None or client_id.strip() == '':
|
||||
@ -87,6 +90,8 @@ class MIoTOauthClient:
|
||||
raise MIoTOauthError('invalid redirect_url')
|
||||
if not cloud_server:
|
||||
raise MIoTOauthError('invalid cloud_server')
|
||||
if not uuid:
|
||||
raise MIoTOauthError('invalid uuid')
|
||||
|
||||
self._client_id = int(client_id)
|
||||
self._redirect_url = redirect_url
|
||||
@ -94,8 +99,15 @@ class MIoTOauthClient:
|
||||
self._oauth_host = DEFAULT_OAUTH2_API_HOST
|
||||
else:
|
||||
self._oauth_host = f'{cloud_server}.{DEFAULT_OAUTH2_API_HOST}'
|
||||
self._device_id = f'ha.{uuid}'
|
||||
self._state = hashlib.sha1(
|
||||
f'd={self._device_id}'.encode('utf-8')).hexdigest()
|
||||
self._session = aiohttp.ClientSession(loop=self._main_loop)
|
||||
|
||||
@property
|
||||
def state(self) -> str:
|
||||
return self._state
|
||||
|
||||
async def deinit_async(self) -> None:
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
@ -132,6 +144,8 @@ class MIoTOauthClient:
|
||||
'redirect_uri': redirect_url or self._redirect_url,
|
||||
'client_id': self._client_id,
|
||||
'response_type': 'code',
|
||||
'device_id': self._device_id,
|
||||
'state': self._state
|
||||
}
|
||||
if state:
|
||||
params['state'] = state
|
||||
@ -166,7 +180,7 @@ class MIoTOauthClient:
|
||||
key in res_obj['result']
|
||||
for key in ['access_token', 'refresh_token', 'expires_in'])
|
||||
):
|
||||
raise MIoTOauthError(f'invalid http response, {http_res.text}')
|
||||
raise MIoTOauthError(f'invalid http response, {res_str}')
|
||||
|
||||
return {
|
||||
**res_obj['result'],
|
||||
@ -191,6 +205,7 @@ class MIoTOauthClient:
|
||||
'client_id': self._client_id,
|
||||
'redirect_uri': self._redirect_url,
|
||||
'code': code,
|
||||
'device_id': self._device_id
|
||||
})
|
||||
|
||||
async def refresh_access_token_async(self, refresh_token: str) -> dict:
|
||||
@ -224,20 +239,20 @@ class MIoTHttpClient:
|
||||
_client_id: str
|
||||
_access_token: str
|
||||
|
||||
_get_prop_timer: asyncio.TimerHandle
|
||||
_get_prop_list: dict[str, dict[str, asyncio.Future | str | bool]]
|
||||
_get_prop_timer: Optional[asyncio.TimerHandle]
|
||||
_get_prop_list: dict[str, dict]
|
||||
|
||||
def __init__(
|
||||
self, cloud_server: str, client_id: str, access_token: str,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> None:
|
||||
self._main_loop = loop or asyncio.get_running_loop()
|
||||
self._host = None
|
||||
self._base_url = None
|
||||
self._client_id = None
|
||||
self._access_token = None
|
||||
self._host = DEFAULT_OAUTH2_API_HOST
|
||||
self._base_url = ''
|
||||
self._client_id = ''
|
||||
self._access_token = ''
|
||||
|
||||
self._get_prop_timer: asyncio.TimerHandle = None
|
||||
self._get_prop_timer = None
|
||||
self._get_prop_list = {}
|
||||
|
||||
if (
|
||||
@ -258,8 +273,9 @@ class MIoTHttpClient:
|
||||
self._get_prop_timer.cancel()
|
||||
self._get_prop_timer = None
|
||||
for item in self._get_prop_list.values():
|
||||
fut: asyncio.Future = item.get('fut')
|
||||
fut.cancel()
|
||||
fut: Optional[asyncio.Future] = item.get('fut', None)
|
||||
if fut:
|
||||
fut.cancel()
|
||||
self._get_prop_list.clear()
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
@ -270,9 +286,7 @@ class MIoTHttpClient:
|
||||
access_token: Optional[str] = None
|
||||
) -> None:
|
||||
if isinstance(cloud_server, str):
|
||||
if cloud_server == 'cn':
|
||||
self._host = DEFAULT_OAUTH2_API_HOST
|
||||
else:
|
||||
if cloud_server != 'cn':
|
||||
self._host = f'{cloud_server}.{DEFAULT_OAUTH2_API_HOST}'
|
||||
self._base_url = f'https://{self._host}'
|
||||
if isinstance(client_id, str):
|
||||
@ -350,8 +364,8 @@ class MIoTHttpClient:
|
||||
async def get_user_info_async(self) -> dict:
|
||||
http_res = await self._session.get(
|
||||
url='https://open.account.xiaomi.com/user/profile',
|
||||
params={'clientId': self._client_id,
|
||||
'token': self._access_token},
|
||||
params={
|
||||
'clientId': self._client_id, 'token': self._access_token},
|
||||
headers={'content-type': 'application/x-www-form-urlencoded'},
|
||||
timeout=MIHOME_HTTP_API_TIMEOUT
|
||||
)
|
||||
@ -368,7 +382,7 @@ class MIoTHttpClient:
|
||||
|
||||
return res_obj['data']
|
||||
|
||||
async def get_central_cert_async(self, csr: str) -> Optional[str]:
|
||||
async def get_central_cert_async(self, csr: str) -> str:
|
||||
if not isinstance(csr, str):
|
||||
raise MIoTHttpError('invalid params')
|
||||
|
||||
@ -386,7 +400,9 @@ class MIoTHttpClient:
|
||||
|
||||
return cert
|
||||
|
||||
async def __get_dev_room_page_async(self, max_id: str = None) -> dict:
|
||||
async def __get_dev_room_page_async(
|
||||
self, max_id: Optional[str] = None
|
||||
) -> dict:
|
||||
res_obj = await self.__mihome_api_post_async(
|
||||
url_path='/app/v2/homeroom/get_dev_room_page',
|
||||
data={
|
||||
@ -442,7 +458,7 @@ class MIoTHttpClient:
|
||||
if 'result' not in res_obj:
|
||||
raise MIoTHttpError('invalid response result')
|
||||
|
||||
uid: str = None
|
||||
uid: Optional[str] = None
|
||||
home_infos: dict = {}
|
||||
for device_source in ['homelist', 'share_home_list']:
|
||||
home_infos.setdefault(device_source, {})
|
||||
@ -507,7 +523,7 @@ class MIoTHttpClient:
|
||||
return (await self.get_homeinfos_async()).get('uid', None)
|
||||
|
||||
async def __get_device_list_page_async(
|
||||
self, dids: list[str], start_did: str = None
|
||||
self, dids: list[str], start_did: Optional[str] = None
|
||||
) -> dict[str, dict]:
|
||||
req_data: dict = {
|
||||
'limit': 200,
|
||||
@ -530,9 +546,18 @@ class MIoTHttpClient:
|
||||
name = device.get('name', None)
|
||||
urn = device.get('spec_type', None)
|
||||
model = device.get('model', None)
|
||||
if did is None or name is None or urn is None or model is None:
|
||||
_LOGGER.error(
|
||||
'get_device_list, cloud, invalid device, %s', device)
|
||||
if did is None or name is None:
|
||||
_LOGGER.info(
|
||||
'invalid device, cloud, %s', device)
|
||||
continue
|
||||
if urn is None or model is None:
|
||||
_LOGGER.info(
|
||||
'missing the urn|model field, cloud, %s', device)
|
||||
continue
|
||||
if did.startswith('miwifi.'):
|
||||
# The miwifi.* routers defined SPEC functions, but none of them
|
||||
# were implemented.
|
||||
_LOGGER.info('ignore miwifi.* device, cloud, %s', did)
|
||||
continue
|
||||
device_infos[did] = {
|
||||
'did': did,
|
||||
@ -575,9 +600,9 @@ class MIoTHttpClient:
|
||||
|
||||
async def get_devices_with_dids_async(
|
||||
self, dids: list[str]
|
||||
) -> dict[str, dict]:
|
||||
) -> Optional[dict[str, dict]]:
|
||||
results: list[dict[str, dict]] = await asyncio.gather(
|
||||
*[self.__get_device_list_page_async(dids[index:index+150])
|
||||
*[self.__get_device_list_page_async(dids=dids[index:index+150])
|
||||
for index in range(0, len(dids), 150)])
|
||||
devices = {}
|
||||
for result in results:
|
||||
@ -587,7 +612,7 @@ class MIoTHttpClient:
|
||||
return devices
|
||||
|
||||
async def get_devices_async(
|
||||
self, home_ids: list[str] = None
|
||||
self, home_ids: Optional[list[str]] = None
|
||||
) -> dict[str, dict]:
|
||||
homeinfos = await self.get_homeinfos_async()
|
||||
homes: dict[str, dict[str, Any]] = {}
|
||||
@ -627,12 +652,13 @@ class MIoTHttpClient:
|
||||
'group_id': group_id
|
||||
} for did in room_info.get('dids', [])})
|
||||
dids = sorted(list(devices.keys()))
|
||||
results: dict[str, dict] = await self.get_devices_with_dids_async(
|
||||
dids=dids)
|
||||
results = await self.get_devices_with_dids_async(dids=dids)
|
||||
if results is None:
|
||||
raise MIoTHttpError('get devices failed')
|
||||
for did in dids:
|
||||
if did not in results:
|
||||
devices.pop(did, None)
|
||||
_LOGGER.error('get device info failed, %s', did)
|
||||
_LOGGER.info('get device info failed, %s', did)
|
||||
continue
|
||||
devices[did].update(results[did])
|
||||
# Whether sub devices
|
||||
@ -706,7 +732,7 @@ class MIoTHttpClient:
|
||||
key = f'{result["did"]}.{result["siid"]}.{result["piid"]}'
|
||||
prop_obj = self._get_prop_list.pop(key, None)
|
||||
if prop_obj is None:
|
||||
_LOGGER.error('get prop error, key not exists, %s', result)
|
||||
_LOGGER.info('get prop error, key not exists, %s', result)
|
||||
continue
|
||||
prop_obj['fut'].set_result(result['value'])
|
||||
props_req.remove(key)
|
||||
@ -717,8 +743,8 @@ class MIoTHttpClient:
|
||||
continue
|
||||
prop_obj['fut'].set_result(None)
|
||||
if props_req:
|
||||
_LOGGER.error(
|
||||
'get prop from cloud failed, %s, %s', len(key), props_req)
|
||||
_LOGGER.info(
|
||||
'get prop from cloud failed, %s', props_req)
|
||||
|
||||
if self._get_prop_list:
|
||||
self._get_prop_timer = self._main_loop.call_later(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -72,8 +72,11 @@ class MIoTErrorCode(Enum):
|
||||
# MIoT ev error code, -10080
|
||||
# Mips service error code, -10090
|
||||
# Config flow error code, -10100
|
||||
CODE_CONFIG_INVALID_INPUT = -10100
|
||||
CODE_CONFIG_INVALID_STATE = -10101
|
||||
# Options flow error code , -10110
|
||||
# MIoT lan error code, -10120
|
||||
CODE_LAN_UNAVAILABLE = -10120
|
||||
|
||||
|
||||
class MIoTError(Exception):
|
||||
@ -141,3 +144,7 @@ class MIoTConfigError(MIoTError):
|
||||
|
||||
class MIoTOptionsError(MIoTError):
|
||||
...
|
||||
|
||||
|
||||
class MIoTLanError(MIoTError):
|
||||
...
|
||||
|
@ -1,324 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2024 Xiaomi Corporation.
|
||||
|
||||
The ownership and intellectual property rights of Xiaomi Home Assistant
|
||||
Integration and related Xiaomi cloud service API interface provided under this
|
||||
license, including source code and object code (collectively, "Licensed Work"),
|
||||
are owned by Xiaomi. Subject to the terms and conditions of this License, Xiaomi
|
||||
hereby grants you a personal, limited, non-exclusive, non-transferable,
|
||||
non-sublicensable, and royalty-free license to reproduce, use, modify, and
|
||||
distribute the Licensed Work only for your use of Home Assistant for
|
||||
non-commercial purposes. For the avoidance of doubt, Xiaomi does not authorize
|
||||
you to use the Licensed Work for any other purpose, including but not limited
|
||||
to use Licensed Work to develop applications (APP), Web services, and other
|
||||
forms of software.
|
||||
|
||||
You may reproduce and distribute copies of the Licensed Work, with or without
|
||||
modifications, whether in source or object form, provided that you must give
|
||||
any other recipients of the Licensed Work a copy of this License and retain all
|
||||
copyright and disclaimers.
|
||||
|
||||
Xiaomi provides the Licensed Work on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied, including, without
|
||||
limitation, any warranties, undertakes, or conditions of TITLE, NO ERROR OR
|
||||
OMISSION, CONTINUITY, RELIABILITY, NON-INFRINGEMENT, MERCHANTABILITY, or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. In any event, you are solely responsible
|
||||
for any direct, indirect, special, incidental, or consequential damages or
|
||||
losses arising from the use or inability to use the Licensed Work.
|
||||
|
||||
Xiaomi reserves all rights not expressly granted to you in this License.
|
||||
Except for the rights expressly granted by Xiaomi under this License, Xiaomi
|
||||
does not authorize you in any form to use the trademarks, copyrights, or other
|
||||
forms of intellectual property rights of Xiaomi and its affiliates, including,
|
||||
without limitation, without obtaining other written permission from Xiaomi, you
|
||||
shall not use "Xiaomi", "Mijia" and other words related to Xiaomi or words that
|
||||
may make the public associate with Xiaomi in any form to publicize or promote
|
||||
the software or hardware devices that use the Licensed Work.
|
||||
|
||||
Xiaomi has the right to immediately terminate all your authorization under this
|
||||
License in the event:
|
||||
1. You assert patent invalidation, litigation, or other claims against patents
|
||||
or other intellectual property rights of Xiaomi or its affiliates; or,
|
||||
2. You make, have made, manufacture, sell, or offer to sell products that knock
|
||||
off Xiaomi or its affiliates' products.
|
||||
|
||||
MIoT event loop.
|
||||
"""
|
||||
import selectors
|
||||
import heapq
|
||||
import time
|
||||
import traceback
|
||||
from typing import Any, Callable, TypeVar
|
||||
import logging
|
||||
import threading
|
||||
|
||||
# pylint: disable=relative-beyond-top-level
|
||||
from .miot_error import MIoTEvError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
TimeoutHandle = TypeVar('TimeoutHandle')
|
||||
|
||||
|
||||
class MIoTFdHandler:
|
||||
"""File descriptor handler."""
|
||||
fd: int
|
||||
read_handler: Callable[[Any], None]
|
||||
read_handler_ctx: Any
|
||||
write_handler: Callable[[Any], None]
|
||||
write_handler_ctx: Any
|
||||
|
||||
def __init__(
|
||||
self, fd: int,
|
||||
read_handler: Callable[[Any], None] = None,
|
||||
read_handler_ctx: Any = None,
|
||||
write_handler: Callable[[Any], None] = None,
|
||||
write_handler_ctx: Any = None
|
||||
) -> None:
|
||||
self.fd = fd
|
||||
self.read_handler = read_handler
|
||||
self.read_handler_ctx = read_handler_ctx
|
||||
self.write_handler = write_handler
|
||||
self.write_handler_ctx = write_handler_ctx
|
||||
|
||||
|
||||
class MIoTTimeout:
|
||||
"""Timeout handler."""
|
||||
key: TimeoutHandle
|
||||
target: int
|
||||
handler: Callable[[Any], None]
|
||||
handler_ctx: Any
|
||||
|
||||
def __init__(
|
||||
self, key: str = None, target: int = None,
|
||||
handler: Callable[[Any], None] = None,
|
||||
handler_ctx: Any = None
|
||||
) -> None:
|
||||
self.key = key
|
||||
self.target = target
|
||||
self.handler = handler
|
||||
self.handler_ctx = handler_ctx
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.target < other.target
|
||||
|
||||
|
||||
class MIoTEventLoop:
|
||||
"""MIoT event loop."""
|
||||
_poll_fd: selectors.DefaultSelector
|
||||
|
||||
_fd_handlers: dict[str, MIoTFdHandler]
|
||||
|
||||
_timer_heap: list[MIoTTimeout]
|
||||
_timer_handlers: dict[str, MIoTTimeout]
|
||||
_timer_handle_seed: int
|
||||
|
||||
# Label if the current fd handler is freed inside a read handler to
|
||||
# avoid invalid reading.
|
||||
_fd_handler_freed_in_read_handler: bool
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._poll_fd = selectors.DefaultSelector()
|
||||
self._timer_heap = []
|
||||
self._timer_handlers = {}
|
||||
self._timer_handle_seed = 1
|
||||
self._fd_handlers = {}
|
||||
self._fd_handler_freed_in_read_handler = False
|
||||
|
||||
def loop_forever(self) -> None:
|
||||
"""Run an event loop in current thread."""
|
||||
next_timeout: int
|
||||
while True:
|
||||
next_timeout = 0
|
||||
# Handle timer
|
||||
now_ms: int = self.__get_monotonic_ms
|
||||
while len(self._timer_heap) > 0:
|
||||
timer: MIoTTimeout = self._timer_heap[0]
|
||||
if timer is None:
|
||||
break
|
||||
if timer.target <= now_ms:
|
||||
heapq.heappop(self._timer_heap)
|
||||
del self._timer_handlers[timer.key]
|
||||
if timer.handler:
|
||||
timer.handler(timer.handler_ctx)
|
||||
else:
|
||||
next_timeout = timer.target-now_ms
|
||||
break
|
||||
# Are there any files to listen to
|
||||
if next_timeout == 0 and self._fd_handlers:
|
||||
next_timeout = None # None == infinite
|
||||
# Wait for timers & fds
|
||||
if next_timeout == 0:
|
||||
# Neither timer nor fds exist, exit loop
|
||||
break
|
||||
# Handle fd event
|
||||
events = self._poll_fd.select(
|
||||
timeout=next_timeout/1000.0 if next_timeout else next_timeout)
|
||||
for key, mask in events:
|
||||
fd_handler: MIoTFdHandler = key.data
|
||||
if fd_handler is None:
|
||||
continue
|
||||
self._fd_handler_freed_in_read_handler = False
|
||||
fd_key = str(id(fd_handler.fd))
|
||||
if fd_key not in self._fd_handlers:
|
||||
continue
|
||||
if (
|
||||
mask & selectors.EVENT_READ > 0
|
||||
and fd_handler.read_handler
|
||||
):
|
||||
fd_handler.read_handler(fd_handler.read_handler_ctx)
|
||||
if (
|
||||
mask & selectors.EVENT_WRITE > 0
|
||||
and self._fd_handler_freed_in_read_handler is False
|
||||
and fd_handler.write_handler
|
||||
):
|
||||
fd_handler.write_handler(fd_handler.write_handler_ctx)
|
||||
|
||||
def loop_stop(self) -> None:
|
||||
"""Stop the event loop."""
|
||||
if self._poll_fd:
|
||||
self._poll_fd.close()
|
||||
self._poll_fd = None
|
||||
self._fd_handlers = {}
|
||||
self._timer_heap = []
|
||||
self._timer_handlers = {}
|
||||
|
||||
def set_timeout(
|
||||
self, timeout_ms: int, handler: Callable[[Any], None],
|
||||
handler_ctx: Any = None
|
||||
) -> TimeoutHandle:
|
||||
"""Set a timer."""
|
||||
if timeout_ms is None or handler is None:
|
||||
raise MIoTEvError('invalid params')
|
||||
new_timeout: MIoTTimeout = MIoTTimeout()
|
||||
new_timeout.key = self.__get_next_timeout_handle
|
||||
new_timeout.target = self.__get_monotonic_ms + timeout_ms
|
||||
new_timeout.handler = handler
|
||||
new_timeout.handler_ctx = handler_ctx
|
||||
heapq.heappush(self._timer_heap, new_timeout)
|
||||
self._timer_handlers[new_timeout.key] = new_timeout
|
||||
return new_timeout.key
|
||||
|
||||
def clear_timeout(self, timer_key: TimeoutHandle) -> None:
|
||||
"""Stop and remove the timer."""
|
||||
if timer_key is None:
|
||||
return
|
||||
timer: MIoTTimeout = self._timer_handlers.pop(timer_key, None)
|
||||
if timer:
|
||||
self._timer_heap = list(self._timer_heap)
|
||||
self._timer_heap.remove(timer)
|
||||
heapq.heapify(self._timer_heap)
|
||||
|
||||
def set_read_handler(
|
||||
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any = None
|
||||
) -> bool:
|
||||
"""Set a read handler for a file descriptor.
|
||||
|
||||
Returns:
|
||||
bool: True, success. False, failed.
|
||||
"""
|
||||
self.__set_handler(
|
||||
fd, is_read=True, handler=handler, handler_ctx=handler_ctx)
|
||||
|
||||
def set_write_handler(
|
||||
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any = None
|
||||
) -> bool:
|
||||
"""Set a write handler for a file descriptor.
|
||||
|
||||
Returns:
|
||||
bool: True, success. False, failed.
|
||||
"""
|
||||
self.__set_handler(
|
||||
fd, is_read=False, handler=handler, handler_ctx=handler_ctx)
|
||||
|
||||
def __set_handler(
|
||||
self, fd, is_read: bool, handler: Callable[[Any], None],
|
||||
handler_ctx: Any = None
|
||||
) -> bool:
|
||||
"""Set a handler."""
|
||||
if fd is None:
|
||||
raise MIoTEvError('invalid params')
|
||||
|
||||
if not self._poll_fd:
|
||||
raise MIoTEvError('event loop not started')
|
||||
|
||||
fd_key: str = str(id(fd))
|
||||
fd_handler = self._fd_handlers.get(fd_key, None)
|
||||
|
||||
if fd_handler is None:
|
||||
fd_handler = MIoTFdHandler(fd=fd)
|
||||
fd_handler.fd = fd
|
||||
self._fd_handlers[fd_key] = fd_handler
|
||||
|
||||
read_handler_existed = fd_handler.read_handler is not None
|
||||
write_handler_existed = fd_handler.write_handler is not None
|
||||
if is_read is True:
|
||||
fd_handler.read_handler = handler
|
||||
fd_handler.read_handler_ctx = handler_ctx
|
||||
else:
|
||||
fd_handler.write_handler = handler
|
||||
fd_handler.write_handler_ctx = handler_ctx
|
||||
|
||||
if fd_handler.read_handler is None and fd_handler.write_handler is None:
|
||||
# Remove from epoll and map
|
||||
try:
|
||||
self._poll_fd.unregister(fd)
|
||||
except (KeyError, ValueError, OSError) as e:
|
||||
del e
|
||||
self._fd_handlers.pop(fd_key, None)
|
||||
# May be inside a read handler, if not, this has no effect
|
||||
self._fd_handler_freed_in_read_handler = True
|
||||
elif read_handler_existed is False and write_handler_existed is False:
|
||||
# Add to epoll
|
||||
events = 0x0
|
||||
if fd_handler.read_handler:
|
||||
events |= selectors.EVENT_READ
|
||||
if fd_handler.write_handler:
|
||||
events |= selectors.EVENT_WRITE
|
||||
try:
|
||||
self._poll_fd.register(fd, events=events, data=fd_handler)
|
||||
except (KeyError, ValueError, OSError) as e:
|
||||
_LOGGER.error(
|
||||
'%s, register fd, error, %s, %s, %s, %s, %s',
|
||||
threading.current_thread().name,
|
||||
'read' if is_read else 'write',
|
||||
fd_key, handler, e, traceback.format_exc())
|
||||
self._fd_handlers.pop(fd_key, None)
|
||||
return False
|
||||
elif (
|
||||
read_handler_existed != (fd_handler.read_handler is not None)
|
||||
or write_handler_existed != (fd_handler.write_handler is not None)
|
||||
):
|
||||
# Modify epoll
|
||||
events = 0x0
|
||||
if fd_handler.read_handler:
|
||||
events |= selectors.EVENT_READ
|
||||
if fd_handler.write_handler:
|
||||
events |= selectors.EVENT_WRITE
|
||||
try:
|
||||
self._poll_fd.modify(fd, events=events, data=fd_handler)
|
||||
except (KeyError, ValueError, OSError) as e:
|
||||
_LOGGER.error(
|
||||
'%s, modify fd, error, %s, %s, %s, %s, %s',
|
||||
threading.current_thread().name,
|
||||
'read' if is_read else 'write',
|
||||
fd_key, handler, e, traceback.format_exc())
|
||||
self._fd_handlers.pop(fd_key, None)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def __get_next_timeout_handle(self) -> str:
|
||||
# Get next timeout handle, that is not larger than the maximum
|
||||
# value of UINT64 type.
|
||||
self._timer_handle_seed += 1
|
||||
# uint64 max
|
||||
self._timer_handle_seed %= 0xFFFFFFFFFFFFFFFF
|
||||
return str(self._timer_handle_seed)
|
||||
|
||||
@property
|
||||
def __get_monotonic_ms(self) -> int:
|
||||
"""Get monotonic ms timestamp."""
|
||||
return int(time.monotonic()*1000)
|
@ -48,7 +48,7 @@ MIoT internationalization translation.
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
# pylint: disable=relative-beyond-top-level
|
||||
from .common import load_json_file
|
||||
@ -98,7 +98,7 @@ class MIoTI18n:
|
||||
|
||||
def translate(
|
||||
self, key: str, replace: Optional[dict[str, str]] = None
|
||||
) -> str | dict | None:
|
||||
) -> Union[str, dict, None]:
|
||||
result = self._data
|
||||
for item in key.split('.'):
|
||||
if item not in result:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -117,7 +117,7 @@ class MipsServiceData:
|
||||
self.type = service_info.type
|
||||
self.server = service_info.server or ''
|
||||
# Parse profile
|
||||
self.did = str(int.from_bytes(self.profile_bin[1:9]))
|
||||
self.did = str(int.from_bytes(self.profile_bin[1:9], byteorder='big'))
|
||||
self.group_id = binascii.hexlify(
|
||||
self.profile_bin[9:17][::-1]).decode('utf-8')
|
||||
self.role = int(self.profile_bin[20] >> 4)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -52,7 +52,8 @@ import socket
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
import subprocess
|
||||
from typing import Callable, Optional
|
||||
from typing import Callable, Coroutine, Optional
|
||||
import aiohttp
|
||||
import psutil
|
||||
import ipaddress
|
||||
|
||||
@ -77,38 +78,54 @@ class NetworkInfo:
|
||||
|
||||
class MIoTNetwork:
|
||||
"""MIoT network utilities."""
|
||||
PING_ADDRESS_LIST = [
|
||||
_IP_ADDRESS_LIST: list[str] = [
|
||||
'1.2.4.8', # CNNIC sDNS
|
||||
'8.8.8.8', # Google Public DNS
|
||||
'233.5.5.5', # AliDNS
|
||||
'1.1.1.1', # Cloudflare DNS
|
||||
'114.114.114.114', # 114 DNS
|
||||
'208.67.222.222', # OpenDNS
|
||||
'9.9.9.9', # Quad9 DNS
|
||||
'9.9.9.9' # Quad9
|
||||
]
|
||||
_URL_ADDRESS_LIST: list[str] = [
|
||||
'https://www.bing.com',
|
||||
'https://www.google.com',
|
||||
'https://www.baidu.com'
|
||||
]
|
||||
_REFRESH_INTERVAL = 30
|
||||
_DETECT_TIMEOUT = 6
|
||||
|
||||
_main_loop: asyncio.AbstractEventLoop
|
||||
|
||||
_ip_addr_map: dict[str, float]
|
||||
_http_addr_map: dict[str, float]
|
||||
_http_session: aiohttp.ClientSession
|
||||
|
||||
_refresh_interval: int
|
||||
_refresh_task: asyncio.Task
|
||||
_refresh_timer: asyncio.TimerHandle
|
||||
_refresh_task: Optional[asyncio.Task]
|
||||
_refresh_timer: Optional[asyncio.TimerHandle]
|
||||
|
||||
_network_status: bool
|
||||
_network_info: dict[str, NetworkInfo]
|
||||
|
||||
_sub_list_network_status: dict[str, Callable[[bool], asyncio.Future]]
|
||||
_sub_list_network_status: dict[str, Callable[[bool], Coroutine]]
|
||||
_sub_list_network_info: dict[str, Callable[[
|
||||
InterfaceStatus, NetworkInfo], asyncio.Future]]
|
||||
|
||||
_ping_address_priority: int
|
||||
|
||||
InterfaceStatus, NetworkInfo], Coroutine]]
|
||||
_done_event: asyncio.Event
|
||||
|
||||
def __init__(
|
||||
self, loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
self,
|
||||
ip_addr_list: Optional[list[str]] = None,
|
||||
url_addr_list: Optional[list[str]] = None,
|
||||
refresh_interval: Optional[int] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> None:
|
||||
self._main_loop = loop or asyncio.get_running_loop()
|
||||
self._ip_addr_map = {
|
||||
ip: self._DETECT_TIMEOUT for ip in
|
||||
ip_addr_list or self._IP_ADDRESS_LIST}
|
||||
self._http_addr_map = {
|
||||
url: self._DETECT_TIMEOUT for url in
|
||||
url_addr_list or self._URL_ADDRESS_LIST}
|
||||
self._http_session = aiohttp.ClientSession()
|
||||
self._refresh_interval = refresh_interval or self._REFRESH_INTERVAL
|
||||
|
||||
self._refresh_interval = None
|
||||
self._refresh_task = None
|
||||
self._refresh_timer = None
|
||||
|
||||
@ -118,10 +135,28 @@ class MIoTNetwork:
|
||||
self._sub_list_network_status = {}
|
||||
self._sub_list_network_info = {}
|
||||
|
||||
self._ping_address_priority = 0
|
||||
|
||||
self._done_event = asyncio.Event()
|
||||
|
||||
async def init_async(self) -> bool:
|
||||
self.__refresh_timer_handler()
|
||||
# MUST get network info before starting
|
||||
return await self._done_event.wait()
|
||||
|
||||
async def deinit_async(self) -> None:
|
||||
if self._refresh_task:
|
||||
self._refresh_task.cancel()
|
||||
self._refresh_task = None
|
||||
if self._refresh_timer:
|
||||
self._refresh_timer.cancel()
|
||||
self._refresh_timer = None
|
||||
await self._http_session.close()
|
||||
|
||||
self._network_status = False
|
||||
self._network_info.clear()
|
||||
self._sub_list_network_status.clear()
|
||||
self._sub_list_network_info.clear()
|
||||
self._done_event.clear()
|
||||
|
||||
@property
|
||||
def network_status(self) -> bool:
|
||||
return self._network_status
|
||||
@ -130,23 +165,28 @@ class MIoTNetwork:
|
||||
def network_info(self) -> dict[str, NetworkInfo]:
|
||||
return self._network_info
|
||||
|
||||
async def deinit_async(self) -> None:
|
||||
if self._refresh_task:
|
||||
self._refresh_task.cancel()
|
||||
self._refresh_task = None
|
||||
if self._refresh_timer:
|
||||
self._refresh_timer.cancel()
|
||||
self._refresh_timer = None
|
||||
|
||||
self._refresh_interval = None
|
||||
self._network_status = False
|
||||
self._network_info.clear()
|
||||
self._sub_list_network_status.clear()
|
||||
self._sub_list_network_info.clear()
|
||||
self._done_event.clear()
|
||||
async def update_addr_list_async(
|
||||
self,
|
||||
ip_addr_list: Optional[list[str]] = None,
|
||||
url_addr_list: Optional[list[str]] = None,
|
||||
) -> None:
|
||||
new_ip_map: dict = {}
|
||||
for ip in ip_addr_list or self._IP_ADDRESS_LIST:
|
||||
if ip in self._ip_addr_map:
|
||||
new_ip_map[ip] = self._ip_addr_map[ip]
|
||||
else:
|
||||
new_ip_map[ip] = self._DETECT_TIMEOUT
|
||||
self._ip_addr_map = new_ip_map
|
||||
new_url_map: dict = {}
|
||||
for url in url_addr_list or self._URL_ADDRESS_LIST:
|
||||
if url in self._http_addr_map:
|
||||
new_url_map[url] = self._http_addr_map[url]
|
||||
else:
|
||||
new_url_map[url] = self._DETECT_TIMEOUT
|
||||
self._http_addr_map = new_url_map
|
||||
|
||||
def sub_network_status(
|
||||
self, key: str, handler: Callable[[bool], asyncio.Future]
|
||||
self, key: str, handler: Callable[[bool], Coroutine]
|
||||
) -> None:
|
||||
self._sub_list_network_status[key] = handler
|
||||
|
||||
@ -155,65 +195,114 @@ class MIoTNetwork:
|
||||
|
||||
def sub_network_info(
|
||||
self, key: str,
|
||||
handler: Callable[[InterfaceStatus, NetworkInfo], asyncio.Future]
|
||||
handler: Callable[[InterfaceStatus, NetworkInfo], Coroutine]
|
||||
) -> None:
|
||||
self._sub_list_network_info[key] = handler
|
||||
|
||||
def unsub_network_info(self, key: str) -> None:
|
||||
self._sub_list_network_info.pop(key, None)
|
||||
|
||||
async def init_async(self, refresh_interval: int = 30) -> bool:
|
||||
self._refresh_interval = refresh_interval
|
||||
self.__refresh_timer_handler()
|
||||
# MUST get network info before starting
|
||||
return await self._done_event.wait()
|
||||
|
||||
async def refresh_async(self) -> None:
|
||||
self.__refresh_timer_handler()
|
||||
|
||||
async def get_network_status_async(self, timeout: int = 6) -> bool:
|
||||
return await self._main_loop.run_in_executor(
|
||||
None, self.__get_network_status, False, timeout)
|
||||
async def get_network_status_async(self) -> bool:
|
||||
try:
|
||||
ip_addr: str = ''
|
||||
ip_ts: float = self._DETECT_TIMEOUT
|
||||
for ip, ts in self._ip_addr_map.items():
|
||||
if ts < ip_ts:
|
||||
ip_addr = ip
|
||||
ip_ts = ts
|
||||
if (
|
||||
ip_ts < self._DETECT_TIMEOUT
|
||||
and await self.ping_multi_async(ip_list=[ip_addr])
|
||||
):
|
||||
return True
|
||||
url_addr: str = ''
|
||||
url_ts: float = self._DETECT_TIMEOUT
|
||||
for http, ts in self._http_addr_map.items():
|
||||
if ts < url_ts:
|
||||
url_addr = http
|
||||
url_ts = ts
|
||||
if (
|
||||
url_ts < self._DETECT_TIMEOUT
|
||||
and await self.http_multi_async(url_list=[url_addr])
|
||||
):
|
||||
return True
|
||||
# Detect all addresses
|
||||
results = await asyncio.gather(
|
||||
*[self.ping_multi_async(), self.http_multi_async()])
|
||||
return any(results)
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
_LOGGER.error('get network status error, %s', err)
|
||||
return False
|
||||
|
||||
async def get_network_info_async(self) -> dict[str, NetworkInfo]:
|
||||
return await self._main_loop.run_in_executor(
|
||||
None, self.__get_network_info)
|
||||
|
||||
async def ping_multi_async(
|
||||
self, ip_list: Optional[list[str]] = None
|
||||
) -> bool:
|
||||
addr_list = ip_list or list(self._ip_addr_map.keys())
|
||||
tasks = []
|
||||
for addr in addr_list:
|
||||
tasks.append(self.__ping_async(addr))
|
||||
results = await asyncio.gather(*tasks)
|
||||
for addr, ts in zip(addr_list, results):
|
||||
if addr in self._ip_addr_map:
|
||||
self._ip_addr_map[addr] = ts
|
||||
return any([ts < self._DETECT_TIMEOUT for ts in results])
|
||||
|
||||
async def http_multi_async(
|
||||
self, url_list: Optional[list[str]] = None
|
||||
) -> bool:
|
||||
addr_list = url_list or list(self._http_addr_map.keys())
|
||||
tasks = []
|
||||
for addr in addr_list:
|
||||
tasks.append(self.__http_async(url=addr))
|
||||
results = await asyncio.gather(*tasks)
|
||||
for addr, ts in zip(addr_list, results):
|
||||
if addr in self._http_addr_map:
|
||||
self._http_addr_map[addr] = ts
|
||||
return any([ts < self._DETECT_TIMEOUT for ts in results])
|
||||
|
||||
def __calc_network_address(self, ip: str, netmask: str) -> str:
|
||||
return str(ipaddress.IPv4Network(
|
||||
f'{ip}/{netmask}', strict=False).network_address)
|
||||
|
||||
def __ping(
|
||||
self, address: Optional[str] = None, timeout: int = 6
|
||||
) -> bool:
|
||||
param = '-n' if platform.system().lower() == 'windows' else '-c'
|
||||
command = ['ping', param, '1', address]
|
||||
async def __ping_async(self, address: Optional[str] = None) -> float:
|
||||
start_ts: float = self._main_loop.time()
|
||||
try:
|
||||
output = subprocess.run(
|
||||
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
check=True, timeout=timeout)
|
||||
return output.returncode == 0
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*(
|
||||
[
|
||||
'ping', '-n', '1', '-w',
|
||||
str(self._DETECT_TIMEOUT*1000), address]
|
||||
if platform.system().lower() == 'windows' else
|
||||
[
|
||||
'ping', '-c', '1', '-w',
|
||||
str(self._DETECT_TIMEOUT), address]),
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
await process.communicate()
|
||||
if process.returncode == 0:
|
||||
return self._main_loop.time() - start_ts
|
||||
return self._DETECT_TIMEOUT
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
print(err)
|
||||
return self._DETECT_TIMEOUT
|
||||
|
||||
async def __http_async(self, url: str) -> float:
|
||||
start_ts: float = self._main_loop.time()
|
||||
try:
|
||||
async with self._http_session.get(
|
||||
url, timeout=self._DETECT_TIMEOUT):
|
||||
return self._main_loop.time() - start_ts
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
return False
|
||||
|
||||
def __get_network_status(
|
||||
self, with_retry: bool = True, timeout: int = 6
|
||||
) -> bool:
|
||||
if self._ping_address_priority >= len(self.PING_ADDRESS_LIST):
|
||||
self._ping_address_priority = 0
|
||||
|
||||
if self.__ping(
|
||||
self.PING_ADDRESS_LIST[self._ping_address_priority], timeout):
|
||||
return True
|
||||
if not with_retry:
|
||||
return False
|
||||
for index in range(len(self.PING_ADDRESS_LIST)):
|
||||
if index == self._ping_address_priority:
|
||||
continue
|
||||
if self.__ping(self.PING_ADDRESS_LIST[index], timeout):
|
||||
self._ping_address_priority = index
|
||||
return True
|
||||
return False
|
||||
pass
|
||||
return self._DETECT_TIMEOUT
|
||||
|
||||
def __get_network_info(self) -> dict[str, NetworkInfo]:
|
||||
interfaces = psutil.net_if_addrs()
|
||||
@ -246,12 +335,10 @@ class MIoTNetwork:
|
||||
for handler in self._sub_list_network_info.values():
|
||||
self._main_loop.create_task(handler(status, info))
|
||||
|
||||
async def __update_status_and_info_async(self, timeout: int = 6) -> None:
|
||||
async def __update_status_and_info_async(self) -> None:
|
||||
try:
|
||||
status: bool = await self._main_loop.run_in_executor(
|
||||
None, self.__get_network_status, timeout)
|
||||
infos = await self._main_loop.run_in_executor(
|
||||
None, self.__get_network_info)
|
||||
status: bool = await self.get_network_status_async()
|
||||
infos = await self.get_network_info_async()
|
||||
|
||||
if self._network_status != status:
|
||||
for handler in self._sub_list_network_status.values():
|
||||
@ -273,7 +360,7 @@ class MIoTNetwork:
|
||||
# Remove
|
||||
self.__call_network_info_change(
|
||||
InterfaceStatus.REMOVE,
|
||||
self._network_info.pop(name, None))
|
||||
self._network_info.pop(name))
|
||||
# Add
|
||||
for name, info in infos.items():
|
||||
self._network_info[name] = info
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -58,7 +58,6 @@ from enum import Enum, auto
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, Union
|
||||
import logging
|
||||
from urllib.request import Request, urlopen
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.x509.oid import NameOID
|
||||
@ -66,13 +65,13 @@ from cryptography import x509
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||
|
||||
|
||||
# pylint: disable=relative-beyond-top-level
|
||||
from .common import load_json_file
|
||||
from .const import (
|
||||
DEFAULT_INTEGRATION_LANGUAGE,
|
||||
MANUFACTURER_EFFECTIVE_TIME,
|
||||
MIHOME_CA_CERT_STR,
|
||||
MIHOME_CA_CERT_SHA256)
|
||||
from .common import MIoTHttp
|
||||
from .miot_error import MIoTCertError, MIoTError, MIoTStorageError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -93,10 +92,10 @@ class MIoTStorage:
|
||||
|
||||
User data will be stored in the `.storage` directory of Home Assistant.
|
||||
"""
|
||||
_main_loop: asyncio.AbstractEventLoop = None
|
||||
_main_loop: asyncio.AbstractEventLoop
|
||||
_file_future: dict[str, tuple[MIoTStorageType, asyncio.Future]]
|
||||
|
||||
_root_path: str = None
|
||||
_root_path: str
|
||||
|
||||
def __init__(
|
||||
self, root_path: str,
|
||||
@ -140,7 +139,7 @@ class MIoTStorage:
|
||||
if r_data is None:
|
||||
_LOGGER.error('load error, empty file, %s', full_path)
|
||||
return None
|
||||
data_bytes: bytes = None
|
||||
data_bytes: bytes
|
||||
# Hash check
|
||||
if with_hash_check:
|
||||
if len(r_data) <= 32:
|
||||
@ -209,17 +208,17 @@ class MIoTStorage:
|
||||
else:
|
||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||
try:
|
||||
type_: type = type(data)
|
||||
w_bytes: bytes = None
|
||||
if type_ == bytes:
|
||||
w_bytes: bytes
|
||||
if isinstance(data, bytes):
|
||||
w_bytes = data
|
||||
elif type_ == str:
|
||||
elif isinstance(data, str):
|
||||
w_bytes = data.encode('utf-8')
|
||||
elif type_ in [dict, list]:
|
||||
elif isinstance(data, (dict, list)):
|
||||
w_bytes = json.dumps(data).encode('utf-8')
|
||||
else:
|
||||
_LOGGER.error(
|
||||
'save error, unsupported data type, %s', type_.__name__)
|
||||
'save error, unsupported data type, %s',
|
||||
type(data).__name__)
|
||||
return False
|
||||
with open(full_path, 'wb') as w_file:
|
||||
w_file.write(w_bytes)
|
||||
@ -353,7 +352,8 @@ class MIoTStorage:
|
||||
def load_file(self, domain: str, name_with_suffix: str) -> Optional[bytes]:
|
||||
full_path = os.path.join(self._root_path, domain, name_with_suffix)
|
||||
return self.__load(
|
||||
full_path=full_path, type_=bytes, with_hash_check=False)
|
||||
full_path=full_path, type_=bytes,
|
||||
with_hash_check=False) # type: ignore
|
||||
|
||||
async def load_file_async(
|
||||
self, domain: str, name_with_suffix: str
|
||||
@ -371,7 +371,7 @@ class MIoTStorage:
|
||||
None, self.__load, full_path, bytes, False)
|
||||
if not fut.done():
|
||||
self.__add_file_future(full_path, MIoTStorageType.LOAD_FILE, fut)
|
||||
return await fut
|
||||
return await fut # type: ignore
|
||||
|
||||
def remove_file(self, domain: str, name_with_suffix: str) -> bool:
|
||||
full_path = os.path.join(self._root_path, domain, name_with_suffix)
|
||||
@ -438,7 +438,7 @@ class MIoTStorage:
|
||||
domain=config_domain, name=config_name, data=config)
|
||||
local_config = (self.load(domain=config_domain,
|
||||
name=config_name, type_=dict)) or {}
|
||||
local_config.update(config)
|
||||
local_config.update(config) # type: ignore
|
||||
return self.save(
|
||||
domain=config_domain, name=config_name, data=local_config)
|
||||
|
||||
@ -474,27 +474,31 @@ class MIoTStorage:
|
||||
domain=config_domain, name=config_name, data=config)
|
||||
local_config = (await self.load_async(
|
||||
domain=config_domain, name=config_name, type_=dict)) or {}
|
||||
local_config.update(config)
|
||||
local_config.update(config) # type: ignore
|
||||
return await self.save_async(
|
||||
domain=config_domain, name=config_name, data=local_config)
|
||||
|
||||
def load_user_config(
|
||||
self, uid: str, cloud_server: str, keys: Optional[list[str]] = None
|
||||
) -> dict[str, Any]:
|
||||
if keys is not None and len(keys) == 0:
|
||||
if isinstance(keys, list) and len(keys) == 0:
|
||||
# Do nothing
|
||||
return {}
|
||||
config_domain = 'miot_config'
|
||||
config_name = f'{uid}_{cloud_server}'
|
||||
local_config = (self.load(domain=config_domain,
|
||||
name=config_name, type_=dict)) or {}
|
||||
name=config_name, type_=dict))
|
||||
if not isinstance(local_config, dict):
|
||||
return {}
|
||||
if keys is None:
|
||||
return local_config
|
||||
return {key: local_config.get(key, None) for key in keys}
|
||||
return {
|
||||
key: local_config[key] for key in keys
|
||||
if key in local_config}
|
||||
|
||||
async def load_user_config_async(
|
||||
self, uid: str, cloud_server: str, keys: Optional[list[str]] = None
|
||||
) -> dict[str, Any]:
|
||||
) -> dict:
|
||||
"""Load user configuration.
|
||||
|
||||
Args:
|
||||
@ -505,13 +509,15 @@ class MIoTStorage:
|
||||
Returns:
|
||||
dict[str, Any]: query result
|
||||
"""
|
||||
if keys is not None and len(keys) == 0:
|
||||
if isinstance(keys, list) and len(keys) == 0:
|
||||
# Do nothing
|
||||
return {}
|
||||
config_domain = 'miot_config'
|
||||
config_name = f'{uid}_{cloud_server}'
|
||||
local_config = (await self.load_async(
|
||||
domain=config_domain, name=config_name, type_=dict)) or {}
|
||||
domain=config_domain, name=config_name, type_=dict))
|
||||
if not isinstance(local_config, dict):
|
||||
return {}
|
||||
if keys is None:
|
||||
return local_config
|
||||
return {
|
||||
@ -519,7 +525,8 @@ class MIoTStorage:
|
||||
if key in local_config}
|
||||
|
||||
def gen_storage_path(
|
||||
self, domain: str = None, name_with_suffix: str = None
|
||||
self, domain: Optional[str] = None,
|
||||
name_with_suffix: Optional[str] = None
|
||||
) -> str:
|
||||
"""Generate file path."""
|
||||
result = self._root_path
|
||||
@ -609,9 +616,8 @@ class MIoTCert:
|
||||
if cert_data is None:
|
||||
return 0
|
||||
# Check user cert
|
||||
user_cert: x509.Certificate = None
|
||||
try:
|
||||
user_cert = x509.load_pem_x509_certificate(
|
||||
user_cert: x509.Certificate = x509.load_pem_x509_certificate(
|
||||
cert_data, default_backend())
|
||||
cert_info = {}
|
||||
for attribute in user_cert.subject:
|
||||
@ -669,7 +675,8 @@ class MIoTCert:
|
||||
NameOID.COMMON_NAME, f'mips.{self._uid}.{did_hash}.2'),
|
||||
]))
|
||||
csr = builder.sign(
|
||||
private_key, algorithm=None, backend=default_backend())
|
||||
private_key, algorithm=None, # type: ignore
|
||||
backend=default_backend())
|
||||
return csr.public_bytes(serialization.Encoding.PEM).decode('utf-8')
|
||||
|
||||
async def load_user_key_async(self) -> Optional[str]:
|
||||
@ -719,250 +726,6 @@ class MIoTCert:
|
||||
return binascii.hexlify(sha1_hash.finalize()).decode('utf-8')
|
||||
|
||||
|
||||
class SpecMultiLang:
|
||||
"""
|
||||
MIoT-Spec-V2 multi-language for entities.
|
||||
"""
|
||||
MULTI_LANG_FILE = 'specs/multi_lang.json'
|
||||
_main_loop: asyncio.AbstractEventLoop
|
||||
_lang: str
|
||||
_data: Optional[dict[str, dict]]
|
||||
|
||||
def __init__(
|
||||
self, lang: str, loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> None:
|
||||
self._main_loop = loop or asyncio.get_event_loop()
|
||||
self._lang = lang
|
||||
self._data = None
|
||||
|
||||
async def init_async(self) -> None:
|
||||
if isinstance(self._data, dict):
|
||||
return
|
||||
multi_lang_data = None
|
||||
self._data = {}
|
||||
try:
|
||||
multi_lang_data = await self._main_loop.run_in_executor(
|
||||
None, load_json_file,
|
||||
os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
self.MULTI_LANG_FILE))
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
_LOGGER.error('multi lang, load file error, %s', err)
|
||||
return
|
||||
# Check if the file is a valid JSON file
|
||||
if not isinstance(multi_lang_data, dict):
|
||||
_LOGGER.error('multi lang, invalid file data')
|
||||
return
|
||||
for lang_data in multi_lang_data.values():
|
||||
if not isinstance(lang_data, dict):
|
||||
_LOGGER.error('multi lang, invalid lang data')
|
||||
return
|
||||
for data in lang_data.values():
|
||||
if not isinstance(data, dict):
|
||||
_LOGGER.error('multi lang, invalid lang data item')
|
||||
return
|
||||
self._data = multi_lang_data
|
||||
|
||||
async def deinit_async(self) -> str:
|
||||
self._data = None
|
||||
|
||||
async def translate_async(self, urn_key: str) -> dict[str, str]:
|
||||
"""MUST call init_async() first."""
|
||||
if urn_key in self._data:
|
||||
return self._data[urn_key].get(self._lang, {})
|
||||
return {}
|
||||
|
||||
|
||||
class SpecBoolTranslation:
|
||||
"""
|
||||
Boolean value translation.
|
||||
"""
|
||||
BOOL_TRANS_FILE = 'specs/bool_trans.json'
|
||||
_main_loop: asyncio.AbstractEventLoop
|
||||
_lang: str
|
||||
_data: Optional[dict[str, dict]]
|
||||
_data_default: dict[str, dict]
|
||||
|
||||
def __init__(
|
||||
self, lang: str, loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
) -> None:
|
||||
self._main_loop = loop or asyncio.get_event_loop()
|
||||
self._lang = lang
|
||||
self._data = None
|
||||
|
||||
async def init_async(self) -> None:
|
||||
if isinstance(self._data, dict):
|
||||
return
|
||||
data = None
|
||||
self._data = {}
|
||||
try:
|
||||
data = await self._main_loop.run_in_executor(
|
||||
None, load_json_file,
|
||||
os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
self.BOOL_TRANS_FILE))
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
_LOGGER.error('bool trans, load file error, %s', err)
|
||||
return
|
||||
# Check if the file is a valid JSON file
|
||||
if (
|
||||
not isinstance(data, dict)
|
||||
or 'data' not in data
|
||||
or not isinstance(data['data'], dict)
|
||||
or 'translate' not in data
|
||||
or not isinstance(data['translate'], dict)
|
||||
):
|
||||
_LOGGER.error('bool trans, valid file')
|
||||
return
|
||||
|
||||
if 'default' in data['translate']:
|
||||
data_default = (
|
||||
data['translate']['default'].get(self._lang, None)
|
||||
or data['translate']['default'].get(
|
||||
DEFAULT_INTEGRATION_LANGUAGE, None))
|
||||
if data_default:
|
||||
self._data_default = [
|
||||
{'value': True, 'description': data_default['true']},
|
||||
{'value': False, 'description': data_default['false']}
|
||||
]
|
||||
|
||||
for urn, key in data['data'].items():
|
||||
if key not in data['translate']:
|
||||
_LOGGER.error('bool trans, unknown key, %s, %s', urn, key)
|
||||
continue
|
||||
trans_data = (
|
||||
data['translate'][key].get(self._lang, None)
|
||||
or data['translate'][key].get(
|
||||
DEFAULT_INTEGRATION_LANGUAGE, None))
|
||||
if trans_data:
|
||||
self._data[urn] = [
|
||||
{'value': True, 'description': trans_data['true']},
|
||||
{'value': False, 'description': trans_data['false']}
|
||||
]
|
||||
|
||||
async def deinit_async(self) -> None:
|
||||
self._data = None
|
||||
self._data_default = None
|
||||
|
||||
async def translate_async(self, urn: str) -> list[dict[bool, str]]:
|
||||
"""
|
||||
MUST call init_async() before calling this method.
|
||||
[
|
||||
{'value': True, 'description': 'True'},
|
||||
{'value': False, 'description': 'False'}
|
||||
]
|
||||
"""
|
||||
|
||||
return self._data.get(urn, self._data_default)
|
||||
|
||||
|
||||
class SpecFilter:
|
||||
"""
|
||||
MIoT-Spec-V2 filter for entity conversion.
|
||||
"""
|
||||
SPEC_FILTER_FILE = 'specs/spec_filter.json'
|
||||
_main_loop: asyncio.AbstractEventLoop
|
||||
_data: dict[str, dict[str, set]]
|
||||
_cache: Optional[dict]
|
||||
|
||||
def __init__(self, loop: Optional[asyncio.AbstractEventLoop]) -> None:
|
||||
self._main_loop = loop or asyncio.get_event_loop()
|
||||
self._data = None
|
||||
self._cache = None
|
||||
|
||||
async def init_async(self) -> None:
|
||||
if isinstance(self._data, dict):
|
||||
return
|
||||
filter_data = None
|
||||
self._data = {}
|
||||
try:
|
||||
filter_data = await self._main_loop.run_in_executor(
|
||||
None, load_json_file,
|
||||
os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
self.SPEC_FILTER_FILE))
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
_LOGGER.error('spec filter, load file error, %s', err)
|
||||
return
|
||||
if not isinstance(filter_data, dict):
|
||||
_LOGGER.error('spec filter, invalid spec filter content')
|
||||
return
|
||||
for values in list(filter_data.values()):
|
||||
if not isinstance(values, dict):
|
||||
_LOGGER.error('spec filter, invalid spec filter data')
|
||||
return
|
||||
for value in values.values():
|
||||
if not isinstance(value, list):
|
||||
_LOGGER.error('spec filter, invalid spec filter rules')
|
||||
return
|
||||
|
||||
self._data = filter_data
|
||||
|
||||
async def deinit_async(self) -> None:
|
||||
self._cache = None
|
||||
self._data = None
|
||||
|
||||
def filter_spec(self, urn_key: str) -> None:
|
||||
"""MUST call init_async() first."""
|
||||
if not self._data:
|
||||
return
|
||||
self._cache = self._data.get(urn_key, None)
|
||||
|
||||
def filter_service(self, siid: int) -> bool:
|
||||
"""Filter service by siid.
|
||||
MUST call init_async() and filter_spec() first."""
|
||||
if (
|
||||
self._cache
|
||||
and 'services' in self._cache
|
||||
and (
|
||||
str(siid) in self._cache['services']
|
||||
or '*' in self._cache['services'])
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def filter_property(self, siid: int, piid: int) -> bool:
|
||||
"""Filter property by piid.
|
||||
MUST call init_async() and filter_spec() first."""
|
||||
if (
|
||||
self._cache
|
||||
and 'properties' in self._cache
|
||||
and (
|
||||
f'{siid}.{piid}' in self._cache['properties']
|
||||
or f'{siid}.*' in self._cache['properties'])
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
def filter_event(self, siid: int, eiid: int) -> bool:
|
||||
"""Filter event by eiid.
|
||||
MUST call init_async() and filter_spec() first."""
|
||||
if (
|
||||
self._cache
|
||||
and 'events' in self._cache
|
||||
and (
|
||||
f'{siid}.{eiid}' in self._cache['events']
|
||||
or f'{siid}.*' in self._cache['events']
|
||||
)
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
def filter_action(self, siid: int, aiid: int) -> bool:
|
||||
""""Filter action by aiid.
|
||||
MUST call init_async() and filter_spec() first."""
|
||||
if (
|
||||
self._cache
|
||||
and 'actions' in self._cache
|
||||
and (
|
||||
f'{siid}.{aiid}' in self._cache['actions']
|
||||
or f'{siid}.*' in self._cache['actions'])
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class DeviceManufacturer:
|
||||
"""Device manufacturer."""
|
||||
DOMAIN: str = 'miot_specs'
|
||||
@ -976,12 +739,11 @@ class DeviceManufacturer:
|
||||
) -> None:
|
||||
self._main_loop = loop or asyncio.get_event_loop()
|
||||
self._storage = storage
|
||||
self._data = None
|
||||
self._data = {}
|
||||
|
||||
async def init_async(self) -> None:
|
||||
if self._data:
|
||||
return
|
||||
data_cache: dict = None
|
||||
data_cache = await self._storage.load_async(
|
||||
domain=self.DOMAIN, name='manufacturer', type_=dict)
|
||||
if (
|
||||
@ -995,8 +757,15 @@ class DeviceManufacturer:
|
||||
_LOGGER.debug('load manufacturer data success')
|
||||
return
|
||||
|
||||
data_cloud = await self._main_loop.run_in_executor(
|
||||
None, self.__get_manufacturer_data)
|
||||
data_cloud = None
|
||||
try:
|
||||
data_cloud = await MIoTHttp.get_json_async(
|
||||
url='https://cdn.cnbj1.fds.api.mi-img.com/res-conf/xiaomi-home/'
|
||||
'manufacturer.json',
|
||||
loop=self._main_loop)
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
_LOGGER.error('get manufacturer info failed, %s', err)
|
||||
|
||||
if data_cloud:
|
||||
await self._storage.save_async(
|
||||
domain=self.DOMAIN, name='manufacturer',
|
||||
@ -1004,32 +773,16 @@ class DeviceManufacturer:
|
||||
self._data = data_cloud
|
||||
_LOGGER.debug('update manufacturer data success')
|
||||
else:
|
||||
if data_cache:
|
||||
self._data = data_cache.get('data', None)
|
||||
if isinstance(data_cache, dict):
|
||||
self._data = data_cache.get('data', {})
|
||||
_LOGGER.error('load manufacturer data failed, use local data')
|
||||
else:
|
||||
_LOGGER.error('load manufacturer data failed')
|
||||
|
||||
async def deinit_async(self) -> None:
|
||||
self._data = None
|
||||
self._data.clear()
|
||||
|
||||
def get_name(self, short_name: str) -> str:
|
||||
if not self._data or not short_name or short_name not in self._data:
|
||||
return short_name
|
||||
return self._data[short_name].get('name', None) or short_name
|
||||
|
||||
def __get_manufacturer_data(self) -> dict:
|
||||
try:
|
||||
request = Request(
|
||||
'https://cdn.cnbj1.fds.api.mi-img.com/res-conf/xiaomi-home/'
|
||||
'manufacturer.json',
|
||||
method='GET')
|
||||
content: bytes = None
|
||||
with urlopen(request) as response:
|
||||
content = response.read()
|
||||
return (
|
||||
json.loads(str(content, 'utf-8'))
|
||||
if content else None)
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
_LOGGER.error('get manufacturer info failed, %s', err)
|
||||
return None
|
||||
|
@ -0,0 +1,136 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="https://cdn.web-global.fds.api.mi-img.com/mcfe--mi-account/static/favicon_new.ico">
|
||||
<link as="style"
|
||||
href="https://font.sec.miui.com/font/css?family=MiSans:300,400,500,600,700:Chinese_Simplify,Chinese_Traditional,Latin&display=swap"
|
||||
rel="preload">
|
||||
<title>TITLE_PLACEHOLDER</title>
|
||||
<style>
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: MiSans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.frame {
|
||||
background: rgb(255 255 255 / 5%);
|
||||
width: 360px;
|
||||
padding: 40 45;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 20px 50px 0 hsla(0, 0%, 64%, .1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo-frame {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-frame {
|
||||
margin: 20px 0 20px 0;
|
||||
font-size: 26px;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.content-frame {
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 20px;
|
||||
background-color: #ff5c00;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="frame">
|
||||
<!-- XIAOMI LOGO-->
|
||||
<div class="logo-frame">
|
||||
<svg width="50" height="50" viewBox="0 0 193 193" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>编组</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<polygon id="path-1"
|
||||
points="1.78097075e-14 0.000125324675 192.540685 0.000125324675 192.540685 192.540058 1.78097075e-14 192.540058">
|
||||
</polygon>
|
||||
</defs>
|
||||
<g id="\u9875\u9762-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="\u7F16\u7EC4">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<g id="Clip-2"></g>
|
||||
<path
|
||||
d="M172.473071,20.1164903 C154.306633,2.02148701 128.188344,-1.78097075e-14 96.2706558,-1.78097075e-14 C64.312237,-1.78097075e-14 38.155724,2.0452987 19.9974318,20.1872987 C1.84352597,38.3261656 1.78097075e-14,64.4406948 1.78097075e-14,96.3640227 C1.78097075e-14,128.286724 1.84352597,154.415039 20.0049513,172.556412 C38.1638701,190.704052 64.3141169,192.540058 96.2706558,192.540058 C128.225942,192.540058 154.376815,190.704052 172.53636,172.556412 C190.694653,154.409399 192.540685,128.286724 192.540685,96.3640227 C192.540685,64.3999643 190.672721,38.2553571 172.473071,20.1164903"
|
||||
id="Fill-1" fill="#FF6900" mask="url(#mask-2)"></path>
|
||||
<path
|
||||
d="M89.1841721,131.948836 C89.1841721,132.594885 88.640263,133.130648 87.9779221,133.130648 L71.5585097,133.130648 C70.8848896,133.130648 70.338474,132.594885 70.338474,131.948836 L70.338474,89.0100961 C70.338474,88.3584078 70.8848896,87.8251513 71.5585097,87.8251513 L87.9779221,87.8251513 C88.640263,87.8251513 89.1841721,88.3584078 89.1841721,89.0100961 L89.1841721,131.948836 Z"
|
||||
id="Fill-3" fill="#FFFFFF" mask="url(#mask-2)"></path>
|
||||
<path
|
||||
d="M121.332896,131.948836 C121.332896,132.594885 120.786481,133.130648 120.121633,133.130648 L104.492393,133.130648 C103.821906,133.130648 103.275491,132.594885 103.275491,131.948836 L103.275491,131.788421 L103.275491,94.9022357 C103.259198,88.4342292 102.889491,81.7863818 99.5502146,78.445226 C96.6790263,75.5652649 91.3251562,74.9054305 85.7557276,74.7669468 L57.4242049,74.7669468 C56.7555977,74.7669468 56.2154484,75.3045896 56.2154484,75.9512649 L56.2154484,128.074424 L56.2154484,131.948836 C56.2154484,132.594885 55.6640198,133.130648 54.9954127,133.130648 L39.3555198,133.130648 C38.6875393,133.130648 38.1498964,132.594885 38.1498964,131.948836 L38.1498964,60.5996188 C38.1498964,59.9447974 38.6875393,59.4121675 39.3555198,59.4121675 L84.4786692,59.4121675 C96.2717211,59.4121675 108.599909,59.9498104 114.680036,66.0380831 C120.786481,72.1533006 121.332896,84.4595571 121.332896,96.2657682 L121.332896,131.948836 Z"
|
||||
id="Fill-5" fill="#FFFFFF" mask="url(#mask-2)"></path>
|
||||
<path
|
||||
d="M153.53056,131.948836 C153.53056,132.594885 152.978505,133.130648 152.316164,133.130648 L136.678778,133.130648 C136.010797,133.130648 135.467515,132.594885 135.467515,131.948836 L135.467515,60.5996188 C135.467515,59.9447974 136.010797,59.4121675 136.678778,59.4121675 L152.316164,59.4121675 C152.978505,59.4121675 153.53056,59.9447974 153.53056,60.5996188 L153.53056,131.948836 Z"
|
||||
id="Fill-7" fill="#FFFFFF" mask="url(#mask-2)"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- TITLE -->
|
||||
<div class="title-frame">
|
||||
<a id="titleArea">TITLE_PLACEHOLDER</a>
|
||||
</div>
|
||||
<!-- CONTENT -->
|
||||
<div class="content-frame">
|
||||
<a id="contentArea">CONTENT_PLACEHOLDER</a>
|
||||
</div>
|
||||
<!-- BUTTON -->
|
||||
<button onClick="window.close();" id="buttonArea">BUTTON_PLACEHOLDER</button>
|
||||
</div>
|
||||
<script>
|
||||
if (STATUS_PLACEHOLDER) {
|
||||
window.opener = null;
|
||||
window.open('', '_self');
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,295 +0,0 @@
|
||||
{
|
||||
"data": {
|
||||
"urn:miot-spec-v2:property:air-cooler:000000EB": "open_close",
|
||||
"urn:miot-spec-v2:property:alarm:00000012": "open_close",
|
||||
"urn:miot-spec-v2:property:anion:00000025": "open_close",
|
||||
"urn:miot-spec-v2:property:anti-fake:00000130": "yes_no",
|
||||
"urn:miot-spec-v2:property:arrhythmia:000000B4": "yes_no",
|
||||
"urn:miot-spec-v2:property:auto-cleanup:00000124": "open_close",
|
||||
"urn:miot-spec-v2:property:auto-deodorization:00000125": "open_close",
|
||||
"urn:miot-spec-v2:property:auto-keep-warm:0000002B": "open_close",
|
||||
"urn:miot-spec-v2:property:automatic-feeding:000000F0": "open_close",
|
||||
"urn:miot-spec-v2:property:blow:000000CD": "open_close",
|
||||
"urn:miot-spec-v2:property:card-insertion-state:00000106": "yes_no",
|
||||
"urn:miot-spec-v2:property:contact-state:0000007C": "contact_state",
|
||||
"urn:miot-spec-v2:property:current-physical-control-lock:00000099": "open_close",
|
||||
"urn:miot-spec-v2:property:delay:0000014F": "yes_no",
|
||||
"urn:miot-spec-v2:property:deodorization:000000C6": "open_close",
|
||||
"urn:miot-spec-v2:property:dns-auto-mode:000000DC": "open_close",
|
||||
"urn:miot-spec-v2:property:driving-status:000000B9": "yes_no",
|
||||
"urn:miot-spec-v2:property:dryer:00000027": "open_close",
|
||||
"urn:miot-spec-v2:property:eco:00000024": "open_close",
|
||||
"urn:miot-spec-v2:property:glimmer-full-color:00000089": "open_close",
|
||||
"urn:miot-spec-v2:property:guard-mode:000000B6": "open_close",
|
||||
"urn:miot-spec-v2:property:heater:00000026": "open_close",
|
||||
"urn:miot-spec-v2:property:heating:000000C7": "open_close",
|
||||
"urn:miot-spec-v2:property:horizontal-swing:00000017": "open_close",
|
||||
"urn:miot-spec-v2:property:hot-water-recirculation:0000011C": "open_close",
|
||||
"urn:miot-spec-v2:property:image-distortion-correction:0000010F": "open_close",
|
||||
"urn:miot-spec-v2:property:local-storage:0000011E": "yes_no",
|
||||
"urn:miot-spec-v2:property:motion-detection:00000056": "open_close",
|
||||
"urn:miot-spec-v2:property:motion-state:0000007D": "motion_state",
|
||||
"urn:miot-spec-v2:property:motion-tracking:0000008A": "open_close",
|
||||
"urn:miot-spec-v2:property:motor-reverse:00000072": "yes_no",
|
||||
"urn:miot-spec-v2:property:mute:00000040": "open_close",
|
||||
"urn:miot-spec-v2:property:off-delay:00000053": "open_close",
|
||||
"urn:miot-spec-v2:property:on:00000006": "open_close",
|
||||
"urn:miot-spec-v2:property:physical-controls-locked:0000001D": "open_close",
|
||||
"urn:miot-spec-v2:property:plasma:00000132": "yes_no",
|
||||
"urn:miot-spec-v2:property:preheat:00000103": "open_close",
|
||||
"urn:miot-spec-v2:property:seating-state:000000B8": "yes_no",
|
||||
"urn:miot-spec-v2:property:silent-execution:000000FB": "yes_no",
|
||||
"urn:miot-spec-v2:property:sleep-aid-mode:0000010B": "open_close",
|
||||
"urn:miot-spec-v2:property:sleep-mode:00000028": "open_close",
|
||||
"urn:miot-spec-v2:property:snore-state:0000012A": "yes_no",
|
||||
"urn:miot-spec-v2:property:soft-wind:000000CF": "open_close",
|
||||
"urn:miot-spec-v2:property:speed-control:000000E8": "open_close",
|
||||
"urn:miot-spec-v2:property:submersion-state:0000007E": "yes_no",
|
||||
"urn:miot-spec-v2:property:time-watermark:00000087": "open_close",
|
||||
"urn:miot-spec-v2:property:un-straight-blowing:00000100": "open_close",
|
||||
"urn:miot-spec-v2:property:uv:00000029": "open_close",
|
||||
"urn:miot-spec-v2:property:valve-switch:000000FE": "open_close",
|
||||
"urn:miot-spec-v2:property:ventilation:000000CE": "open_close",
|
||||
"urn:miot-spec-v2:property:vertical-swing:00000018": "open_close",
|
||||
"urn:miot-spec-v2:property:wake-up-mode:00000107": "open_close",
|
||||
"urn:miot-spec-v2:property:water-pump:000000F2": "open_close",
|
||||
"urn:miot-spec-v2:property:watering:000000CC": "open_close",
|
||||
"urn:miot-spec-v2:property:wdr-mode:00000088": "open_close",
|
||||
"urn:miot-spec-v2:property:wet:0000002A": "open_close",
|
||||
"urn:miot-spec-v2:property:wifi-band-combine:000000E0": "open_close",
|
||||
"urn:miot-spec-v2:property:wifi-ssid-hidden:000000E3": "yes_no",
|
||||
"urn:miot-spec-v2:property:wind-reverse:00000117": "yes_no"
|
||||
},
|
||||
"translate": {
|
||||
"default": {
|
||||
"de": {
|
||||
"true": "Wahr",
|
||||
"false": "Falsch"
|
||||
},
|
||||
"en": {
|
||||
"true": "True",
|
||||
"false": "False"
|
||||
},
|
||||
"es": {
|
||||
"true": "Verdadero",
|
||||
"false": "Falso"
|
||||
},
|
||||
"fr": {
|
||||
"true": "Vrai",
|
||||
"false": "Faux"
|
||||
},
|
||||
"ja": {
|
||||
"true": "真",
|
||||
"false": "偽"
|
||||
},
|
||||
"nl": {
|
||||
"true": "True",
|
||||
"false": "False"
|
||||
},
|
||||
"pt": {
|
||||
"true": "True",
|
||||
"false": "False"
|
||||
},
|
||||
"pt-BR": {
|
||||
"true": "True",
|
||||
"false": "False"
|
||||
},
|
||||
"ru": {
|
||||
"true": "Истина",
|
||||
"false": "Ложь"
|
||||
},
|
||||
"zh-Hans": {
|
||||
"true": "真",
|
||||
"false": "假"
|
||||
},
|
||||
"zh-Hant": {
|
||||
"true": "真",
|
||||
"false": "假"
|
||||
}
|
||||
},
|
||||
"open_close": {
|
||||
"de": {
|
||||
"true": "Öffnen",
|
||||
"false": "Schließen"
|
||||
},
|
||||
"en": {
|
||||
"true": "Open",
|
||||
"false": "Close"
|
||||
},
|
||||
"es": {
|
||||
"true": "Abierto",
|
||||
"false": "Cerrado"
|
||||
},
|
||||
"fr": {
|
||||
"true": "Ouvert",
|
||||
"false": "Fermer"
|
||||
},
|
||||
"ja": {
|
||||
"true": "開く",
|
||||
"false": "閉じる"
|
||||
},
|
||||
"nl": {
|
||||
"true": "Open",
|
||||
"false": "Dicht"
|
||||
},
|
||||
"pt": {
|
||||
"true": "Aberto",
|
||||
"false": "Fechado"
|
||||
},
|
||||
"pt-BR": {
|
||||
"true": "Aberto",
|
||||
"false": "Fechado"
|
||||
},
|
||||
"ru": {
|
||||
"true": "Открыть",
|
||||
"false": "Закрыть"
|
||||
},
|
||||
"zh-Hans": {
|
||||
"true": "开启",
|
||||
"false": "关闭"
|
||||
},
|
||||
"zh-Hant": {
|
||||
"true": "開啟",
|
||||
"false": "關閉"
|
||||
}
|
||||
},
|
||||
"yes_no": {
|
||||
"de": {
|
||||
"true": "Ja",
|
||||
"false": "Nein"
|
||||
},
|
||||
"en": {
|
||||
"true": "Yes",
|
||||
"false": "No"
|
||||
},
|
||||
"es": {
|
||||
"true": "Sí",
|
||||
"false": "No"
|
||||
},
|
||||
"fr": {
|
||||
"true": "Oui",
|
||||
"false": "Non"
|
||||
},
|
||||
"ja": {
|
||||
"true": "はい",
|
||||
"false": "いいえ"
|
||||
},
|
||||
"nl": {
|
||||
"true": "Ja",
|
||||
"false": "Nee"
|
||||
},
|
||||
"pt": {
|
||||
"true": "Sim",
|
||||
"false": "Não"
|
||||
},
|
||||
"pt-BR": {
|
||||
"true": "Sim",
|
||||
"false": "Não"
|
||||
},
|
||||
"ru": {
|
||||
"true": "Да",
|
||||
"false": "Нет"
|
||||
},
|
||||
"zh-Hans": {
|
||||
"true": "是",
|
||||
"false": "否"
|
||||
},
|
||||
"zh-Hant": {
|
||||
"true": "是",
|
||||
"false": "否"
|
||||
}
|
||||
},
|
||||
"motion_state": {
|
||||
"de": {
|
||||
"true": "Bewegung erkannt",
|
||||
"false": "Keine Bewegung erkannt"
|
||||
},
|
||||
"en": {
|
||||
"true": "Motion Detected",
|
||||
"false": "No Motion Detected"
|
||||
},
|
||||
"es": {
|
||||
"true": "Movimiento detectado",
|
||||
"false": "No se detecta movimiento"
|
||||
},
|
||||
"fr": {
|
||||
"true": "Mouvement détecté",
|
||||
"false": "Aucun mouvement détecté"
|
||||
},
|
||||
"ja": {
|
||||
"true": "動きを検知",
|
||||
"false": "動きが検出されません"
|
||||
},
|
||||
"nl": {
|
||||
"true": "Contact",
|
||||
"false": "Geen contact"
|
||||
},
|
||||
"pt": {
|
||||
"true": "Contato",
|
||||
"false": "Sem contato"
|
||||
},
|
||||
"pt-BR": {
|
||||
"true": "Contato",
|
||||
"false": "Sem contato"
|
||||
},
|
||||
"ru": {
|
||||
"true": "Обнаружено движение",
|
||||
"false": "Движение не обнаружено"
|
||||
},
|
||||
"zh-Hans": {
|
||||
"true": "有人",
|
||||
"false": "无人"
|
||||
},
|
||||
"zh-Hant": {
|
||||
"true": "有人",
|
||||
"false": "無人"
|
||||
}
|
||||
},
|
||||
"contact_state": {
|
||||
"de": {
|
||||
"true": "Kontakt",
|
||||
"false": "Kein Kontakt"
|
||||
},
|
||||
"en": {
|
||||
"true": "Contact",
|
||||
"false": "No Contact"
|
||||
},
|
||||
"es": {
|
||||
"true": "Contacto",
|
||||
"false": "Sin contacto"
|
||||
},
|
||||
"fr": {
|
||||
"true": "Contact",
|
||||
"false": "Pas de contact"
|
||||
},
|
||||
"ja": {
|
||||
"true": "接触",
|
||||
"false": "非接触"
|
||||
},
|
||||
"nl": {
|
||||
"true": "Contact",
|
||||
"false": "Geen contact"
|
||||
},
|
||||
"pt": {
|
||||
"true": "Contato",
|
||||
"false": "Sem contato"
|
||||
},
|
||||
"pt-BR": {
|
||||
"true": "Contato",
|
||||
"false": "Sem contato"
|
||||
},
|
||||
"ru": {
|
||||
"true": "Контакт",
|
||||
"false": "Нет контакта"
|
||||
},
|
||||
"zh-Hans": {
|
||||
"true": "接触",
|
||||
"false": "分离"
|
||||
},
|
||||
"zh-Hant": {
|
||||
"true": "接觸",
|
||||
"false": "分離"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
246
custom_components/xiaomi_home/miot/specs/bool_trans.yaml
Normal file
246
custom_components/xiaomi_home/miot/specs/bool_trans.yaml
Normal file
@ -0,0 +1,246 @@
|
||||
data:
|
||||
urn:miot-spec-v2:property:air-cooler:000000EB: open_close
|
||||
urn:miot-spec-v2:property:alarm:00000012: open_close
|
||||
urn:miot-spec-v2:property:anion:00000025: open_close
|
||||
urn:miot-spec-v2:property:anti-fake:00000130: yes_no
|
||||
urn:miot-spec-v2:property:arrhythmia:000000B4: yes_no
|
||||
urn:miot-spec-v2:property:auto-cleanup:00000124: open_close
|
||||
urn:miot-spec-v2:property:auto-deodorization:00000125: open_close
|
||||
urn:miot-spec-v2:property:auto-keep-warm:0000002B: open_close
|
||||
urn:miot-spec-v2:property:automatic-feeding:000000F0: open_close
|
||||
urn:miot-spec-v2:property:blow:000000CD: open_close
|
||||
urn:miot-spec-v2:property:card-insertion-state:00000106: yes_no
|
||||
urn:miot-spec-v2:property:contact-state:0000007C: contact_state
|
||||
urn:miot-spec-v2:property:current-physical-control-lock:00000099: open_close
|
||||
urn:miot-spec-v2:property:delay:0000014F: yes_no
|
||||
urn:miot-spec-v2:property:deodorization:000000C6: open_close
|
||||
urn:miot-spec-v2:property:dns-auto-mode:000000DC: open_close
|
||||
urn:miot-spec-v2:property:driving-status:000000B9: yes_no
|
||||
urn:miot-spec-v2:property:dryer:00000027: open_close
|
||||
urn:miot-spec-v2:property:eco:00000024: open_close
|
||||
urn:miot-spec-v2:property:glimmer-full-color:00000089: open_close
|
||||
urn:miot-spec-v2:property:guard-mode:000000B6: open_close
|
||||
urn:miot-spec-v2:property:heater:00000026: open_close
|
||||
urn:miot-spec-v2:property:heating:000000C7: open_close
|
||||
urn:miot-spec-v2:property:horizontal-swing:00000017: open_close
|
||||
urn:miot-spec-v2:property:hot-water-recirculation:0000011C: open_close
|
||||
urn:miot-spec-v2:property:image-distortion-correction:0000010F: open_close
|
||||
urn:miot-spec-v2:property:local-storage:0000011E: yes_no
|
||||
urn:miot-spec-v2:property:motion-detection:00000056: open_close
|
||||
urn:miot-spec-v2:property:motion-state:0000007D: motion_state
|
||||
urn:miot-spec-v2:property:motion-tracking:0000008A: open_close
|
||||
urn:miot-spec-v2:property:motor-reverse:00000072: yes_no
|
||||
urn:miot-spec-v2:property:mute:00000040: open_close
|
||||
urn:miot-spec-v2:property:off-delay:00000053: open_close
|
||||
urn:miot-spec-v2:property:on:00000006: open_close
|
||||
urn:miot-spec-v2:property:physical-controls-locked:0000001D: open_close
|
||||
urn:miot-spec-v2:property:plasma:00000132: yes_no
|
||||
urn:miot-spec-v2:property:preheat:00000103: open_close
|
||||
urn:miot-spec-v2:property:seating-state:000000B8: yes_no
|
||||
urn:miot-spec-v2:property:silent-execution:000000FB: yes_no
|
||||
urn:miot-spec-v2:property:sleep-aid-mode:0000010B: open_close
|
||||
urn:miot-spec-v2:property:sleep-mode:00000028: open_close
|
||||
urn:miot-spec-v2:property:snore-state:0000012A: yes_no
|
||||
urn:miot-spec-v2:property:soft-wind:000000CF: open_close
|
||||
urn:miot-spec-v2:property:speed-control:000000E8: open_close
|
||||
urn:miot-spec-v2:property:submersion-state:0000007E: yes_no
|
||||
urn:miot-spec-v2:property:time-watermark:00000087: open_close
|
||||
urn:miot-spec-v2:property:un-straight-blowing:00000100: open_close
|
||||
urn:miot-spec-v2:property:uv:00000029: open_close
|
||||
urn:miot-spec-v2:property:valve-switch:000000FE: open_close
|
||||
urn:miot-spec-v2:property:ventilation:000000CE: open_close
|
||||
urn:miot-spec-v2:property:vertical-swing:00000018: open_close
|
||||
urn:miot-spec-v2:property:wake-up-mode:00000107: open_close
|
||||
urn:miot-spec-v2:property:water-pump:000000F2: open_close
|
||||
urn:miot-spec-v2:property:watering:000000CC: open_close
|
||||
urn:miot-spec-v2:property:wdr-mode:00000088: open_close
|
||||
urn:miot-spec-v2:property:wet:0000002A: open_close
|
||||
urn:miot-spec-v2:property:wifi-band-combine:000000E0: open_close
|
||||
urn:miot-spec-v2:property:wifi-ssid-hidden:000000E3: yes_no
|
||||
urn:miot-spec-v2:property:wind-reverse:00000117: yes_no
|
||||
translate:
|
||||
default:
|
||||
de:
|
||||
'false': Falsch
|
||||
'true': Wahr
|
||||
en:
|
||||
'false': 'False'
|
||||
'true': 'True'
|
||||
es:
|
||||
'false': Falso
|
||||
'true': Verdadero
|
||||
fr:
|
||||
'false': Faux
|
||||
'true': Vrai
|
||||
it:
|
||||
'false': Falso
|
||||
'true': Vero
|
||||
ja:
|
||||
'false': 偽
|
||||
'true': 真
|
||||
nl:
|
||||
'false': 'False'
|
||||
'true': 'True'
|
||||
pt:
|
||||
'false': 'False'
|
||||
'true': 'True'
|
||||
pt-BR:
|
||||
'false': 'False'
|
||||
'true': 'True'
|
||||
ru:
|
||||
'false': Ложь
|
||||
'true': Истина
|
||||
zh-Hans:
|
||||
'false': 假
|
||||
'true': 真
|
||||
zh-Hant:
|
||||
'false': 假
|
||||
'true': 真
|
||||
contact_state:
|
||||
de:
|
||||
'false': Kein Kontakt
|
||||
'true': Kontakt
|
||||
en:
|
||||
'false': No Contact
|
||||
'true': Contact
|
||||
es:
|
||||
'false': Sin contacto
|
||||
'true': Contacto
|
||||
fr:
|
||||
'false': Pas de contact
|
||||
'true': Contact
|
||||
it:
|
||||
'false': Nessun contatto
|
||||
'true': Contatto
|
||||
ja:
|
||||
'false': 非接触
|
||||
'true': 接触
|
||||
nl:
|
||||
'false': Geen contact
|
||||
'true': Contact
|
||||
pt:
|
||||
'false': Sem contato
|
||||
'true': Contato
|
||||
pt-BR:
|
||||
'false': Sem contato
|
||||
'true': Contato
|
||||
ru:
|
||||
'false': Нет контакта
|
||||
'true': Контакт
|
||||
zh-Hans:
|
||||
'false': 分离
|
||||
'true': 接触
|
||||
zh-Hant:
|
||||
'false': 分離
|
||||
'true': 接觸
|
||||
motion_state:
|
||||
de:
|
||||
'false': Keine Bewegung erkannt
|
||||
'true': Bewegung erkannt
|
||||
en:
|
||||
'false': No Motion Detected
|
||||
'true': Motion Detected
|
||||
es:
|
||||
'false': No se detecta movimiento
|
||||
'true': Movimiento detectado
|
||||
fr:
|
||||
'false': Aucun mouvement détecté
|
||||
'true': Mouvement détecté
|
||||
it:
|
||||
'false': Nessun Movimento Rilevato
|
||||
'true': Movimento Rilevato
|
||||
ja:
|
||||
'false': 動きが検出されません
|
||||
'true': 動きを検知
|
||||
nl:
|
||||
'false': Geen contact
|
||||
'true': Contact
|
||||
pt:
|
||||
'false': Sem contato
|
||||
'true': Contato
|
||||
pt-BR:
|
||||
'false': Sem contato
|
||||
'true': Contato
|
||||
ru:
|
||||
'false': Движение не обнаружено
|
||||
'true': Обнаружено движение
|
||||
zh-Hans:
|
||||
'false': 无人
|
||||
'true': 有人
|
||||
zh-Hant:
|
||||
'false': 無人
|
||||
'true': 有人
|
||||
open_close:
|
||||
de:
|
||||
'false': Schließen
|
||||
'true': Öffnen
|
||||
en:
|
||||
'false': Close
|
||||
'true': Open
|
||||
es:
|
||||
'false': Cerrado
|
||||
'true': Abierto
|
||||
fr:
|
||||
'false': Fermer
|
||||
'true': Ouvert
|
||||
it:
|
||||
'false': Chiuso
|
||||
'true': Aperto
|
||||
ja:
|
||||
'false': 閉じる
|
||||
'true': 開く
|
||||
nl:
|
||||
'false': Dicht
|
||||
'true': Open
|
||||
pt:
|
||||
'false': Fechado
|
||||
'true': Aberto
|
||||
pt-BR:
|
||||
'false': Fechado
|
||||
'true': Aberto
|
||||
ru:
|
||||
'false': Закрыть
|
||||
'true': Открыть
|
||||
zh-Hans:
|
||||
'false': 关闭
|
||||
'true': 开启
|
||||
zh-Hant:
|
||||
'false': 關閉
|
||||
'true': 開啟
|
||||
yes_no:
|
||||
de:
|
||||
'false': Nein
|
||||
'true': Ja
|
||||
en:
|
||||
'false': 'No'
|
||||
'true': 'Yes'
|
||||
es:
|
||||
'false': 'No'
|
||||
'true': Sí
|
||||
fr:
|
||||
'false': Non
|
||||
'true': Oui
|
||||
it:
|
||||
'false': 'No'
|
||||
'true': Si
|
||||
ja:
|
||||
'false': いいえ
|
||||
'true': はい
|
||||
nl:
|
||||
'false': Nee
|
||||
'true': Ja
|
||||
pt:
|
||||
'false': Não
|
||||
'true': Sim
|
||||
pt-BR:
|
||||
'false': Não
|
||||
'true': Sim
|
||||
ru:
|
||||
'false': Нет
|
||||
'true': Да
|
||||
zh-Hans:
|
||||
'false': 否
|
||||
'true': 是
|
||||
zh-Hant:
|
||||
'false': 否
|
||||
'true': 是
|
@ -155,7 +155,7 @@
|
||||
"service:004:property:001": "事件名稱"
|
||||
}
|
||||
},
|
||||
"urn:miot-spec-v2:device:switch:0000A003:lumi-acn040:1": {
|
||||
"urn:miot-spec-v2:device:switch:0000A003:lumi-acn040": {
|
||||
"en": {
|
||||
"service:011": "Right Button On and Off",
|
||||
"service:011:property:001": "Right Button On and Off",
|
||||
@ -168,5 +168,20 @@
|
||||
"service:016:action:001": "中键确认",
|
||||
"service:017:action:001": "右键确认"
|
||||
}
|
||||
},
|
||||
"urn:miot-spec-v2:device:bath-heater:0000A028:yeelink-v10": {
|
||||
"en": {
|
||||
"service:003:property:001:valuelist:000": "Idle",
|
||||
"service:003:property:001:valuelist:001": "Dry"
|
||||
}
|
||||
},
|
||||
"urn:miot-spec-v2:device:plant-monitor:0000A030:hhcc-v1": {
|
||||
"en": {
|
||||
"service:002:property:001": "Soil Moisture"
|
||||
},
|
||||
"zh-Hans": {
|
||||
"service:002:property:001": "土壤湿度",
|
||||
"service:002:property:003": "光照强度"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
{
|
||||
"urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-ma4": {
|
||||
"properties": [
|
||||
"9.*",
|
||||
"13.*",
|
||||
"15.*"
|
||||
],
|
||||
"services": [
|
||||
"10"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:curtain:0000A00C:lumi-hmcn01": {
|
||||
"properties": [
|
||||
"5.1"
|
||||
],
|
||||
"services": [
|
||||
"4",
|
||||
"7",
|
||||
"8"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1": {
|
||||
"events": [
|
||||
"2.1"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:health-pot:0000A051:chunmi-a1": {
|
||||
"services": [
|
||||
"5"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:light:0000A001:philips-strip3": {
|
||||
"properties": [
|
||||
"2.2"
|
||||
],
|
||||
"services": [
|
||||
"1",
|
||||
"3"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:light:0000A001:yeelink-color2": {
|
||||
"properties": [
|
||||
"3.*",
|
||||
"2.5"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:light:0000A001:yeelink-dnlight2": {
|
||||
"services": [
|
||||
"3"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:light:0000A001:yeelink-mbulb3": {
|
||||
"services": [
|
||||
"3"
|
||||
]
|
||||
},
|
||||
"urn:miot-spec-v2:device:motion-sensor:0000A014:xiaomi-pir1": {
|
||||
"services": [
|
||||
"1",
|
||||
"5"
|
||||
]
|
||||
}
|
||||
}
|
43
custom_components/xiaomi_home/miot/specs/spec_filter.yaml
Normal file
43
custom_components/xiaomi_home/miot/specs/spec_filter.yaml
Normal file
@ -0,0 +1,43 @@
|
||||
urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-ma4:
|
||||
properties:
|
||||
- 9.*
|
||||
- 13.*
|
||||
- 15.*
|
||||
services:
|
||||
- '10'
|
||||
urn:miot-spec-v2:device:curtain:0000A00C:lumi-hmcn01:
|
||||
properties:
|
||||
- '5.1'
|
||||
services:
|
||||
- '4'
|
||||
- '7'
|
||||
- '8'
|
||||
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:
|
||||
events:
|
||||
- '2.1'
|
||||
urn:miot-spec-v2:device:health-pot:0000A051:chunmi-a1:
|
||||
services:
|
||||
- '5'
|
||||
urn:miot-spec-v2:device:light:0000A001:philips-strip3:
|
||||
properties:
|
||||
- '2.2'
|
||||
services:
|
||||
- '1'
|
||||
- '3'
|
||||
urn:miot-spec-v2:device:light:0000A001:yeelink-color2:
|
||||
properties:
|
||||
- 3.*
|
||||
- '2.5'
|
||||
urn:miot-spec-v2:device:light:0000A001:yeelink-dnlight2:
|
||||
services:
|
||||
- '3'
|
||||
urn:miot-spec-v2:device:light:0000A001:yeelink-mbulb3:
|
||||
services:
|
||||
- '3'
|
||||
urn:miot-spec-v2:device:motion-sensor:0000A014:xiaomi-pir1:
|
||||
services:
|
||||
- '1'
|
||||
- '5'
|
||||
urn:miot-spec-v2:device:router:0000A036:xiaomi-rd03:
|
||||
services:
|
||||
- '*'
|
153
custom_components/xiaomi_home/miot/specs/spec_modify.yaml
Normal file
153
custom_components/xiaomi_home/miot/specs/spec_modify.yaml
Normal file
@ -0,0 +1,153 @@
|
||||
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1:
|
||||
prop.2.1:
|
||||
name: access-mode
|
||||
access:
|
||||
- read
|
||||
- notify
|
||||
prop.2.2:
|
||||
name: ip-address
|
||||
icon: mdi:ip
|
||||
prop.2.3:
|
||||
name: wifi-ssid
|
||||
access:
|
||||
- read
|
||||
- notify
|
||||
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:2: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1
|
||||
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:3: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1
|
||||
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1:
|
||||
prop.5.1:
|
||||
name: power-consumption
|
||||
expr: round(src_value/1000, 3)
|
||||
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:2: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1
|
||||
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1
|
||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp1md:1:
|
||||
prop.2.2:
|
||||
name: power-consumption
|
||||
expr: round(src_value/1000, 3)
|
||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1:
|
||||
prop.11.1:
|
||||
name: power-consumption
|
||||
expr: round(src_value/100, 2)
|
||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:2: urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1
|
||||
urn:miot-spec-v2:device:outlet:0000A002:zimi-zncz01:2:0000C816:
|
||||
prop.3.1:
|
||||
name: electric-power
|
||||
expr: round(src_value/100, 2)
|
||||
urn:miot-spec-v2:device:router:0000A036:xiaomi-rd08:1:
|
||||
prop.2.1:
|
||||
name: download-speed
|
||||
icon: mdi:download
|
||||
unit: B/s
|
||||
prop.2.2:
|
||||
name: upload-speed
|
||||
icon: mdi:upload
|
||||
unit: B/s
|
||||
urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:1:
|
||||
prop.2.3:
|
||||
value-range:
|
||||
- 0
|
||||
- 1
|
||||
- 1
|
||||
urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:2: urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:1
|
||||
urn:miot-spec-v2:device:bath-heater:0000A028:opple-acmoto:1:
|
||||
prop.5.2:
|
||||
value-list:
|
||||
- value: 1
|
||||
description: low
|
||||
- value: 128
|
||||
description: medium
|
||||
- value: 255
|
||||
description: high
|
||||
urn:miot-spec-v2:device:bath-heater:0000A028:mike-2:1:
|
||||
prop.3.1:
|
||||
name: mode-a
|
||||
prop.3.11:
|
||||
name: mode-b
|
||||
prop.3.12:
|
||||
name: mode-c
|
||||
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1:
|
||||
prop.2.2:
|
||||
name: fan-level-a
|
||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6:
|
||||
prop.10.6:
|
||||
unit: none
|
||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:1: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
|
||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
|
||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
|
||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
|
||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:5: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
|
||||
urn:miot-spec-v2:device:airer:0000A00D:mrbond-m33a:1:
|
||||
prop.2.3:
|
||||
name: current-position-a
|
||||
prop.2.11:
|
||||
name: current-position-b
|
||||
urn:miot-spec-v2:device:thermostat:0000A031:suittc-wk168:1:
|
||||
prop.2.3:
|
||||
value-list:
|
||||
- value: 1
|
||||
description: '1'
|
||||
- value: 2
|
||||
description: '2'
|
||||
- value: 3
|
||||
description: '3'
|
||||
- value: 4
|
||||
description: '4'
|
||||
- value: 5
|
||||
description: '5'
|
||||
- value: 6
|
||||
description: '6'
|
||||
- value: 7
|
||||
description: '7'
|
||||
- value: 8
|
||||
description: '8'
|
||||
- value: 9
|
||||
description: '9'
|
||||
- value: 10
|
||||
description: '10'
|
||||
- value: 11
|
||||
description: '11'
|
||||
- value: 12
|
||||
description: '12'
|
||||
- value: 13
|
||||
description: '13'
|
||||
- value: 14
|
||||
description: '14'
|
||||
- value: 15
|
||||
description: '15'
|
||||
- value: 16
|
||||
description: '16'
|
||||
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3:
|
||||
prop.5.1:
|
||||
expr: round(src_value*6/1000000, 3)
|
||||
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3
|
||||
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:2: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3
|
||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2:
|
||||
prop.2.3:
|
||||
expr: round(src_value/10, 1)
|
||||
prop.2.4:
|
||||
unit: mA
|
||||
prop.3.2:
|
||||
expr: round(src_value/10, 1)
|
||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:1: urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2
|
||||
urn:miot-spec-v2:device:plant-monitor:0000A030:hhcc-v1:1:
|
||||
prop.2.1:
|
||||
name: soil-moisture
|
||||
icon: mdi:watering-can
|
||||
prop.2.2:
|
||||
name: soil-ec
|
||||
icon: mdi:sprout-outline
|
||||
unit: μS/cm
|
||||
urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-s1:1:
|
||||
prop.2.5:
|
||||
name: voc-density
|
||||
urn:miot-spec-v2:device:water-purifier:0000A013:roswan-lte01:1:0000D05A:
|
||||
prop.4.1:
|
||||
unit: ppm
|
||||
prop.4.2:
|
||||
unit: ppm
|
||||
urn:miot-spec-v2:device:relay:0000A03D:lumi-c2acn01:1:
|
||||
prop.4.1:
|
||||
unit: kWh
|
||||
urn:miot-spec-v2:device:bath-heater:0000A028:xiaomi-s1:1:
|
||||
prop.4.4:
|
||||
name: fan-level-ventilation
|
@ -46,7 +46,22 @@ off Xiaomi or its affiliates' products.
|
||||
Conversion rules of MIoT-Spec-V2 instance to Home Assistant entity.
|
||||
"""
|
||||
from homeassistant.components.sensor import SensorDeviceClass
|
||||
from homeassistant.components.sensor import SensorStateClass
|
||||
from homeassistant.components.event import EventDeviceClass
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
EntityCategory,
|
||||
LIGHT_LUX,
|
||||
UnitOfEnergy,
|
||||
UnitOfPower,
|
||||
UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfTemperature,
|
||||
UnitOfPressure,
|
||||
PERCENTAGE
|
||||
)
|
||||
|
||||
# pylint: disable=pointless-string-statement
|
||||
"""SPEC_DEVICE_TRANS_MAP
|
||||
@ -88,7 +103,7 @@ from homeassistant.components.event import EventDeviceClass
|
||||
}
|
||||
}
|
||||
"""
|
||||
SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'humidifier': {
|
||||
'required': {
|
||||
'humidifier': {
|
||||
@ -124,7 +139,7 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
'optional': {
|
||||
'properties': {'mode', 'target-humidity'}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'environment': {
|
||||
@ -150,8 +165,7 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
'continue-sweep',
|
||||
'stop-and-gocharge'
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
@ -164,9 +178,9 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
'required': {
|
||||
'properties': {
|
||||
'battery-level': {'read'}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
'entity': 'vacuum'
|
||||
},
|
||||
@ -182,7 +196,7 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
},
|
||||
'optional': {
|
||||
'properties': {'target-humidity'}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
@ -211,6 +225,31 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
'entity': 'air-conditioner'
|
||||
},
|
||||
'air-condition-outlet': 'air-conditioner',
|
||||
'thermostat': {
|
||||
'required': {
|
||||
'thermostat': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'on': {'read', 'write'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'target-temperature', 'mode', 'fan-level',
|
||||
'temperature'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'environment': {
|
||||
'required': {},
|
||||
'optional': {
|
||||
'properties': {'temperature', 'relative-humidity'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'entity': 'thermostat'
|
||||
},
|
||||
'heater': {
|
||||
'required': {
|
||||
'heater': {
|
||||
@ -221,7 +260,7 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
},
|
||||
'optional': {
|
||||
'properties': {'target-temperature', 'heat-level'}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
@ -230,10 +269,58 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
'optional': {
|
||||
'properties': {'temperature', 'relative-humidity'}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
'entity': 'heater'
|
||||
}
|
||||
},
|
||||
'bath-heater': {
|
||||
'required': {
|
||||
'ptc-bath-heater': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'mode':{'read', 'write'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {'target-temperature', 'temperature'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'fan-control': {
|
||||
'required': {},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'on', 'fan-level', 'horizontal-swing', 'vertical-swing'
|
||||
}
|
||||
}
|
||||
},
|
||||
'environment': {
|
||||
'required': {},
|
||||
'optional': {
|
||||
'properties': {'temperature'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'entity': 'bath-heater',
|
||||
},
|
||||
'electric-blanket': {
|
||||
'required': {
|
||||
'electric-blanket': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'on': {'read', 'write'},
|
||||
'target-temperature': {'read', 'write'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {'mode', 'temperature'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'optional': {},
|
||||
'entity': 'electric-blanket'
|
||||
},
|
||||
}
|
||||
|
||||
"""SPEC_SERVICE_TRANS_MAP
|
||||
@ -251,11 +338,12 @@ SPEC_DEVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
'events': set<event instance name: str>,
|
||||
'actions': set<action instance name: str>
|
||||
},
|
||||
'entity': str
|
||||
'entity': str,
|
||||
'entity_category'?: str
|
||||
}
|
||||
}
|
||||
"""
|
||||
SPEC_SERVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
SPEC_SERVICE_TRANS_MAP: dict = {
|
||||
'light': {
|
||||
'required': {
|
||||
'properties': {
|
||||
@ -269,10 +357,23 @@ SPEC_SERVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
},
|
||||
'entity': 'light'
|
||||
},
|
||||
'indicator-light': 'light',
|
||||
'ambient-light': 'light',
|
||||
'night-light': 'light',
|
||||
'white-light': 'light',
|
||||
'indicator-light': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'on': {'read', 'write'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'mode', 'brightness',
|
||||
}
|
||||
},
|
||||
'entity': 'light',
|
||||
'entity_category': EntityCategory.CONFIG
|
||||
},
|
||||
'fan': {
|
||||
'required': {
|
||||
'properties': {
|
||||
@ -281,12 +382,13 @@ SPEC_SERVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {'mode', 'horizontal-swing'}
|
||||
'properties': {'mode', 'horizontal-swing', 'wind-reverse'}
|
||||
},
|
||||
'entity': 'fan'
|
||||
},
|
||||
'fan-control': 'fan',
|
||||
'ceiling-fan': 'fan',
|
||||
'air-fresh': 'fan',
|
||||
'water-heater': {
|
||||
'required': {
|
||||
'properties': {
|
||||
@ -306,12 +408,27 @@ SPEC_SERVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'motor-control', 'status', 'current-position', 'target-position'
|
||||
'status', 'current-position', 'target-position'
|
||||
}
|
||||
},
|
||||
'entity': 'cover'
|
||||
},
|
||||
'window-opener': 'curtain'
|
||||
'window-opener': 'curtain',
|
||||
'motor-controller': 'curtain',
|
||||
'airer': 'curtain',
|
||||
'air-conditioner': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'on': {'read', 'write'},
|
||||
'mode': {'read', 'write'},
|
||||
'target-temperature': {'read', 'write'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {'target-humidity'}
|
||||
},
|
||||
'entity': 'air-conditioner'
|
||||
}
|
||||
}
|
||||
|
||||
"""SPEC_PROP_TRANS_MAP
|
||||
@ -325,74 +442,146 @@ SPEC_SERVICE_TRANS_MAP: dict[str, dict | str] = {
|
||||
'properties': {
|
||||
'<property instance name>':{
|
||||
'device_class': str,
|
||||
'entity': str
|
||||
'entity': str,
|
||||
'state_class'?: str,
|
||||
'unit_of_measurement'?: str
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
SPEC_PROP_TRANS_MAP: dict[str, dict | str] = {
|
||||
SPEC_PROP_TRANS_MAP: dict = {
|
||||
'entities': {
|
||||
'sensor': {
|
||||
'format': {'int', 'float'},
|
||||
'access': {'read'}
|
||||
},
|
||||
'binary_sensor': {
|
||||
'format': {'bool', 'int'},
|
||||
'access': {'read'}
|
||||
},
|
||||
'switch': {
|
||||
'format': {'bool'},
|
||||
'access': {'read', 'write'}
|
||||
}
|
||||
},
|
||||
'properties': {
|
||||
'submersion-state': {
|
||||
'device_class': BinarySensorDeviceClass.MOISTURE,
|
||||
'entity': 'binary_sensor'
|
||||
},
|
||||
'contact-state': {
|
||||
'device_class': BinarySensorDeviceClass.DOOR,
|
||||
'entity': 'binary_sensor'
|
||||
},
|
||||
'occupancy-status': {
|
||||
'device_class': BinarySensorDeviceClass.OCCUPANCY,
|
||||
'entity': 'binary_sensor',
|
||||
},
|
||||
'temperature': {
|
||||
'device_class': SensorDeviceClass.TEMPERATURE,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': UnitOfTemperature.CELSIUS
|
||||
},
|
||||
'relative-humidity': {
|
||||
'device_class': SensorDeviceClass.HUMIDITY,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': PERCENTAGE
|
||||
},
|
||||
'air-quality-index': {
|
||||
'device_class': SensorDeviceClass.AQI,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
},
|
||||
'pm2.5-density': {
|
||||
'device_class': SensorDeviceClass.PM25,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
},
|
||||
'pm10-density': {
|
||||
'device_class': SensorDeviceClass.PM10,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
},
|
||||
'pm1': {
|
||||
'device_class': SensorDeviceClass.PM1,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
},
|
||||
'atmospheric-pressure': {
|
||||
'device_class': SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': UnitOfPressure.PA
|
||||
},
|
||||
'tvoc-density': {
|
||||
'device_class': SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT
|
||||
},
|
||||
'voc-density': {
|
||||
'device_class': SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT
|
||||
},
|
||||
'voc-density': 'tvoc-density',
|
||||
'battery-level': {
|
||||
'device_class': SensorDeviceClass.BATTERY,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': PERCENTAGE
|
||||
},
|
||||
'voltage': {
|
||||
'device_class': SensorDeviceClass.VOLTAGE,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': UnitOfElectricPotential.VOLT
|
||||
},
|
||||
'electric-current': {
|
||||
'device_class': SensorDeviceClass.CURRENT,
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': UnitOfElectricCurrent.AMPERE
|
||||
},
|
||||
'illumination': {
|
||||
'device_class': SensorDeviceClass.ILLUMINANCE,
|
||||
'entity': 'sensor'
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': LIGHT_LUX
|
||||
},
|
||||
'no-one-determine-time': {
|
||||
'device_class': SensorDeviceClass.DURATION,
|
||||
'entity': 'sensor'
|
||||
},
|
||||
'has-someone-duration': 'no-one-determine-time',
|
||||
'no-one-duration': 'no-one-determine-time'
|
||||
'no-one-duration': 'no-one-determine-time',
|
||||
'electric-power': {
|
||||
'device_class': SensorDeviceClass.POWER,
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': UnitOfPower.WATT
|
||||
},
|
||||
'surge-power': {
|
||||
'device_class': SensorDeviceClass.POWER,
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': UnitOfPower.WATT
|
||||
},
|
||||
'power-consumption': {
|
||||
'device_class': SensorDeviceClass.ENERGY,
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.TOTAL_INCREASING,
|
||||
'unit_of_measurement': UnitOfEnergy.KILO_WATT_HOUR
|
||||
},
|
||||
'power': {
|
||||
'device_class': SensorDeviceClass.POWER,
|
||||
'entity': 'sensor',
|
||||
'state_class': SensorStateClass.MEASUREMENT,
|
||||
'unit_of_measurement': UnitOfPower.WATT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,237 +46,31 @@ off Xiaomi or its affiliates' products.
|
||||
MIoT redirect web pages.
|
||||
"""
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
def oauth_redirect_page(lang: str, status: str) -> str:
|
||||
_template = ''
|
||||
|
||||
|
||||
def _load_page_template():
|
||||
path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
'resource/oauth_redirect_page.html')
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
global _template
|
||||
_template = f.read()
|
||||
|
||||
|
||||
async def oauth_redirect_page(
|
||||
title: str, content: str, button: str, success: bool
|
||||
) -> str:
|
||||
"""Return oauth redirect page."""
|
||||
return '''
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="https://cdn.web-global.fds.api.mi-img.com/mcfe--mi-account/static/favicon_new.ico">
|
||||
<link as="style"
|
||||
href="https://font.sec.miui.com/font/css?family=MiSans:300,400,500,600,700:Chinese_Simplify,Chinese_Traditional,Latin&display=swap"
|
||||
rel="preload">
|
||||
<title></title>
|
||||
<style>
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: MiSans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
.frame {
|
||||
background: rgb(255 255 255 / 5%);
|
||||
width: 360px;
|
||||
padding: 40 45;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 20px 50px 0 hsla(0, 0%, 64%, .1);
|
||||
text-align: center;
|
||||
}
|
||||
.logo-frame {
|
||||
text-align: center;
|
||||
}
|
||||
.title-frame {
|
||||
margin: 20px 0 20px 0;
|
||||
font-size: 26px;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.content-frame {
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
button {
|
||||
margin-top: 20px;
|
||||
background-color: #ff5c00;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="frame">
|
||||
<!-- XIAOMI LOGO-->
|
||||
<div class="logo-frame">
|
||||
<svg width="50" height="50" viewBox="0 0 193 193" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"><title>编组</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<polygon id="path-1"
|
||||
points="1.78097075e-14 0.000125324675 192.540685 0.000125324675 192.540685 192.540058 1.78097075e-14 192.540058"></polygon>
|
||||
</defs>
|
||||
<g id="\u9875\u9762-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="\u7F16\u7EC4">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<g id="Clip-2"></g>
|
||||
<path d="M172.473071,20.1164903 C154.306633,2.02148701 128.188344,-1.78097075e-14 96.2706558,-1.78097075e-14 C64.312237,-1.78097075e-14 38.155724,2.0452987 19.9974318,20.1872987 C1.84352597,38.3261656 1.78097075e-14,64.4406948 1.78097075e-14,96.3640227 C1.78097075e-14,128.286724 1.84352597,154.415039 20.0049513,172.556412 C38.1638701,190.704052 64.3141169,192.540058 96.2706558,192.540058 C128.225942,192.540058 154.376815,190.704052 172.53636,172.556412 C190.694653,154.409399 192.540685,128.286724 192.540685,96.3640227 C192.540685,64.3999643 190.672721,38.2553571 172.473071,20.1164903"
|
||||
id="Fill-1" fill="#FF6900" mask="url(#mask-2)"></path>
|
||||
<path d="M89.1841721,131.948836 C89.1841721,132.594885 88.640263,133.130648 87.9779221,133.130648 L71.5585097,133.130648 C70.8848896,133.130648 70.338474,132.594885 70.338474,131.948836 L70.338474,89.0100961 C70.338474,88.3584078 70.8848896,87.8251513 71.5585097,87.8251513 L87.9779221,87.8251513 C88.640263,87.8251513 89.1841721,88.3584078 89.1841721,89.0100961 L89.1841721,131.948836 Z"
|
||||
id="Fill-3" fill="#FFFFFF" mask="url(#mask-2)"></path>
|
||||
<path d="M121.332896,131.948836 C121.332896,132.594885 120.786481,133.130648 120.121633,133.130648 L104.492393,133.130648 C103.821906,133.130648 103.275491,132.594885 103.275491,131.948836 L103.275491,131.788421 L103.275491,94.9022357 C103.259198,88.4342292 102.889491,81.7863818 99.5502146,78.445226 C96.6790263,75.5652649 91.3251562,74.9054305 85.7557276,74.7669468 L57.4242049,74.7669468 C56.7555977,74.7669468 56.2154484,75.3045896 56.2154484,75.9512649 L56.2154484,128.074424 L56.2154484,131.948836 C56.2154484,132.594885 55.6640198,133.130648 54.9954127,133.130648 L39.3555198,133.130648 C38.6875393,133.130648 38.1498964,132.594885 38.1498964,131.948836 L38.1498964,60.5996188 C38.1498964,59.9447974 38.6875393,59.4121675 39.3555198,59.4121675 L84.4786692,59.4121675 C96.2717211,59.4121675 108.599909,59.9498104 114.680036,66.0380831 C120.786481,72.1533006 121.332896,84.4595571 121.332896,96.2657682 L121.332896,131.948836 Z"
|
||||
id="Fill-5" fill="#FFFFFF" mask="url(#mask-2)"></path>
|
||||
<path d="M153.53056,131.948836 C153.53056,132.594885 152.978505,133.130648 152.316164,133.130648 L136.678778,133.130648 C136.010797,133.130648 135.467515,132.594885 135.467515,131.948836 L135.467515,60.5996188 C135.467515,59.9447974 136.010797,59.4121675 136.678778,59.4121675 L152.316164,59.4121675 C152.978505,59.4121675 153.53056,59.9447974 153.53056,60.5996188 L153.53056,131.948836 Z"
|
||||
id="Fill-7" fill="#FFFFFF" mask="url(#mask-2)"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- TITLE -->
|
||||
<div class="title-frame">
|
||||
<a id="titleArea"></a>
|
||||
</div>
|
||||
<!-- CONTENT -->
|
||||
<div class="content-frame">
|
||||
<a id="contentArea"></a>
|
||||
</div>
|
||||
<!-- BUTTON -->
|
||||
<button onClick="window.close();" id="buttonArea"></button>
|
||||
</div>
|
||||
<script>
|
||||
// get language (user language -> system language)
|
||||
const locale = (localStorage.getItem('selectedLanguage')?? "''' + lang + '''").replaceAll('"','');
|
||||
const language = locale.includes("-") ? locale.substring(0, locale.indexOf("-")).trim() : locale;
|
||||
const status = "''' + status + '''";
|
||||
console.log(locale);
|
||||
// translation
|
||||
let translation = {
|
||||
zh: {
|
||||
success: {
|
||||
title: "认证完成",
|
||||
content: "请关闭此页面,返回账号认证页面点击“下一步”",
|
||||
button: "关闭页面"
|
||||
},
|
||||
fail: {
|
||||
title: "认证失败",
|
||||
content: "请关闭此页面,返回账号认证页面重新点击认链接进行认证。",
|
||||
button: "关闭页面"
|
||||
}
|
||||
},
|
||||
'zh-Hant': {
|
||||
success: {
|
||||
title: "認證完成",
|
||||
content: "請關閉此頁面,返回帳號認證頁面點擊「下一步」。",
|
||||
button: "關閉頁面"
|
||||
},
|
||||
fail: {
|
||||
title: "認證失敗",
|
||||
content: "請關閉此頁面,返回帳號認證頁面重新點擊認鏈接進行認證。",
|
||||
button: "關閉頁面"
|
||||
}
|
||||
},
|
||||
en: {
|
||||
success: {
|
||||
title: "Authentication Completed",
|
||||
content: "Please close this page and return to the account authentication page to click NEXT",
|
||||
button: "Close Page"
|
||||
},
|
||||
fail: {
|
||||
title: "Authentication Failed",
|
||||
content: "Please close this page and return to the account authentication page to click the authentication link again.",
|
||||
button: "Close Page"
|
||||
}
|
||||
},
|
||||
fr: {
|
||||
success: {
|
||||
title: "Authentification Terminée",
|
||||
content: "Veuillez fermer cette page et revenir à la page d'authentification du compte pour cliquer sur « SUIVANT »",
|
||||
button: "Fermer la page"
|
||||
},
|
||||
fail: {
|
||||
title: "Échec de l'Authentification",
|
||||
content: "Veuillez fermer cette page et revenir à la page d'authentification du compte pour cliquer de nouveau sur le lien d'authentification.",
|
||||
button: "Fermer la page"
|
||||
}
|
||||
},
|
||||
ru: {
|
||||
success: {
|
||||
title: "Подтверждение завершено",
|
||||
content: "Пожалуйста, закройте эту страницу, вернитесь на страницу аутентификации учетной записи и нажмите кнопку «Далее».",
|
||||
button: "Закрыть страницу"
|
||||
},
|
||||
fail: {
|
||||
title: "Ошибка аутентификации",
|
||||
content: "Пожалуйста, закройте эту страницу, вернитесь на страницу аутентификации учетной записи и повторите процесс аутентификации, щелкнув ссылку.",
|
||||
button: "Закрыть страницу"
|
||||
}
|
||||
},
|
||||
de: {
|
||||
success: {
|
||||
title: "Authentifizierung abgeschlossen",
|
||||
content: "Bitte schließen Sie diese Seite, kehren Sie zur Kontobestätigungsseite zurück und klicken Sie auf „WEITER“.",
|
||||
button: "Seite schließen"
|
||||
},
|
||||
fail: {
|
||||
title: "Authentifizierung fehlgeschlagen",
|
||||
content: "Bitte schließen Sie diese Seite, kehren Sie zur Kontobestätigungsseite zurück und wiederholen Sie den Authentifizierungsprozess, indem Sie auf den Link klicken.",
|
||||
button: "Seite schließen"
|
||||
}
|
||||
},
|
||||
es: {
|
||||
success: {
|
||||
title: "Autenticación completada",
|
||||
content: "Por favor, cierre esta página, regrese a la página de autenticación de la cuenta y haga clic en 'SIGUIENTE'.",
|
||||
button: "Cerrar página"
|
||||
},
|
||||
fail: {
|
||||
title: "Error de autenticación",
|
||||
content: "Por favor, cierre esta página, regrese a la página de autenticación de la cuenta y vuelva a hacer clic en el enlace de autenticación.",
|
||||
button: "Cerrar página"
|
||||
}
|
||||
},
|
||||
ja: {
|
||||
success: {
|
||||
title: "認証完了",
|
||||
content: "このページを閉じて、アカウント認証ページに戻り、「次」をクリックしてください。",
|
||||
button: "ページを閉じる"
|
||||
},
|
||||
fail: {
|
||||
title: "認証失敗",
|
||||
content: "このページを閉じて、アカウント認証ページに戻り、認証リンクを再度クリックしてください。",
|
||||
button: "ページを閉じる"
|
||||
}
|
||||
}
|
||||
}
|
||||
// insert translate into page / match order: locale > language > english
|
||||
document.title = translation[locale]?.[status]?.title ?? translation[language]?.[status]?.title ?? translation["en"]?.[status]?.title;
|
||||
document.getElementById("titleArea").innerText = translation[locale]?.[status]?.title ?? translation[language]?.[status]?.title ?? translation["en"]?.[status]?.title;
|
||||
document.getElementById("contentArea").innerText = translation[locale]?.[status]?.content ?? translation[language]?.[status]?.content ?? translation["en"]?.[status]?.content;
|
||||
document.getElementById("buttonArea").innerText = translation[locale]?.[status]?.button ?? translation[language]?.[status]?.button ?? translation["en"]?.[status]?.button;
|
||||
window.opener=null;
|
||||
window.open('','_self');
|
||||
window.close();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
if _template == '':
|
||||
await asyncio.get_running_loop().run_in_executor(
|
||||
None, _load_page_template)
|
||||
web_page = _template.replace('TITLE_PLACEHOLDER', title)
|
||||
web_page = web_page.replace('CONTENT_PLACEHOLDER', content)
|
||||
web_page = web_page.replace('BUTTON_PLACEHOLDER', button)
|
||||
web_page = web_page.replace(
|
||||
'STATUS_PLACEHOLDER', 'true' if success else 'false')
|
||||
return web_page
|
||||
|
@ -46,14 +46,15 @@ off Xiaomi or its affiliates' products.
|
||||
Notify entities for Xiaomi Home.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.components.notify import NotifyEntity
|
||||
from homeassistant.util import yaml
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .miot.miot_spec import MIoTSpecAction
|
||||
from .miot.miot_device import MIoTDevice, MIoTActionEntity
|
||||
@ -82,13 +83,14 @@ async def async_setup_entry(
|
||||
|
||||
class Notify(MIoTActionEntity, NotifyEntity):
|
||||
"""Notify entities for Xiaomi Home."""
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
def __init__(self, miot_device: MIoTDevice, spec: MIoTSpecAction) -> None:
|
||||
"""Initialize the Notify."""
|
||||
super().__init__(miot_device=miot_device, spec=spec)
|
||||
self._attr_extra_state_attributes = {}
|
||||
action_in: str = ', '.join([
|
||||
f'{prop.description_trans}({prop.format_})'
|
||||
f'{prop.description_trans}({prop.format_.__name__})'
|
||||
for prop in self.spec.in_])
|
||||
self._attr_extra_state_attributes['action params'] = f'[{action_in}]'
|
||||
|
||||
@ -96,34 +98,58 @@ class Notify(MIoTActionEntity, NotifyEntity):
|
||||
self, message: str, title: Optional[str] = None
|
||||
) -> None:
|
||||
"""Send a message."""
|
||||
del title
|
||||
if not message:
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), empty action params',
|
||||
self.name, self.entity_id)
|
||||
return
|
||||
in_list: Any = None
|
||||
try:
|
||||
in_list: list = json.loads(message)
|
||||
except json.JSONDecodeError:
|
||||
# YAML will convert yes, no, on, off, true, false to the bool type,
|
||||
# and if it is a string, quotation marks need to be added.
|
||||
in_list = yaml.parse_yaml(content=message)
|
||||
except HomeAssistantError:
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), invalid action params format, %s',
|
||||
self.name, self.entity_id, message)
|
||||
return
|
||||
|
||||
if len(self.spec.in_) == 1 and not isinstance(in_list, list):
|
||||
in_list = [in_list]
|
||||
if not isinstance(in_list, list) or len(in_list) != len(self.spec.in_):
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), invalid action params, %s',
|
||||
self.name, self.entity_id, message)
|
||||
return
|
||||
|
||||
in_value: list[dict] = []
|
||||
for index, prop in enumerate(self.spec.in_):
|
||||
if type(in_list[index]).__name__ != prop.format_:
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), invalid params item, '
|
||||
'which item(%s) in the list must be %s, %s',
|
||||
self.name, self.entity_id, prop.description_trans,
|
||||
prop.format_, message)
|
||||
return
|
||||
in_value.append({'piid': prop.iid, 'value': in_list[index]})
|
||||
return await self.action_async(in_list=in_value)
|
||||
if prop.format_ == str:
|
||||
if isinstance(in_list[index], (bool, int, float, str)):
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': str(in_list[index])})
|
||||
continue
|
||||
elif prop.format_ == bool:
|
||||
if isinstance(in_list[index], (bool, int)):
|
||||
# yes, no, on, off, true, false and other bool types
|
||||
# will also be parsed as 0 and 1 of int.
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': bool(in_list[index])})
|
||||
continue
|
||||
elif prop.format_ == float:
|
||||
if isinstance(in_list[index], (int, float)):
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': in_list[index]})
|
||||
continue
|
||||
elif prop.format_ == int:
|
||||
if isinstance(in_list[index], int):
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': in_list[index]})
|
||||
continue
|
||||
# Invalid params type, raise error.
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), invalid params item, '
|
||||
'which item(%s) in the list must be %s, %s type was %s, %s',
|
||||
self.name, self.entity_id, prop.description_trans,
|
||||
prop.format_, in_list[index], type(
|
||||
in_list[index]).__name__, message)
|
||||
return
|
||||
await self.action_async(in_list=in_value)
|
||||
|
@ -88,13 +88,13 @@ class Number(MIoTPropertyEntity, NumberEntity):
|
||||
if self.spec.external_unit:
|
||||
self._attr_native_unit_of_measurement = self.spec.external_unit
|
||||
# Set icon
|
||||
if self.spec.icon:
|
||||
if self.spec.icon and not self.device_class:
|
||||
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]:
|
||||
|
@ -82,7 +82,8 @@ class Select(MIoTPropertyEntity, SelectEntity):
|
||||
def __init__(self, miot_device: MIoTDevice, spec: MIoTSpecProperty) -> None:
|
||||
"""Initialize the Select."""
|
||||
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:
|
||||
"""Change the selected option."""
|
||||
|
@ -76,6 +76,12 @@ async def async_setup_entry(
|
||||
for prop in miot_device.prop_list.get('sensor', []):
|
||||
new_entities.append(Sensor(miot_device=miot_device, spec=prop))
|
||||
|
||||
if miot_device.miot_client.display_binary_text:
|
||||
for prop in miot_device.prop_list.get('binary_sensor', []):
|
||||
if not prop.value_list:
|
||||
continue
|
||||
new_entities.append(Sensor(miot_device=miot_device, spec=prop))
|
||||
|
||||
if new_entities:
|
||||
async_add_entities(new_entities)
|
||||
|
||||
@ -89,9 +95,9 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
|
||||
# Set device_class
|
||||
if self._value_list:
|
||||
self._attr_device_class = SensorDeviceClass.ENUM
|
||||
self._attr_icon = 'mdi:message-text'
|
||||
self._attr_icon = 'mdi:format-text'
|
||||
self._attr_native_unit_of_measurement = None
|
||||
self._attr_options = list(self._value_list.values())
|
||||
self._attr_options = self._value_list.descriptions
|
||||
else:
|
||||
self._attr_device_class = spec.device_class
|
||||
if spec.external_unit:
|
||||
@ -100,11 +106,17 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
|
||||
# device_class is not empty but unit is empty.
|
||||
# Set the default unit according to device_class.
|
||||
unit_sets = DEVICE_CLASS_UNITS.get(
|
||||
self._attr_device_class, None)
|
||||
self._attr_device_class, None) # type: ignore
|
||||
self._attr_native_unit_of_measurement = list(
|
||||
unit_sets)[0] if unit_sets else None
|
||||
# Set suggested precision
|
||||
if spec.format_ in {int, float} and spec.expr is None:
|
||||
self._attr_suggested_display_precision = spec.precision
|
||||
# Set state_class
|
||||
if spec.state_class:
|
||||
self._attr_state_class = spec.state_class
|
||||
# Set icon
|
||||
if spec.icon:
|
||||
if spec.icon and not self.device_class:
|
||||
self._attr_icon = spec.icon
|
||||
|
||||
@property
|
||||
@ -112,14 +124,14 @@ 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',
|
||||
self.entity_id, self._value, self._value_range)
|
||||
if self._value_list:
|
||||
return self._value_list.get(self._value, None)
|
||||
return self.get_vlist_description(self._value)
|
||||
if isinstance(self._value, str):
|
||||
return self._value[:255]
|
||||
return self._value
|
||||
|
@ -46,14 +46,15 @@ off Xiaomi or its affiliates' products.
|
||||
Text entities for Xiaomi Home.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.components.text import TextEntity
|
||||
from homeassistant.util import yaml
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .miot.const import DOMAIN
|
||||
from .miot.miot_spec import MIoTSpecAction, MIoTSpecProperty
|
||||
@ -75,9 +76,10 @@ async def async_setup_entry(
|
||||
for prop in miot_device.prop_list.get('text', []):
|
||||
new_entities.append(Text(miot_device=miot_device, spec=prop))
|
||||
|
||||
for action in miot_device.action_list.get('action_text', []):
|
||||
new_entities.append(ActionText(
|
||||
miot_device=miot_device, spec=action))
|
||||
if miot_device.miot_client.action_debug:
|
||||
for action in miot_device.action_list.get('notify', []):
|
||||
new_entities.append(ActionText(
|
||||
miot_device=miot_device, spec=action))
|
||||
|
||||
if new_entities:
|
||||
async_add_entities(new_entities)
|
||||
@ -110,25 +112,25 @@ class ActionText(MIoTActionEntity, TextEntity):
|
||||
self._attr_extra_state_attributes = {}
|
||||
self._attr_native_value = ''
|
||||
action_in: str = ', '.join([
|
||||
f'{prop.description_trans}({prop.format_})'
|
||||
f'{prop.description_trans}({prop.format_.__name__})'
|
||||
for prop in self.spec.in_])
|
||||
self._attr_extra_state_attributes['action params'] = f'[{action_in}]'
|
||||
# For action debug
|
||||
self.action_platform = 'action_text'
|
||||
|
||||
async def async_set_value(self, value: str) -> None:
|
||||
if not value:
|
||||
return
|
||||
in_list: list = None
|
||||
in_list: Any = None
|
||||
try:
|
||||
in_list = json.loads(value)
|
||||
except json.JSONDecodeError as e:
|
||||
in_list = yaml.parse_yaml(content=value)
|
||||
except HomeAssistantError as e:
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), invalid action params format, %s',
|
||||
self.name, self.entity_id, value)
|
||||
raise ValueError(
|
||||
f'action exec failed, {self.name}({self.entity_id}), '
|
||||
f'invalid action params format, {value}') from e
|
||||
if len(self.spec.in_) == 1 and not isinstance(in_list, list):
|
||||
in_list = [in_list]
|
||||
if not isinstance(in_list, list) or len(in_list) != len(self.spec.in_):
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), invalid action params, %s',
|
||||
@ -138,16 +140,40 @@ class ActionText(MIoTActionEntity, TextEntity):
|
||||
f'invalid action params, {value}')
|
||||
in_value: list[dict] = []
|
||||
for index, prop in enumerate(self.spec.in_):
|
||||
if type(in_list[index]).__name__ != prop.format_:
|
||||
logging.error(
|
||||
'action exec failed, %s(%s), invalid params item, which '
|
||||
'item(%s) in the list must be %s, %s', self.name,
|
||||
self.entity_id, prop.description_trans, prop.format_, value)
|
||||
raise ValueError(
|
||||
f'action exec failed, {self.name}({self.entity_id}), '
|
||||
f'invalid params item, which item({prop.description_trans})'
|
||||
f' in the list must be {prop.format_}, {value}')
|
||||
in_value.append({'piid': prop.iid, 'value': in_list[index]})
|
||||
if prop.format_ == str:
|
||||
if isinstance(in_list[index], (bool, int, float, str)):
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': str(in_list[index])})
|
||||
continue
|
||||
elif prop.format_ == bool:
|
||||
if isinstance(in_list[index], (bool, int)):
|
||||
# yes, no, on, off, true, false and other bool types
|
||||
# will also be parsed as 0 and 1 of int.
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': bool(in_list[index])})
|
||||
continue
|
||||
elif prop.format_ == float:
|
||||
if isinstance(in_list[index], (int, float)):
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': in_list[index]})
|
||||
continue
|
||||
elif prop.format_ == int:
|
||||
if isinstance(in_list[index], int):
|
||||
in_value.append(
|
||||
{'piid': prop.iid, 'value': in_list[index]})
|
||||
continue
|
||||
# Invalid params type, raise error.
|
||||
_LOGGER.error(
|
||||
'action exec failed, %s(%s), invalid params item, '
|
||||
'which item(%s) in the list must be %s, %s type was %s, %s',
|
||||
self.name, self.entity_id, prop.description_trans,
|
||||
prop.format_, in_list[index], type(
|
||||
in_list[index]).__name__, value)
|
||||
raise ValueError(
|
||||
f'action exec failed, {self.name}({self.entity_id}), '
|
||||
f'invalid params item, which item({prop.description_trans}) '
|
||||
f'in the list must be {prop.format_}, {in_list[index]} type '
|
||||
f'was {type(in_list[index]).__name__}, {value}')
|
||||
|
||||
self._attr_native_value = value
|
||||
if await self.action_async(in_list=in_value):
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Grundkonfiguration",
|
||||
"description": "### Anmeldegebiet\r\nWählen Sie das Gebiet, in dem sich Ihr Xiaomi Home-Konto befindet. Sie können es in der Xiaomi Home App unter `Mein (unten im Menü) > Weitere Einstellungen > Über Xiaomi Home` überprüfen.\r\n### Sprache\r\nWählen Sie die Sprache, in der Geräte und Entitätsnamen angezeigt werden. Teile von Sätzen, die nicht übersetzt sind, werden in Englisch angezeigt.\r\n### OAuth2-Authentifizierungs-Umleitungs-URL\r\nDie Umleitungs-URL für die OAuth2-Authentifizierung lautet **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant muss im selben lokalen Netzwerk wie das aktuelle Betriebsterminal (z. B. ein persönlicher Computer) und das Betriebsterminal muss über diese Adresse auf die Home Assistant-Homepage zugreifen können. Andernfalls kann die Anmeldeauthentifizierung fehlschlagen.\r\n### Hinweis\r\n- Für Benutzer mit Hunderten oder mehr Mi Home-Geräten wird das erste Hinzufügen der Integration einige Zeit in Anspruch nehmen. Bitte haben Sie Geduld.\r\n- Wenn Home Assistant in einer Docker-Umgebung läuft, stellen Sie bitte sicher, dass der Docker-Netzwerkmodus auf host eingestellt ist, da sonst die lokale Steuerungsfunktion möglicherweise nicht richtig funktioniert.\r\n- Die lokale Steuerungsfunktion der Integration hat einige Abhängigkeiten. Bitte lesen Sie das README sorgfältig.",
|
||||
"description": "### Anmeldegebiet\r\nWählen Sie das Gebiet, in dem sich Ihr Xiaomi Home-Konto befindet. Sie können es in der Xiaomi Home App unter `Mein (unten im Menü) > Weitere Einstellungen > Über Xiaomi Home` überprüfen.\r\n### Sprache\r\nWählen Sie die Sprache, in der Geräte und Entitätsnamen angezeigt werden. Teile von Sätzen, die nicht übersetzt sind, werden in Englisch angezeigt.\r\n### OAuth2-Authentifizierungs-Umleitungs-URL\r\nDie Umleitungs-URL für die OAuth2-Authentifizierung lautet **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant muss im selben lokalen Netzwerk wie das aktuelle Betriebsterminal (z. B. ein persönlicher Computer) und das Betriebsterminal muss über diese Adresse auf die Home Assistant-Homepage zugreifen können. Andernfalls kann die Anmeldeauthentifizierung fehlschlagen.\r\n### Integrierte Netzwerkkonfiguration\r\nÜberprüfen Sie, ob das lokale Netzwerk normal funktioniert und ob die zugehörigen Netzwerkressourcen zugänglich sind. **Es wird empfohlen, dies beim ersten Hinzufügen auszuwählen.**\r\n### Hinweis\r\n- Für Benutzer mit Hunderten oder mehr Mi Home-Geräten wird das erste Hinzufügen der Integration einige Zeit in Anspruch nehmen. Bitte haben Sie Geduld.\r\n- Wenn Home Assistant in einer Docker-Umgebung läuft, stellen Sie bitte sicher, dass der Docker-Netzwerkmodus auf host eingestellt ist, da sonst die lokale Steuerungsfunktion möglicherweise nicht richtig funktioniert.\r\n- Die lokale Steuerungsfunktion der Integration hat einige Abhängigkeiten. Bitte lesen Sie das README sorgfältig.",
|
||||
"data": {
|
||||
"cloud_server": "Anmeldegebiet",
|
||||
"integration_language": "Sprache",
|
||||
"oauth_redirect_url": "OAuth2-Authentifizierungs-Umleitungs-URL"
|
||||
"oauth_redirect_url": "OAuth2-Authentifizierungs-Umleitungs-URL",
|
||||
"network_detect_config": "Integrierte Netzwerkkonfiguration"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Integrierte Netzwerkkonfiguration",
|
||||
"description": "## Gebrauchsanweisung\r\n### Netzwerk-Erkennungsadresse\r\nWird verwendet, um zu überprüfen, ob das Netzwerk normal funktioniert. Wenn nicht festgelegt, wird die Standardadresse des Systems verwendet. Wenn die Standardadressprüfung fehlschlägt, können Sie versuchen, eine benutzerdefinierte Adresse einzugeben.\r\n- Sie können mehrere Erkennungsadressen eingeben, getrennt durch ein Komma, z. B. `8.8.8.8,https://www.bing.com`\r\n- Wenn es sich um eine IP-Adresse handelt, wird die Erkennung durch Ping durchgeführt. Wenn es sich um eine HTTP(s)-Adresse handelt, wird die Erkennung durch einen HTTP GET-Aufruf durchgeführt.\r\n- Wenn Sie die Standarderkennungsadresse des Systems wiederherstellen möchten, geben Sie ein Komma `,` ein und klicken Sie auf 'Weiter'.\r\n- **Diese Konfiguration ist global und Änderungen wirken sich auf andere Integrationsinstanzen aus. Bitte ändern Sie sie mit Vorsicht.**\r\n### Überprüfung der Netzwerkabhängigkeiten\r\nÜberprüfen Sie nacheinander, ob die folgenden Netzwerkabhängigkeiten zugänglich sind. Wenn die entsprechenden Adressen nicht zugänglich sind, führt dies zu Integrationsfehlern.\r\n- OAuth2-Authentifizierungsadresse: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API-Adresse: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API-Adresse: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT Broker-Adresse: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Netzwerkerkennungsadresse",
|
||||
"check_network_deps": "Überprüfung der Netzwerkabhängigkeiten"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Erweiterte Einstellungen",
|
||||
"description": "## Gebrauchsanweisung\r\n### Wenn Sie die Bedeutung der folgenden Optionen nicht genau kennen, belassen Sie sie bitte auf den Standardeinstellungen.\r\n### Geräte filtern\r\nUnterstützt das Filtern von Geräten nach Raumnamen und Gerätetypen sowie das Filtern nach Gerätedimensionen.\r\n\r\n### Steuerungsmodus\r\n- Automatisch: Wenn ein verfügbarer Xiaomi-Hub im lokalen Netzwerk vorhanden ist, priorisiert Home Assistant das Senden von Steuerbefehlen über den Hub, um eine lokale Steuerung zu ermöglichen. Wenn kein Hub vorhanden ist, wird versucht, Steuerbefehle über das Xiaomi-OT-Protokoll zu senden. Nur wenn diese Bedingungen für die lokale Steuerung nicht erfüllt sind, werden die Befehle über die Cloud gesendet.\r\n- Cloud: Steuerbefehle werden ausschließlich über die Cloud gesendet.\r\n### Action-Debug-Modus\r\nFür Methoden, die von MIoT-Spec-V2-Geräten definiert werden, wird neben der Benachrichtigungsentität auch eine Texteingabe-Entität erstellt, mit der Sie während des Debuggens Steuerbefehle an das Gerät senden können.\r\n### Nicht standardmäßige Entitäten ausblenden\r\nBlendet Entitäten aus, die von nicht-standardmäßigen MIoT-Spec-V2-Instanzen generiert werden und deren Name mit „*“ beginnt.\r\n### Gerätestatusänderungen anzeigen\r\nDetaillierte Anzeige von Gerätestatusänderungen, es werden nur die ausgewählten Benachrichtigungen angezeigt.",
|
||||
"description": "## Gebrauchsanweisung\r\n### Wenn Sie die Bedeutung der folgenden Optionen nicht genau kennen, belassen Sie sie bitte auf den Standardeinstellungen.\r\n### Geräte filtern\r\nUnterstützt das Filtern von Geräten nach Raumnamen und Gerätetypen sowie das Filtern nach Gerätedimensionen.\r\n\r\n### Steuerungsmodus\r\n- Automatisch: Wenn ein verfügbarer Xiaomi-Hub im lokalen Netzwerk vorhanden ist, priorisiert Home Assistant das Senden von Steuerbefehlen über den Hub, um eine lokale Steuerung zu ermöglichen. Wenn kein Hub vorhanden ist, wird versucht, Steuerbefehle über das Xiaomi-OT-Protokoll zu senden. Nur wenn diese Bedingungen für die lokale Steuerung nicht erfüllt sind, werden die Befehle über die Cloud gesendet.\r\n- Cloud: Steuerbefehle werden ausschließlich über die Cloud gesendet.\r\n### Action-Debug-Modus\r\nFür Methoden, die von MIoT-Spec-V2-Geräten definiert werden, wird neben der Benachrichtigungsentität auch eine Texteingabe-Entität erstellt, mit der Sie während des Debuggens Steuerbefehle an das Gerät senden können.\r\n### Nicht standardmäßige Entitäten ausblenden\r\nBlendet Entitäten aus, die von nicht-standardmäßigen MIoT-Spec-V2-Instanzen generiert werden und deren Name mit „*“ beginnt.\r\n### Binärsensor-Anzeigemodus\r\nZeigt Binärsensoren in Xiaomi Home als Textsensor-Entität oder Binärsensor-Entität an。\r\n### Gerätestatusänderungen anzeigen\r\nDetaillierte Anzeige von Gerätestatusänderungen, es werden nur die ausgewählten Benachrichtigungen angezeigt.",
|
||||
"data": {
|
||||
"devices_filter": "Geräte filtern",
|
||||
"ctrl_mode": "Steuerungsmodus",
|
||||
"action_debug": "Action-Debug-Modus",
|
||||
"hide_non_standard_entities": "Nicht standardmäßige Entitäten ausblenden",
|
||||
"display_binary_mode": "Binärsensor-Anzeigemodus",
|
||||
"display_devices_changed_notify": "Gerätestatusänderungen anzeigen"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Lokaler Geräteerkennungsdienst ist nicht verfügbar.",
|
||||
"get_cert_error": "Fehler beim Abrufen des Gateway-Zertifikats.",
|
||||
"no_family_selected": "Keine Familie ausgewählt.",
|
||||
"no_devices": "In der ausgewählten Familie sind keine Geräte enthalten. Bitte wählen Sie eine Familie mit Geräten aus und fahren Sie fort.",
|
||||
"no_central_device": "Im Modus \"Xiaomi Central Hub Gateway\" muss ein verfügbares Xiaomi Central Hub Gateway im lokalen Netzwerk von Home Assistant vorhanden sein. Stellen Sie sicher, dass die ausgewählte Familie diese Anforderungen erfüllt."
|
||||
"no_devices": "Im ausgewählten Haushalt sind keine Geräte vorhanden. Bitte wählen Sie einen Haushalt mit Geräten aus und fahren Sie fort.",
|
||||
"no_filter_devices": "Gefilterte Geräte sind leer. Bitte wählen Sie gültige Filterkriterien aus und fahren Sie fort.",
|
||||
"no_central_device": "Im Modus \"Xiaomi Central Hub Gateway\" muss ein verfügbares Xiaomi Central Hub Gateway im lokalen Netzwerk von Home Assistant vorhanden sein. Stellen Sie sicher, dass die ausgewählte Familie diese Anforderungen erfüllt.",
|
||||
"invalid_network_addr": "Ungültige IP-Adresse oder HTTP-Adresse vorhanden, bitte geben Sie eine gültige Adresse ein.",
|
||||
"invalid_ip_addr": "Unzugängliche IP-Adresse vorhanden, bitte geben Sie eine gültige IP-Adresse ein.",
|
||||
"invalid_http_addr": "Unzugängliche HTTP-Adresse vorhanden, bitte geben Sie eine gültige HTTP-Adresse ein.",
|
||||
"invalid_default_addr": "Die Standard-Netzwerkerkennungsadresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration oder verwenden Sie eine benutzerdefinierte Netzwerkerkennungsadresse.",
|
||||
"unreachable_oauth2_host": "OAuth2-Authentifizierungsadresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration.",
|
||||
"unreachable_http_host": "Xiaomi HTTP API-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration.",
|
||||
"unreachable_spec_host": "Xiaomi SPEC API-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration.",
|
||||
"unreachable_mqtt_broker": "Xiaomi MQTT Broker-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Fehler beim Abrufen der Home Assistant-UUID.",
|
||||
"network_connect_error": "Konfiguration fehlgeschlagen. Netzwerkverbindung fehlgeschlagen. Überprüfen Sie die Netzwerkkonfiguration des Geräts.",
|
||||
"already_configured": "Dieser Benutzer hat die Konfiguration bereits abgeschlossen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Konfiguration zu ändern.",
|
||||
"invalid_auth_info": "Authentifizierungsinformationen sind abgelaufen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Authentifizierung erneut durchzuführen.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Konfigurationsoptionen",
|
||||
"description": "### Hallo {nick_name}!\r\n\r\nXiaomi Home-Konto-ID: {uid}\r\nAktuelles Anmeldegebiet: {cloud_server}\r\n\r\nWählen Sie die Optionen aus, die Sie erneut konfigurieren möchten, und klicken Sie dann auf \"Weiter\".",
|
||||
"description": "### Hallo {nick_name}!\r\n\r\nXiaomi Home-Konto-ID: {uid}\r\nAktuelles Anmeldegebiet: {cloud_server}\r\nIntegrationsinstanz-ID: {instance_id}\r\n\r\nWählen Sie die Optionen aus, die Sie erneut konfigurieren möchten, und klicken Sie dann auf \"Weiter\".",
|
||||
"data": {
|
||||
"integration_language": "Integrationsprache",
|
||||
"update_user_info": "Benutzerinformationen aktualisieren",
|
||||
"update_devices": "Geräteliste aktualisieren",
|
||||
"action_debug": "Action-Debug-Modus",
|
||||
"hide_non_standard_entities": "Verstecke Nicht-Standard-Entitäten",
|
||||
"display_binary_mode": "Binärsensor-Anzeigemodus",
|
||||
"display_devices_changed_notify": "Gerätestatusänderungen anzeigen",
|
||||
"update_trans_rules": "Entitätskonvertierungsregeln aktualisieren",
|
||||
"update_lan_ctrl_config": "LAN-Steuerungskonfiguration aktualisieren"
|
||||
"update_lan_ctrl_config": "LAN-Steuerungskonfiguration aktualisieren",
|
||||
"network_detect_config": "Integrierte Netzwerkkonfiguration"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "LAN-Abonnement aktivieren"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Integrierte Netzwerkkonfiguration",
|
||||
"description": "## Gebrauchsanweisung\r\n### Netzwerk-Erkennungsadresse\r\nWird verwendet, um zu überprüfen, ob das Netzwerk normal funktioniert. Wenn nicht festgelegt, wird die Standardadresse des Systems verwendet. Wenn die Standardadressprüfung fehlschlägt, können Sie versuchen, eine benutzerdefinierte Adresse einzugeben.\r\n- Sie können mehrere Erkennungsadressen eingeben, getrennt durch ein Komma, z. B. `8.8.8.8,https://www.bing.com`\r\n- Wenn es sich um eine IP-Adresse handelt, wird die Erkennung durch Ping durchgeführt. Wenn es sich um eine HTTP(s)-Adresse handelt, wird die Erkennung durch einen HTTP GET-Aufruf durchgeführt.\r\n- Wenn Sie die Standarderkennungsadresse des Systems wiederherstellen möchten, geben Sie ein Komma `,` ein und klicken Sie auf 'Weiter'.\r\n- **Diese Konfiguration ist global und Änderungen wirken sich auf andere Integrationsinstanzen aus. Bitte ändern Sie sie mit Vorsicht.**\r\n### Überprüfung der Netzwerkabhängigkeiten\r\nÜberprüfen Sie nacheinander, ob die folgenden Netzwerkabhängigkeiten zugänglich sind. Wenn die entsprechenden Adressen nicht zugänglich sind, führt dies zu Integrationsfehlern.\r\n- OAuth2-Authentifizierungsadresse: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API-Adresse: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API-Adresse: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT Broker-Adresse: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Netzwerkerkennungsadresse",
|
||||
"check_network_deps": "Überprüfung der Netzwerkabhängigkeiten"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Bestätigen Sie die Konfiguration",
|
||||
"description": "**{nick_name}**, bitte bestätigen Sie die neuesten Konfigurationsinformationen und klicken Sie dann auf \"Senden\". Die Integration wird mit den aktualisierten Konfigurationen erneut geladen.\r\n\r\nIntegrationsprache:\t{lang_new}\r\nBenutzername:\t{nick_name_new}\r\nAction-Debug-Modus:\t{action_debug}\r\nVerstecke Nicht-Standard-Entitäten:\t{hide_non_standard_entities}\r\nGerätestatusänderungen anzeigen:\t{display_devices_changed_notify}\r\nGeräteänderungen:\t{devices_add} neue Geräte hinzufügen, {devices_remove} Geräte entfernen\r\nKonvertierungsregeländerungen:\tInsgesamt {trans_rules_count} Regeln, aktualisiert {trans_rules_count_success} Regeln",
|
||||
@ -168,11 +198,20 @@
|
||||
"get_homeinfo_error": "Fehler beim Abrufen von Home-Informationen.",
|
||||
"get_cert_error": "Fehler beim Abrufen des Zentralzertifikats.",
|
||||
"no_family_selected": "Keine Familie ausgewählt.",
|
||||
"no_devices": "In der ausgewählten Familie sind keine Geräte vorhanden. Bitte wählen Sie eine Familie mit Geräten und fahren Sie dann fort.",
|
||||
"no_devices": "Im ausgewählten Haushalt sind keine Geräte vorhanden. Bitte wählen Sie einen Haushalt mit Geräten aus und fahren Sie fort.",
|
||||
"no_filter_devices": "Gefilterte Geräte sind leer. Bitte wählen Sie gültige Filterkriterien aus und fahren Sie fort.",
|
||||
"no_central_device": "Der Modus \"Zentral Gateway\" erfordert ein verfügbares Xiaomi-Zentral-Gateway im lokalen Netzwerk, in dem Home Assistant installiert ist. Überprüfen Sie, ob die ausgewählte Familie diese Anforderung erfüllt.",
|
||||
"mdns_discovery_error": "Lokaler Geräteerkennungsdienstfehler.",
|
||||
"update_config_error": "Fehler beim Aktualisieren der Konfigurationsinformationen.",
|
||||
"not_confirm": "Änderungen wurden nicht bestätigt. Bitte bestätigen Sie die Auswahl, bevor Sie sie einreichen."
|
||||
"not_confirm": "Änderungen wurden nicht bestätigt. Bitte bestätigen Sie die Auswahl, bevor Sie sie einreichen.",
|
||||
"invalid_network_addr": "Ungültige IP-Adresse oder HTTP-Adresse vorhanden, bitte geben Sie eine gültige Adresse ein.",
|
||||
"invalid_ip_addr": "Unzugängliche IP-Adresse vorhanden, bitte geben Sie eine gültige IP-Adresse ein.",
|
||||
"invalid_http_addr": "Unzugängliche HTTP-Adresse vorhanden, bitte geben Sie eine gültige HTTP-Adresse ein.",
|
||||
"invalid_default_addr": "Die Standard-Netzwerkerkennungsadresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration oder verwenden Sie eine benutzerdefinierte Netzwerkerkennungsadresse.",
|
||||
"unreachable_oauth2_host": "OAuth2-Authentifizierungsadresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration.",
|
||||
"unreachable_http_host": "Xiaomi HTTP API-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration.",
|
||||
"unreachable_spec_host": "Xiaomi SPEC API-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration.",
|
||||
"unreachable_mqtt_broker": "Xiaomi MQTT Broker-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "Konfiguration fehlgeschlagen. Netzwerkverbindungsfehler. Überprüfen Sie die Netzwerkkonfiguration des Geräts.",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Basic configuration",
|
||||
"description": "### Login Region\r\nSelect the region of your Xiaomi account. You can find it in the Xiaomi Home APP > Profile (located in the menu at the bottom) > Additional settings > About Xiaomi Home.\r\n### Language\r\nSelect the language of the device and entity names. Some sentences without translation will be displayed in English.\r\n### OAuth2 Redirect URL\r\nThe OAuth2 authentication redirect address is **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. The Home Assistant needs to be in the same local area network as the current operating terminal (e.g., the personal computer) and the operating terminal can access the Home Assistant home page through this address. Otherwise, the login authentication may fail.\r\n### Note\r\n- For users with hundreds or more Mi Home devices, the initial addition of the integration will take some time. Please be patient.\r\n- If Home Assistant is running in a Docker environment, please ensure that the Docker network mode is set to host, otherwise local control functionality may not work properly.\r\n- The local control functionality of the integration has some dependencies. Please read the README carefully.",
|
||||
"description": "### Login Region\r\nSelect the region of your Xiaomi account. You can find it in the Xiaomi Home APP > Profile (located in the menu at the bottom) > Additional settings > About Xiaomi Home.\r\n### Language\r\nSelect the language of the device and entity names. Some sentences without translation will be displayed in English.\r\n### OAuth2 Redirect URL\r\nThe OAuth2 authentication redirect address is **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. The Home Assistant needs to be in the same local area network as the current operating terminal (e.g., the personal computer) and the operating terminal can access the Home Assistant home page through this address. Otherwise, the login authentication may fail.\r\n### Integrated Network Configuration\r\nCheck if the local network is functioning properly and if the related network resources are accessible. **It is recommended to select this when adding for the first time.**\r\n### Note\r\n- For users with hundreds or more Mi Home devices, the initial addition of the integration will take some time. Please be patient.\r\n- If Home Assistant is running in a Docker environment, please ensure that the Docker network mode is set to host, otherwise local control functionality may not work properly.\r\n- The local control functionality of the integration has some dependencies. Please read the README carefully.",
|
||||
"data": {
|
||||
"cloud_server": "Login Region",
|
||||
"integration_language": "Language",
|
||||
"oauth_redirect_url": "OAuth2 Redirect URL"
|
||||
"oauth_redirect_url": "OAuth2 Redirect URL",
|
||||
"network_detect_config": "Integrated Network Configuration"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Integrated Network Configuration",
|
||||
"description": "## Usage Introduction\r\n### Network Detection Address\r\nUsed to check if the network is functioning properly. If not set, the system default address will be used. If the default address check fails, you can try entering a custom address.\r\n- You can enter multiple detection addresses, separated by commas, such as `8.8.8.8,https://www.bing.com`\r\n- If it is an IP address, detection will be done via ping. If it is an HTTP(s) address, detection will be done via HTTP GET request.\r\n- If you want to restore the system default detection address, please enter a comma `,` and click 'Next'.\r\n- **This configuration is global, and changes will affect other integration instances. Please modify with caution.**\r\n### Check Network Dependencies\r\nCheck the following network dependencies one by one to see if they are accessible. If the related addresses are not accessible, it will cause integration issues.\r\n- OAuth2 Authentication Address: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API Address: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API Address: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT Broker Address: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Network Detection Address",
|
||||
"check_network_deps": "Check Network Dependencies"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Advanced Settings",
|
||||
"description": "## Introduction\r\n### Unless you are very clear about the meaning of the following options, please keep the default settings.\r\n### Filter Devices\r\nSupports filtering devices by room name and device type, and also supports device dimension filtering.\r\n### Control Mode\r\n- Auto: When there is an available Xiaomi central hub gateway in the local area network, Home Assistant will prioritize sending device control commands through the central hub gateway to achieve local control. If there is no central hub gateway in the local area network, it will attempt to send control commands through Xiaomi OT protocol to achieve local control. Only when the above local control conditions are not met, the device control commands will be sent through the cloud.\r\n- Cloud: All control commands are sent through the cloud.\r\n### Action Debug Mode\r\nFor the methods defined by the device MIoT-Spec-V2, in addition to generating notification entities, a text input box entity will also be generated. You can use it to send control commands to the device during debugging.\r\n### Hide Non-Standard Generated Entities\r\nHide entities generated by non-standard MIoT-Spec-V2 instances with names starting with \"*\".\r\n### Display Device Status Change Notifications\r\nDisplay detailed device status change notifications, only showing the selected notifications.",
|
||||
"description": "## Introduction\r\n### Unless you are very clear about the meaning of the following options, please keep the default settings.\r\n### Filter Devices\r\nSupports filtering devices by room name and device type, and also supports device dimension filtering.\r\n### Control Mode\r\n- Auto: When there is an available Xiaomi central hub gateway in the local area network, Home Assistant will prioritize sending device control commands through the central hub gateway to achieve local control. If there is no central hub gateway in the local area network, it will attempt to send control commands through Xiaomi OT protocol to achieve local control. Only when the above local control conditions are not met, the device control commands will be sent through the cloud.\r\n- Cloud: All control commands are sent through the cloud.\r\n### Action Debug Mode\r\nFor the methods defined by the device MIoT-Spec-V2, in addition to generating notification entities, a text input box entity will also be generated. You can use it to send control commands to the device during debugging.\r\n### Hide Non-Standard Generated Entities\r\nHide entities generated by non-standard MIoT-Spec-V2 instances with names starting with \"*\".\r\n### Binary Sensor Display Mode\r\nDisplay binary sensors in Xiaomi Home as text sensor entity or binary sensor entity。\r\n### Display Device Status Change Notifications\r\nDisplay detailed device status change notifications, only showing the selected notifications.",
|
||||
"data": {
|
||||
"devices_filter": "Filter Devices",
|
||||
"ctrl_mode": "Control Mode",
|
||||
"action_debug": "Action Debug Mode",
|
||||
"hide_non_standard_entities": "Hide Non-Standard Generated Entities",
|
||||
"display_binary_mode": "Binary Sensor Display Mode",
|
||||
"display_devices_changed_notify": "Display Device Status Change Notifications"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Local device discovery service exception.",
|
||||
"get_cert_error": "Failed to retrieve the central hub gateway certificate.",
|
||||
"no_family_selected": "No home selected.",
|
||||
"no_devices": "The selected home does not have any devices. Please choose a home containing devices and continue.",
|
||||
"no_central_device": "[Central Hub Gateway Mode] requires a Xiaomi central hub gateway available in the local network where Home Assistant exists. Please check if the selected home meets the requirement."
|
||||
"no_devices": "There are no devices in the selected home. Please select a home with devices and continue.",
|
||||
"no_filter_devices": "Filtered devices are empty. Please select valid filter criteria and continue.",
|
||||
"no_central_device": "[Central Hub Gateway Mode] requires a Xiaomi central hub gateway available in the local network where Home Assistant exists. Please check if the selected home meets the requirement.",
|
||||
"invalid_network_addr": "Invalid IP address or HTTP address detected, please enter a valid address.",
|
||||
"invalid_ip_addr": "Unreachable IP address detected, please enter a valid IP address.",
|
||||
"invalid_http_addr": "Unreachable HTTP address detected, please enter a valid HTTP address.",
|
||||
"invalid_default_addr": "Default network detection address is unreachable, please check network configuration or use a custom network detection address.",
|
||||
"unreachable_oauth2_host": "Unable to reach OAuth2 authentication address, please check network configuration.",
|
||||
"unreachable_http_host": "Unable to reach Xiaomi HTTP API address, please check network configuration.",
|
||||
"unreachable_spec_host": "Unable to reach Xiaomi SPEC API address, please check network configuration.",
|
||||
"unreachable_mqtt_broker": "Unable to reach Xiaomi MQTT Broker address, please check network configuration."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Failed to get Home Assistant UUID.",
|
||||
"network_connect_error": "Configuration failed. The network connection is abnormal. Please check the equipment network configuration.",
|
||||
"already_configured": "Configuration for this user is already completed. Please go to the integration page and click the CONFIGURE button for modifications.",
|
||||
"invalid_auth_info": "Authentication information has expired. Please go to the integration page and click the CONFIGURE button to re-authenticate.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Configuration Options",
|
||||
"description": "### Hello, {nick_name}\r\n\r\nXiaomi ID: {uid}\r\nCurrent Login Region: {cloud_server}\r\n\r\nPlease select the options you need to configure, then click NEXT.",
|
||||
"description": "### Hello, {nick_name}\r\n\r\nXiaomi ID: {uid}\r\nCurrent Login Region: {cloud_server}\r\nIntegration Instance ID: {instance_id}\r\n\r\nPlease select the options you need to configure, then click NEXT.",
|
||||
"data": {
|
||||
"integration_language": "Integration Language",
|
||||
"update_user_info": "Update user information",
|
||||
"update_devices": "Update device list",
|
||||
"action_debug": "Debug mode for action",
|
||||
"hide_non_standard_entities": "Hide non-standard created entities",
|
||||
"display_binary_mode": "Binary Sensor Display Mode",
|
||||
"display_devices_changed_notify": "Display device status change notifications",
|
||||
"update_trans_rules": "Update entity conversion rules",
|
||||
"update_lan_ctrl_config": "Update LAN control configuration"
|
||||
"update_lan_ctrl_config": "Update LAN control configuration",
|
||||
"network_detect_config": "Integrated Network Configuration"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "Enable LAN subscription"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Integrated Network Configuration",
|
||||
"description": "## Usage Introduction\r\n### Network Detection Address\r\nUsed to check if the network is functioning properly. If not set, the system default address will be used. If the default address check fails, you can try entering a custom address.\r\n- You can enter multiple detection addresses, separated by commas, such as `8.8.8.8,https://www.bing.com`\r\n- If it is an IP address, detection will be done via ping. If it is an HTTP(s) address, detection will be done via HTTP GET request.\r\n- If you want to restore the system default detection address, please enter a comma `,` and click 'Next'.\r\n- **This configuration is global, and changes will affect other integration instances. Please modify with caution.**\r\n### Check Network Dependencies\r\nCheck the following network dependencies one by one to see if they are accessible. If the related addresses are not accessible, it will cause integration issues.\r\n- OAuth2 Authentication Address: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API Address: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API Address: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT Broker Address: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Network Detection Address",
|
||||
"check_network_deps": "Check Network Dependencies"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirm Configuration",
|
||||
"description": "Hello **{nick_name}**, please confirm the latest configuration information and then Click SUBMIT.\r\nThe integration will reload using the updated configuration.\r\n\r\nIntegration Language: \t{lang_new}\r\nNickname: \t{nick_name_new}\r\nDebug mode for action: \t{action_debug}\r\nHide non-standard created entities: \t{hide_non_standard_entities}\r\nDisplay device status change notifications:\t{display_devices_changed_notify}\r\nDevice Changes: \tAdd **{devices_add}** devices, Remove **{devices_remove}** devices\r\nTransformation rules change: \tThere are a total of **{trans_rules_count}** rules, and updated **{trans_rules_count_success}** rules",
|
||||
@ -167,12 +197,21 @@
|
||||
"get_token_error": "Failed to retrieve login authorization information (OAuth token).",
|
||||
"get_homeinfo_error": "Failed to retrieve home information.",
|
||||
"get_cert_error": "Failed to retrieve the central hub gateway certificate.",
|
||||
"no_devices": "The selected home does not have any devices. Please choose a home containing devices and continue.",
|
||||
"no_devices": "There are no devices in the selected home. Please select a home with devices and continue.",
|
||||
"no_filter_devices": "Filtered devices are empty. Please select valid filter criteria and continue.",
|
||||
"no_family_selected": "No home selected.",
|
||||
"no_central_device": "[Central Hub Gateway Mode] requires a Xiaomi central hub gateway available in the local network where Home Assistant exists. Please check if the selected home meets the requirement.",
|
||||
"mdns_discovery_error": "Local device discovery service exception.",
|
||||
"update_config_error": "Failed to update configuration information.",
|
||||
"not_confirm": "Changes are not confirmed. Please confirm the change before submitting."
|
||||
"not_confirm": "Changes are not confirmed. Please confirm the change before submitting.",
|
||||
"invalid_network_addr": "Invalid IP address or HTTP address detected, please enter a valid address.",
|
||||
"invalid_ip_addr": "Unreachable IP address detected, please enter a valid IP address.",
|
||||
"invalid_http_addr": "Unreachable HTTP address detected, please enter a valid HTTP address.",
|
||||
"invalid_default_addr": "Default network detection address is unreachable, please check network configuration or use a custom network detection address.",
|
||||
"unreachable_oauth2_host": "Unable to reach OAuth2 authentication address, please check network configuration.",
|
||||
"unreachable_http_host": "Unable to reach Xiaomi HTTP API address, please check network configuration.",
|
||||
"unreachable_spec_host": "Unable to reach Xiaomi SPEC API address, please check network configuration.",
|
||||
"unreachable_mqtt_broker": "Unable to reach Xiaomi MQTT Broker address, please check network configuration."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "Configuration failed. The network connection is abnormal. Please check the equipment network configuration.",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Configuración básica",
|
||||
"description": "### Región de inicio de sesión\r\nSeleccione la región donde se encuentra su cuenta de Xiaomi. Puede consultar esta información en `Xiaomi Home APP > Yo (ubicado en el menú inferior) > Más ajustes > Acerca de Xiaomi Home`.\r\n### Idioma\r\nSeleccione el idioma utilizado para los nombres de los dispositivos y entidades. Las partes de las frases que no están traducidas se mostrarán en inglés.\r\n### Dirección de redireccionamiento de autenticación de OAuth2\r\nLa dirección de redireccionamiento de autenticación de OAuth2 es **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant debe estar en la misma red local que el terminal de operación actual (por ejemplo, una computadora personal) y el terminal de operación debe poder acceder a la página de inicio de Home Assistant a través de esta dirección, de lo contrario, la autenticación de inicio de sesión podría fallar.\r\n### Nota\r\n- Para los usuarios con cientos o más dispositivos Mi Home, la adición inicial de la integración tomará algún tiempo. Por favor, sea paciente.\r\n- Si Home Assistant se está ejecutando en un entorno Docker, asegúrese de que el modo de red de Docker esté configurado en host, de lo contrario, la funcionalidad de control local puede no funcionar correctamente.\r\n- La funcionalidad de control local de la integración tiene algunas dependencias. Por favor, lea el README cuidadosamente.",
|
||||
"description": "### Región de inicio de sesión\r\nSeleccione la región donde se encuentra su cuenta de Xiaomi. Puede consultar esta información en `Xiaomi Home APP > Yo (ubicado en el menú inferior) > Más ajustes > Acerca de Xiaomi Home`.\r\n### Idioma\r\nSeleccione el idioma utilizado para los nombres de los dispositivos y entidades. Las partes de las frases que no están traducidas se mostrarán en inglés.\r\n### Dirección de redireccionamiento de autenticación de OAuth2\r\nLa dirección de redireccionamiento de autenticación de OAuth2 es **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant debe estar en la misma red local que el terminal de operación actual (por ejemplo, una computadora personal) y el terminal de operación debe poder acceder a la página de inicio de Home Assistant a través de esta dirección, de lo contrario, la autenticación de inicio de sesión podría fallar.\r\n### Configuración de Red Integrada\r\nVerifique si la red local funciona correctamente y si los recursos de red relacionados son accesibles. **Se recomienda seleccionar esto al agregar por primera vez.**\r\n### Nota\r\n- Para los usuarios con cientos o más dispositivos Mi Home, la adición inicial de la integración tomará algún tiempo. Por favor, sea paciente.\r\n- Si Home Assistant se está ejecutando en un entorno Docker, asegúrese de que el modo de red de Docker esté configurado en host, de lo contrario, la funcionalidad de control local puede no funcionar correctamente.\r\n- La funcionalidad de control local de la integración tiene algunas dependencias. Por favor, lea el README cuidadosamente.",
|
||||
"data": {
|
||||
"cloud_server": "Región de inicio de sesión",
|
||||
"integration_language": "Idioma",
|
||||
"oauth_redirect_url": "Dirección de redireccionamiento de autenticación de OAuth2"
|
||||
"oauth_redirect_url": "Dirección de redireccionamiento de autenticación de OAuth2",
|
||||
"network_detect_config": "Configuración de Red Integrada"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuración de Red Integrada",
|
||||
"description": "## Introducción al Uso\r\n### Dirección de Detección de Red\r\nSe utiliza para verificar si la red funciona correctamente. Si no se establece, se utilizará la dirección predeterminada del sistema. Si la verificación de la dirección predeterminada falla, puede intentar ingresar una dirección personalizada.\r\n- Puede ingresar varias direcciones de detección, separadas por comas, como `8.8.8.8,https://www.bing.com`\r\n- Si es una dirección IP, la detección se realizará mediante ping. Si es una dirección HTTP(s), la detección se realizará mediante una solicitud HTTP GET.\r\n- Si desea restaurar la dirección de detección predeterminada del sistema, ingrese una coma `,` y haga clic en 'Siguiente'.\r\n- **Esta configuración es global y los cambios afectarán a otras instancias de integración. Modifique con precaución.**\r\n### Verificar Dependencias de Red\r\nVerifique una por una las siguientes dependencias de red para ver si son accesibles. Si las direcciones relacionadas no son accesibles, causará problemas de integración.\r\n- Dirección de Autenticación OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Dirección de API HTTP de Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Dirección de API SPEC de Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Dirección del Broker MQTT de Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Dirección de Detección de Red",
|
||||
"check_network_deps": "Verificar Dependencias de Red"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Opciones Avanzadas",
|
||||
"description": "## Introducción\r\n### A menos que entienda claramente el significado de las siguientes opciones, manténgalas en su configuración predeterminada.\r\n### Filtrar dispositivos\r\nAdmite la filtración de dispositivos por nombre de habitación y tipo de dispositivo, y también admite la filtración por familia.\r\n### Modo de Control\r\n- Automático: Cuando hay una puerta de enlace central de Xiaomi disponible en la red local, Home Assistant enviará comandos de control de dispositivos a través de la puerta de enlace central para lograr la función de control local. Cuando no hay una puerta de enlace central en la red local, intentará enviar comandos de control a través del protocolo OT de Xiaomi para lograr la función de control local. Solo cuando no se cumplan las condiciones de control local anteriores, los comandos de control de dispositivos se enviarán a través de la nube.\r\n- Nube: Los comandos de control solo se envían a través de la nube.\r\n### Modo de Depuración de Acciones\r\nPara los métodos definidos por el dispositivo MIoT-Spec-V2, además de generar una entidad de notificación, también se generará una entidad de cuadro de texto que se puede utilizar para enviar comandos de control al dispositivo durante la depuración.\r\n### Ocultar Entidades Generadas No Estándar\r\nOcultar entidades generadas por instancias MIoT-Spec-V2 no estándar que comienzan con \"*\".\r\n### Mostrar notificaciones de cambio de estado del dispositivo\r\nMostrar notificaciones detalladas de cambio de estado del dispositivo, mostrando solo las notificaciones seleccionadas.",
|
||||
"description": "## Introducción\r\n### A menos que entienda claramente el significado de las siguientes opciones, manténgalas en su configuración predeterminada.\r\n### Filtrar dispositivos\r\nAdmite la filtración de dispositivos por nombre de habitación y tipo de dispositivo, y también admite la filtración por familia.\r\n### Modo de Control\r\n- Automático: Cuando hay una puerta de enlace central de Xiaomi disponible en la red local, Home Assistant enviará comandos de control de dispositivos a través de la puerta de enlace central para lograr la función de control local. Cuando no hay una puerta de enlace central en la red local, intentará enviar comandos de control a través del protocolo OT de Xiaomi para lograr la función de control local. Solo cuando no se cumplan las condiciones de control local anteriores, los comandos de control de dispositivos se enviarán a través de la nube.\r\n- Nube: Los comandos de control solo se envían a través de la nube.\r\n### Modo de Depuración de Acciones\r\nPara los métodos definidos por el dispositivo MIoT-Spec-V2, además de generar una entidad de notificación, también se generará una entidad de cuadro de texto que se puede utilizar para enviar comandos de control al dispositivo durante la depuración.\r\n### Ocultar Entidades Generadas No Estándar\r\nOcultar entidades generadas por instancias MIoT-Spec-V2 no estándar que comienzan con \"*\".\r\n### Modo de visualización del sensor binario\r\nMuestra los sensores binarios en Xiaomi Home como entidad de sensor de texto o entidad de sensor binario。\r\n### Mostrar notificaciones de cambio de estado del dispositivo\r\nMostrar notificaciones detalladas de cambio de estado del dispositivo, mostrando solo las notificaciones seleccionadas.",
|
||||
"data": {
|
||||
"devices_filter": "Filtrar Dispositivos",
|
||||
"ctrl_mode": "Modo de Control",
|
||||
"action_debug": "Modo de Depuración de Acciones",
|
||||
"hide_non_standard_entities": "Ocultar Entidades Generadas No Estándar",
|
||||
"display_binary_mode": "Modo de visualización del sensor binario",
|
||||
"display_devices_changed_notify": "Mostrar notificaciones de cambio de estado del dispositivo"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Error en el servicio de descubrimiento de dispositivos locales.",
|
||||
"get_cert_error": "Error al obtener el certificado de la puerta de enlace.",
|
||||
"no_family_selected": "No se ha seleccionado ningún hogar.",
|
||||
"no_devices": "No hay dispositivos en el hogar seleccionado. Seleccione un hogar con dispositivos y continúe.",
|
||||
"no_central_device": "【Modo de puerta de enlace central】Se requiere una puerta de enlace Xiaomi disponible en la red local donde se encuentra Home Assistant. Verifique si el hogar seleccionado cumple con este requisito."
|
||||
"no_devices": "No hay dispositivos en el hogar seleccionado. Por favor, seleccione un hogar con dispositivos y continúe.",
|
||||
"no_filter_devices": "Los dispositivos filtrados están vacíos. Por favor, seleccione criterios de filtro válidos y continúe.",
|
||||
"no_central_device": "【Modo de puerta de enlace central】Se requiere una puerta de enlace Xiaomi disponible en la red local donde se encuentra Home Assistant. Verifique si el hogar seleccionado cumple con este requisito.",
|
||||
"invalid_network_addr": "Se detectó una dirección IP o HTTP no válida, por favor ingrese una dirección válida.",
|
||||
"invalid_ip_addr": "Se detectó una dirección IP inaccesible, por favor ingrese una dirección IP válida.",
|
||||
"invalid_http_addr": "Se detectó una dirección HTTP inaccesible, por favor ingrese una dirección HTTP válida.",
|
||||
"invalid_default_addr": "La dirección de detección de red predeterminada no es accesible, por favor verifique la configuración de la red o use una dirección de detección de red personalizada.",
|
||||
"unreachable_oauth2_host": "No se puede acceder a la dirección de autenticación OAuth2, por favor verifique la configuración de la red.",
|
||||
"unreachable_http_host": "No se puede acceder a la dirección de la API HTTP de Xiaomi, por favor verifique la configuración de la red.",
|
||||
"unreachable_spec_host": "No se puede acceder a la dirección de la API SPEC de Xiaomi, por favor verifique la configuración de la red.",
|
||||
"unreachable_mqtt_broker": "No se puede acceder a la dirección del Broker MQTT de Xiaomi, por favor verifique la configuración de la red."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Error al obtener el UUID de Home Assistant.",
|
||||
"network_connect_error": "La configuración ha fallado. Existe un problema con la conexión de red, verifique la configuración de red del dispositivo.",
|
||||
"already_configured": "Esta cuenta ya ha finalizado la configuración. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para modificar la configuración.",
|
||||
"invalid_auth_info": "La información de autorización ha caducado. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para volver a autenticarse.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Opciones de configuración",
|
||||
"description": "### ¡Hola, {nick_name}!\r\n\r\nID de cuenta de Xiaomi: {uid}\r\nRegión de inicio de sesión actual: {cloud_server}\r\n\r\nSeleccione las opciones que desea reconfigurar y haga clic en \"Siguiente\".",
|
||||
"description": "### ¡Hola, {nick_name}!\r\n\r\nID de cuenta de Xiaomi: {uid}\r\nRegión de inicio de sesión actual: {cloud_server}\r\nID de Instancia de Integración: {instance_id}\r\n\r\nSeleccione las opciones que desea reconfigurar y haga clic en \"Siguiente\".",
|
||||
"data": {
|
||||
"integration_language": "Idioma de la integración",
|
||||
"update_user_info": "Actualizar información de usuario",
|
||||
"update_devices": "Actualizar lista de dispositivos",
|
||||
"action_debug": "Modo de depuración de Action",
|
||||
"hide_non_standard_entities": "Ocultar entidades generadas no estándar",
|
||||
"display_binary_mode": "Modo de visualización del sensor binario",
|
||||
"display_devices_changed_notify": "Mostrar notificaciones de cambio de estado del dispositivo",
|
||||
"update_trans_rules": "Actualizar reglas de conversión de entidad",
|
||||
"update_lan_ctrl_config": "Actualizar configuración de control LAN"
|
||||
"update_lan_ctrl_config": "Actualizar configuración de control LAN",
|
||||
"network_detect_config": "Configuración de Red Integrada"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "Habilitar suscripción LAN"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuración de Red Integrada",
|
||||
"description": "## Introducción al Uso\r\n### Dirección de Detección de Red\r\nSe utiliza para verificar si la red funciona correctamente. Si no se establece, se utilizará la dirección predeterminada del sistema. Si la verificación de la dirección predeterminada falla, puede intentar ingresar una dirección personalizada.\r\n- Puede ingresar varias direcciones de detección, separadas por comas, como `8.8.8.8,https://www.bing.com`\r\n- Si es una dirección IP, la detección se realizará mediante ping. Si es una dirección HTTP(s), la detección se realizará mediante una solicitud HTTP GET.\r\n- Si desea restaurar la dirección de detección predeterminada del sistema, ingrese una coma `,` y haga clic en 'Siguiente'.\r\n- **Esta configuración es global y los cambios afectarán a otras instancias de integración. Modifique con precaución.**\r\n### Verificar Dependencias de Red\r\nVerifique una por una las siguientes dependencias de red para ver si son accesibles. Si las direcciones relacionadas no son accesibles, causará problemas de integración.\r\n- Dirección de Autenticación OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Dirección de API HTTP de Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Dirección de API SPEC de Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Dirección del Broker MQTT de Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Dirección de Detección de Red",
|
||||
"check_network_deps": "Verificar Dependencias de Red"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmar configuración",
|
||||
"description": "¡Hola, **{nick_name}**! Por favor, confirme la última información de configuración y haga clic en \"Enviar\" para finalizar la configuración.\r\nLa integración se volverá a cargar con la nueva configuración.\r\n\r\nIdioma de la integración:\t{lang_new}\r\nApodo de usuario:\t{nick_name_new}\r\nModo de depuración de Action:\t{action_debug}\r\nOcultar entidades generadas no estándar:\t{hide_non_standard_entities}\r\nMostrar notificaciones de cambio de estado del dispositivo:\t{display_devices_changed_notify}\r\nCambios de dispositivos:\t{devices_add} dispositivos agregados, {devices_remove} dispositivos eliminados\r\nCambios en las reglas de conversión:\t{trans_rules_count} reglas en total, {trans_rules_count_success} reglas actualizadas",
|
||||
@ -168,11 +198,20 @@
|
||||
"get_homeinfo_error": "Error al obtener la información del hogar.",
|
||||
"get_cert_error": "Error al obtener el certificado de la puerta de enlace.",
|
||||
"no_family_selected": "No se ha seleccionado ningún hogar.",
|
||||
"no_devices": "No hay dispositivos en el hogar seleccionado. Seleccione un hogar con dispositivos y continúe.",
|
||||
"no_devices": "No hay dispositivos en el hogar seleccionado. Por favor, seleccione un hogar con dispositivos y continúe.",
|
||||
"no_filter_devices": "Los dispositivos filtrados están vacíos. Por favor, seleccione criterios de filtro válidos y continúe.",
|
||||
"no_central_device": "【Modo de puerta de enlace central】Se requiere una puerta de enlace Xiaomi disponible en la red local donde se encuentra Home Assistant. Verifique si el hogar seleccionado cumple con este requisito.",
|
||||
"mdns_discovery_error": "Error en el servicio de descubrimiento de dispositivos locales.",
|
||||
"update_config_error": "Error al actualizar la información de configuración.",
|
||||
"not_confirm": "No se ha confirmado la opción de modificación. Seleccione y confirme la opción antes de enviar."
|
||||
"not_confirm": "No se ha confirmado la opción de modificación. Seleccione y confirme la opción antes de enviar.",
|
||||
"invalid_network_addr": "Se detectó una dirección IP o HTTP no válida, por favor ingrese una dirección válida.",
|
||||
"invalid_ip_addr": "Se detectó una dirección IP inaccesible, por favor ingrese una dirección IP válida.",
|
||||
"invalid_http_addr": "Se detectó una dirección HTTP inaccesible, por favor ingrese una dirección HTTP válida.",
|
||||
"invalid_default_addr": "La dirección de detección de red predeterminada no es accesible, por favor verifique la configuración de la red o use una dirección de detección de red personalizada.",
|
||||
"unreachable_oauth2_host": "No se puede acceder a la dirección de autenticación OAuth2, por favor verifique la configuración de la red.",
|
||||
"unreachable_http_host": "No se puede acceder a la dirección de la API HTTP de Xiaomi, por favor verifique la configuración de la red.",
|
||||
"unreachable_spec_host": "No se puede acceder a la dirección de la API SPEC de Xiaomi, por favor verifique la configuración de la red.",
|
||||
"unreachable_mqtt_broker": "No se puede acceder a la dirección del Broker MQTT de Xiaomi, por favor verifique la configuración de la red."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "La configuración ha fallado. Existe un problema con la conexión de red, verifique la configuración de red del dispositivo.",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Configuration de base",
|
||||
"description": "### Région de connexion\r\nSélectionnez la région où se trouve votre compte Xiaomi. Vous pouvez le trouver dans `Xiaomi Home APP > Mon (situé dans le menu inférieur) > Plus de paramètres > À propos de Xiaomi Home`.\r\n### Langue\r\nChoisissez la langue utilisée pour les noms de périphériques et d'entités. Les parties de phrases sans traduction seront affichées en anglais.\r\n### Adresse de redirection de l'authentification OAuth2\r\nL'adresse de redirection de l'authentification OAuth2 est **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant doit être dans le même réseau local que le terminal de l'opération actuelle (par exemple, un ordinateur personnel) et le terminal de l'opération doit pouvoir accéder à la page d'accueil de Home Assistant via cette adresse. Sinon, l'authentification de connexion peut échouer.\r\n### Remarque\r\n- Pour les utilisateurs ayant des centaines ou plus d'appareils Mi Home, l'ajout initial de l'intégration prendra un certain temps. Veuillez être patient.\r\n- Si Home Assistant fonctionne dans un environnement Docker, veuillez vous assurer que le mode réseau Docker est réglé sur host, sinon la fonctionnalité de contrôle local peut ne pas fonctionner correctement.\r\n- La fonctionnalité de contrôle local de l'intégration a quelques dépendances. Veuillez lire attentivement le README.",
|
||||
"description": "### Région de connexion\r\nSélectionnez la région où se trouve votre compte Xiaomi. Vous pouvez le trouver dans `Xiaomi Home APP > Mon (situé dans le menu inférieur) > Plus de paramètres > À propos de Xiaomi Home`.\r\n### Langue\r\nChoisissez la langue utilisée pour les noms de périphériques et d'entités. Les parties de phrases sans traduction seront affichées en anglais.\r\n### Adresse de redirection de l'authentification OAuth2\r\nL'adresse de redirection de l'authentification OAuth2 est **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant doit être dans le même réseau local que le terminal de l'opération actuelle (par exemple, un ordinateur personnel) et le terminal de l'opération doit pouvoir accéder à la page d'accueil de Home Assistant via cette adresse. Sinon, l'authentification de connexion peut échouer.\r\n### Configuration Réseau Intégrée\r\nVérifiez si le réseau local fonctionne correctement et si les ressources réseau associées sont accessibles. **Il est recommandé de sélectionner cela lors du premier ajout.**\r\n### Remarque\r\n- Pour les utilisateurs ayant des centaines ou plus d'appareils Mi Home, l'ajout initial de l'intégration prendra un certain temps. Veuillez être patient.\r\n- Si Home Assistant fonctionne dans un environnement Docker, veuillez vous assurer que le mode réseau Docker est réglé sur host, sinon la fonctionnalité de contrôle local peut ne pas fonctionner correctement.\r\n- La fonctionnalité de contrôle local de l'intégration a quelques dépendances. Veuillez lire attentivement le README.",
|
||||
"data": {
|
||||
"cloud_server": "Région de connexion",
|
||||
"integration_language": "Langue",
|
||||
"oauth_redirect_url": "Adresse de redirection de l'authentification"
|
||||
"oauth_redirect_url": "Adresse de redirection de l'authentification",
|
||||
"network_detect_config": "Configuration Réseau Intégrée"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuration Réseau Intégrée",
|
||||
"description": "## Introduction à l'Utilisation\r\n### Adresse de Détection Réseau\r\nUtilisé pour vérifier si le réseau fonctionne correctement. Si non défini, l'adresse par défaut du système sera utilisée. Si la vérification de l'adresse par défaut échoue, vous pouvez essayer de saisir une adresse personnalisée.\r\n- Vous pouvez entrer plusieurs adresses de détection, séparées par des virgules, telles que `8.8.8.8,https://www.bing.com`\r\n- S'il s'agit d'une adresse IP, la détection se fera par ping. S'il s'agit d'une adresse HTTP(s), la détection se fera par une requête HTTP GET.\r\n- Si vous souhaitez rétablir l'adresse de détection par défaut du système, veuillez entrer une virgule `,` et cliquer sur 'Suivant'.\r\n- **Cette configuration est globale et les modifications affecteront les autres instances d'intégration. Veuillez modifier avec prudence.**\r\n### Vérification des Dépendances Réseau\r\nVérifiez une par une les dépendances réseau suivantes pour voir si elles sont accessibles. Si les adresses associées ne sont pas accessibles, cela entraînera des problèmes d'intégration.\r\n- Adresse d'Authentification OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Adresse de l'API HTTP de Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Adresse de l'API SPEC de Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Adresse du Broker MQTT de Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Adresse de Détection Réseau",
|
||||
"check_network_deps": "Vérification des Dépendances Réseau"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Paramètres Avancés",
|
||||
"description": "## Introduction\r\n### Sauf si vous comprenez très bien la signification des options suivantes, veuillez les laisser par défaut.\r\n### Filtrer les appareils\r\nPrend en charge le filtrage des appareils en fonction du nom de la pièce et du type d'appareil, ainsi que le filtrage basé sur les appareils.\r\n### Mode de Contrôle\r\n- Automatique : Lorsqu'une passerelle Xiaomi est disponible dans le réseau local, Home Assistant enverra les commandes de contrôle des appareils via la passerelle pour permettre le contrôle local. Si aucune passerelle n'est disponible dans le réseau local, Home Assistant essaiera d'envoyer les commandes de contrôle des appareils via le protocole OT Xiaomi pour permettre le contrôle local. Seules si les conditions de contrôle local ci-dessus ne sont pas remplies, les commandes de contrôle des appareils seront envoyées via le cloud.\r\n- Cloud : Les commandes de contrôle des appareils sont envoyées uniquement via le cloud.\r\n### Mode de Débogage d’Actions\r\nPour les méthodes définies par les appareils MIoT-Spec-V2, en plus de générer une entité de notification, une entité de champ de texte sera également générée pour vous permettre d'envoyer des commandes de contrôle aux appareils lors du débogage.\r\n### Masquer les Entités Non Standard\r\nMasquer les entités générées par des instances MIoT-Spec-V2 non standard et commençant par \"*\".\r\n### Afficher les notifications de changement d'état de l'appareil\r\nAfficher les notifications détaillées de changement d'état de l'appareil, en affichant uniquement les notifications sélectionnées.",
|
||||
"description": "## Introduction\r\n### Sauf si vous comprenez très bien la signification des options suivantes, veuillez les laisser par défaut.\r\n### Filtrer les appareils\r\nPrend en charge le filtrage des appareils en fonction du nom de la pièce et du type d'appareil, ainsi que le filtrage basé sur les appareils.\r\n### Mode de Contrôle\r\n- Automatique : Lorsqu'une passerelle Xiaomi est disponible dans le réseau local, Home Assistant enverra les commandes de contrôle des appareils via la passerelle pour permettre le contrôle local. Si aucune passerelle n'est disponible dans le réseau local, Home Assistant essaiera d'envoyer les commandes de contrôle des appareils via le protocole OT Xiaomi pour permettre le contrôle local. Seules si les conditions de contrôle local ci-dessus ne sont pas remplies, les commandes de contrôle des appareils seront envoyées via le cloud.\r\n- Cloud : Les commandes de contrôle des appareils sont envoyées uniquement via le cloud.\r\n### Mode de Débogage d’Actions\r\nPour les méthodes définies par les appareils MIoT-Spec-V2, en plus de générer une entité de notification, une entité de champ de texte sera également générée pour vous permettre d'envoyer des commandes de contrôle aux appareils lors du débogage.\r\n### Masquer les Entités Non Standard\r\nMasquer les entités générées par des instances MIoT-Spec-V2 non standard et commençant par \"*\".\r\n### Mode d'affichage du capteur binaire\r\nAffiche les capteurs binaires dans Xiaomi Home comme entité de capteur de texte ou entité de capteur binaire。\r\n### Afficher les notifications de changement d'état de l'appareil\r\nAfficher les notifications détaillées de changement d'état de l'appareil, en affichant uniquement les notifications sélectionnées.",
|
||||
"data": {
|
||||
"devices_filter": "Filtrer les Appareils",
|
||||
"ctrl_mode": "Mode de Contrôle",
|
||||
"action_debug": "Mode de Débogage d’Actions",
|
||||
"hide_non_standard_entities": "Masquer les Entités Non Standard",
|
||||
"display_binary_mode": "Mode d'affichage du capteur binaire",
|
||||
"display_devices_changed_notify": "Afficher les notifications de changement d'état de l'appareil"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Le service de découverte de périphériques locaux est anormal.",
|
||||
"get_cert_error": "Échec de l'obtention du certificat de la passerelle.",
|
||||
"no_family_selected": "Aucune maison sélectionnée.",
|
||||
"no_devices": "Il n'y a pas d'appareil dans la maison sélectionnée. Veuillez sélectionner une maison avec des appareils avant de continuer.",
|
||||
"no_central_device": "Le mode gateway central a besoin d'un Xiaomi Gateway disponible dans le réseau local où se trouve Home Assistant. Veuillez vérifier si la maison sélectionnée répond à cette exigence."
|
||||
"no_devices": "Il n'y a pas d'appareils dans la maison sélectionnée. Veuillez sélectionner une maison avec des appareils et continuer.",
|
||||
"no_filter_devices": "Les appareils filtrés sont vides. Veuillez sélectionner des critères de filtre valides et continuer.",
|
||||
"no_central_device": "Le mode gateway central a besoin d'un Xiaomi Gateway disponible dans le réseau local où se trouve Home Assistant. Veuillez vérifier si la maison sélectionnée répond à cette exigence.",
|
||||
"invalid_network_addr": "Adresse IP ou HTTP invalide détectée, veuillez entrer une adresse valide.",
|
||||
"invalid_ip_addr": "Adresse IP inaccessible détectée, veuillez entrer une adresse IP valide.",
|
||||
"invalid_http_addr": "Adresse HTTP inaccessible détectée, veuillez entrer une adresse HTTP valide.",
|
||||
"invalid_default_addr": "L'adresse de détection réseau par défaut est inaccessible, veuillez vérifier la configuration réseau ou utiliser une adresse de détection réseau personnalisée.",
|
||||
"unreachable_oauth2_host": "Impossible d'atteindre l'adresse d'authentification OAuth2, veuillez vérifier la configuration réseau.",
|
||||
"unreachable_http_host": "Impossible d'atteindre l'adresse de l'API HTTP de Xiaomi, veuillez vérifier la configuration réseau.",
|
||||
"unreachable_spec_host": "Impossible d'atteindre l'adresse de l'API SPEC de Xiaomi, veuillez vérifier la configuration réseau.",
|
||||
"unreachable_mqtt_broker": "Impossible d'atteindre l'adresse du Broker MQTT de Xiaomi, veuillez vérifier la configuration réseau."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Échec de l'obtention de l'UUID de Home Assistant.",
|
||||
"network_connect_error": "La configuration a échoué. Erreur de connexion réseau. Veuillez vérifier la configuration du réseau de l'appareil.",
|
||||
"already_configured": "Cet utilisateur a déjà terminé la configuration. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour modifier la configuration.",
|
||||
"invalid_auth_info": "Les informations d'authentification ont expiré. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour vous authentifier à nouveau.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Options de configuration",
|
||||
"description": "### {nick_name} Bonjour !\r\n\r\nID de compte Xiaomi : {uid}\r\nRégion de connexion actuelle : {cloud_server}\r\n\r\nVeuillez sélectionner les options que vous devez reconfigurer et cliquer sur \"Suivant\".",
|
||||
"description": "### {nick_name} Bonjour !\r\n\r\nID de compte Xiaomi : {uid}\r\nRégion de connexion actuelle : {cloud_server}\r\nID d'Instance d'Intégration: {instance_id}\r\n\r\nVeuillez sélectionner les options que vous devez reconfigurer et cliquer sur \"Suivant\".",
|
||||
"data": {
|
||||
"integration_language": "Langue d'intégration",
|
||||
"update_user_info": "Mettre à jour les informations utilisateur",
|
||||
"update_devices": "Mettre à jour la liste des appareils",
|
||||
"action_debug": "Mode de débogage d'action",
|
||||
"hide_non_standard_entities": "Masquer les entités générées non standard",
|
||||
"display_binary_mode": "Mode d'affichage du capteur binaire",
|
||||
"display_devices_changed_notify": "Afficher les notifications de changement d'état de l'appareil",
|
||||
"update_trans_rules": "Mettre à jour les règles de conversion d'entités",
|
||||
"update_lan_ctrl_config": "Mettre à jour la configuration de contrôle LAN"
|
||||
"update_lan_ctrl_config": "Mettre à jour la configuration de contrôle LAN",
|
||||
"network_detect_config": "Configuration Réseau Intégrée"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "Activer la souscription"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuration Réseau Intégrée",
|
||||
"description": "## Introduction à l'Utilisation\r\n### Adresse de Détection Réseau\r\nUtilisé pour vérifier si le réseau fonctionne correctement. Si non défini, l'adresse par défaut du système sera utilisée. Si la vérification de l'adresse par défaut échoue, vous pouvez essayer de saisir une adresse personnalisée.\r\n- Vous pouvez entrer plusieurs adresses de détection, séparées par des virgules, telles que `8.8.8.8,https://www.bing.com`\r\n- S'il s'agit d'une adresse IP, la détection se fera par ping. S'il s'agit d'une adresse HTTP(s), la détection se fera par une requête HTTP GET.\r\n- Si vous souhaitez rétablir l'adresse de détection par défaut du système, veuillez entrer une virgule `,` et cliquer sur 'Suivant'.\r\n- **Cette configuration est globale et les modifications affecteront les autres instances d'intégration. Veuillez modifier avec prudence.**\r\n### Vérification des Dépendances Réseau\r\nVérifiez une par une les dépendances réseau suivantes pour voir si elles sont accessibles. Si les adresses associées ne sont pas accessibles, cela entraînera des problèmes d'intégration.\r\n- Adresse d'Authentification OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Adresse de l'API HTTP de Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Adresse de l'API SPEC de Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Adresse du Broker MQTT de Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Adresse de Détection Réseau",
|
||||
"check_network_deps": "Vérification des Dépendances Réseau"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmer la configuration",
|
||||
"description": "**{nick_name}** Bonjour ! Veuillez confirmer les dernières informations de configuration et cliquer sur \"Soumettre\".\r\nL'intégration rechargera avec la nouvelle configuration.\r\n\r\nLangue d'intégration : {lang_new}\r\nPseudo utilisateur : {nick_name_new}\r\nMode de débogage d'action : {action_debug}\r\nMasquer les entités générées non standard : {hide_non_standard_entities}\r\nAfficher les notifications de changement d'état de l'appareil:\t{display_devices_changed_notify}\r\nModifications des appareils : Ajouter **{devices_add}** appareils, supprimer **{devices_remove}** appareils\r\nModifications des règles de conversion : **{trans_rules_count}** règles au total, mise à jour de **{trans_rules_count_success}** règles",
|
||||
@ -168,11 +198,20 @@
|
||||
"get_homeinfo_error": "Impossible d'obtenir les informations de la maison.",
|
||||
"get_cert_error": "Impossible d'obtenir le certificat central.",
|
||||
"no_family_selected": "Aucune maison sélectionnée.",
|
||||
"no_devices": "Aucun périphérique dans la maison sélectionnée. Veuillez sélectionner une maison avec des périphériques et continuer.",
|
||||
"no_devices": "Il n'y a pas d'appareils dans la maison sélectionnée. Veuillez sélectionner une maison avec des appareils et continuer.",
|
||||
"no_filter_devices": "Les appareils filtrés sont vides. Veuillez sélectionner des critères de filtre valides et continuer.",
|
||||
"no_central_device": "Le mode passerelle centrale nécessite une passerelle Xiaomi disponible dans le réseau local où est installé Home Assistant. Veuillez vérifier que la maison sélectionnée répond à cette exigence.",
|
||||
"mdns_discovery_error": "Service de découverte de périphérique local en panne.",
|
||||
"update_config_error": "Échec de la mise à jour des informations de configuration.",
|
||||
"not_confirm": "La modification n'a pas été confirmée. Veuillez cocher la case de confirmation avant de soumettre."
|
||||
"not_confirm": "La modification n'a pas été confirmée. Veuillez cocher la case de confirmation avant de soumettre.",
|
||||
"invalid_network_addr": "Adresse IP ou HTTP invalide détectée, veuillez entrer une adresse valide.",
|
||||
"invalid_ip_addr": "Adresse IP inaccessible détectée, veuillez entrer une adresse IP valide.",
|
||||
"invalid_http_addr": "Adresse HTTP inaccessible détectée, veuillez entrer une adresse HTTP valide.",
|
||||
"invalid_default_addr": "L'adresse de détection réseau par défaut est inaccessible, veuillez vérifier la configuration réseau ou utiliser une adresse de détection réseau personnalisée.",
|
||||
"unreachable_oauth2_host": "Impossible d'atteindre l'adresse d'authentification OAuth2, veuillez vérifier la configuration réseau.",
|
||||
"unreachable_http_host": "Impossible d'atteindre l'adresse de l'API HTTP de Xiaomi, veuillez vérifier la configuration réseau.",
|
||||
"unreachable_spec_host": "Impossible d'atteindre l'adresse de l'API SPEC de Xiaomi, veuillez vérifier la configuration réseau.",
|
||||
"unreachable_mqtt_broker": "Impossible d'atteindre l'adresse du Broker MQTT de Xiaomi, veuillez vérifier la configuration réseau."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "Échec de la configuration. Problème de connexion réseau, veuillez vérifier la configuration du périphérique.",
|
||||
|
224
custom_components/xiaomi_home/translations/it.json
Normal file
224
custom_components/xiaomi_home/translations/it.json
Normal file
@ -0,0 +1,224 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "Integrazione Xiaomi Home",
|
||||
"step": {
|
||||
"eula": {
|
||||
"title": "Avviso sui Rischi",
|
||||
"description": "1. Le informazioni del tuo utente Xiaomi e le informazioni del dispositivo saranno memorizzate nel sistema Home Assistant. **Xiaomi non può garantire la sicurezza del meccanismo di archiviazione di Home Assistant**. Sei responsabile per prevenire il furto delle tue informazioni.\r\n2. Questa integrazione è mantenuta dalla comunità open-source. Potrebbero esserci problemi di stabilità o altri problemi. In caso di problemi o bug con questa integrazione, **dovresti cercare aiuto dalla comunità open-source piuttosto che contattare il servizio clienti Xiaomi**.\r\n3. È necessaria una certa abilità tecnica per mantenere il tuo ambiente operativo locale. L'integrazione non è user-friendly per i principianti.\r\n4. Si prega di leggere il file README prima di iniziare.\n\n5. Per garantire un uso stabile dell'integrazione e prevenire l'abuso dell'interfaccia, **questa integrazione può essere utilizzata solo in Home Assistant. Per i dettagli, consulta il LICENSE**.",
|
||||
"data": {
|
||||
"eula": "Sono consapevole dei rischi sopra indicati e sono disposto ad assumermi volontariamente qualsiasi rischio associato all'uso dell'integrazione."
|
||||
}
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Configurazione di base",
|
||||
"description": "### Regione di Login\r\nSeleziona la regione del tuo account Xiaomi. Puoi trovarla nell'APP Xiaomi Home > Profilo (nel menu in basso) > Impostazioni aggiuntive > Informazioni su Xiaomi Home.\r\n### Lingua\r\nSeleziona la lingua dei nomi dei dispositivi e delle entità. Alcune frasi senza traduzione verranno visualizzate in inglese.\r\n### URL di reindirizzamento OAuth2\r\nL'indirizzo di reindirizzamento dell'autenticazione OAuth2 è **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant deve trovarsi nella stessa rete locale del terminale operativo corrente (ad esempio, il computer personale) e il terminale operativo deve poter accedere alla home page di Home Assistant tramite questo indirizzo. Altrimenti, l'autenticazione del login potrebbe fallire.\r\n### Nota\r\n- Per gli utenti con centinaia o più dispositivi Mi Home, l'aggiunta iniziale dell'integrazione richiederà del tempo. Si prega di essere pazienti.\r\n- Se Home Assistant è in esecuzione in un ambiente Docker, assicurarsi che la modalità di rete Docker sia impostata su host, altrimenti la funzionalità di controllo locale potrebbe non funzionare correttamente.\r\n- La funzionalità di controllo locale dell'integrazione ha alcune dipendenze. Si prega di leggere attentamente il README.",
|
||||
"data": {
|
||||
"cloud_server": "Regione di Login",
|
||||
"integration_language": "Lingua",
|
||||
"oauth_redirect_url": "URL di reindirizzamento OAuth2",
|
||||
"network_detect_config": "Configurazione di rete integrata"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configurazione di Rete Integrata",
|
||||
"description": "## Introduzione all'uso\r\n### Indirizzo di Rilevamento della Rete\r\nUtilizzato per verificare se la rete funziona correttamente. Se non impostato, verrà utilizzato l'indirizzo di default del sistema. Se il controllo dell'indirizzo predefinito fallisce, puoi provare a inserire un indirizzo personalizzato.\r\n- Puoi inserire più indirizzi di rilevamento, separati da virgole, come `8.8.8.8,https://www.bing.com`\r\n- Se è un indirizzo IP, il rilevamento verrà eseguito tramite ping. Se è un indirizzo HTTP(s), il rilevamento verrà eseguito tramite richiesta HTTP GET.\r\n- Se desideri ripristinare l'indirizzo di rilevamento predefinito del sistema, inserisci una virgola `,` e fai clic su 'Avanti'.\r\n- **Questa configurazione è globale e le modifiche influenzeranno altre istanze di integrazione. Si prega di modificare con cautela.**\r\n### Controlla le Dipendenze di Rete\r\nControlla una per una le seguenti dipendenze di rete per vedere se sono accessibili. Se gli indirizzi correlati non sono accessibili, causerà problemi di integrazione.\r\n- Indirizzo di Autenticazione OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Indirizzo API HTTP di Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Indirizzo API SPEC di Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Indirizzo del Broker MQTT di Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Indirizzo di Rilevamento della Rete",
|
||||
"check_network_deps": "Controlla le Dipendenze di Rete"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
"title": "Errore di Login",
|
||||
"description": "Clicca AVANTI per riprovare."
|
||||
},
|
||||
"homes_select": {
|
||||
"title": "Seleziona Famiglia e Dispositivo",
|
||||
"description": "## Introduzione\r\n### Importa la Famiglia del Dispositivo\r\nL'integrazione aggiungerà i dispositivi dalla famiglia selezionata.\r\n### Modalità di Sincronizzazione del Nome della Stanza\r\nQuando si sincronizzano i dispositivi dall'app Mi Home a Home Assistant, la denominazione dell'area in Home Assistant seguirà le regole indicate di seguito. Si noti che il processo di sincronizzazione non modificherà le impostazioni di famiglia e stanza nell'app Mi Home.\r\n- Non sincronizzare: Il dispositivo non verrà aggiunto a nessuna area.\r\n- Altre opzioni: L'area a cui viene aggiunto il dispositivo verrà denominata come la famiglia o il nome della stanza nell'app Mi Home.\r\n### Impostazioni Avanzate\r\nMostra le impostazioni avanzate per modificare le opzioni di configurazione professionale dell'integrazione.\r\n\r\n \r\n### {nick_name} Ciao! Seleziona la famiglia a cui desideri aggiungere il dispositivo.",
|
||||
"data": {
|
||||
"home_infos": "Importa la Famiglia del Dispositivo",
|
||||
"area_name_rule": "Modalità di Sincronizzazione del Nome della Stanza",
|
||||
"advanced_options": "Impostazioni Avanzate"
|
||||
}
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Impostazioni Avanzate",
|
||||
"description": "## Introduzione\r\n### A meno che tu non abbia chiaro il significato delle seguenti opzioni, si prega di mantenere le impostazioni predefinite.\r\n### Filtra Dispositivi\r\nSupporta il filtraggio dei dispositivi per nome della stanza e tipo di dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo.\r\n### Modalità di Controllo\r\n- Automatico: Quando è disponibile un gateway hub centrale Xiaomi nella rete locale, Home Assistant darà priorità all'invio dei comandi di controllo dei dispositivi tramite il gateway hub centrale per ottenere il controllo locale. Se non è presente un gateway hub centrale nella rete locale, tenterà di inviare comandi di controllo tramite il protocollo OT di Xiaomi per ottenere il controllo locale. Solo quando le condizioni di controllo locale sopra indicate non sono soddisfatte, i comandi di controllo del dispositivo verranno inviati tramite il cloud.\r\n- Cloud: Tutti i comandi di controllo vengono inviati tramite il cloud.\r\n### Modalità di Debug delle Azioni\r\nPer i metodi definiti dal dispositivo MIoT-Spec-V2, oltre a generare entità di notifica, verrà generata anche un'entità di casella di input di testo. È possibile utilizzarla per inviare comandi di controllo al dispositivo durante il debug.\r\n### Nascondi Entità Generate Non Standard\r\nNasconde le entità generate da istanze non standard MIoT-Spec-V2 con nomi che iniziano con \"*\".\r\n### Modalità di visualizzazione del sensore binario\r\nVisualizza i sensori binari in Mi Home come entità del sensore di testo o entità del sensore binario。\r\n### Mostra Notifiche di Cambio di Stato del Dispositivo\r\nMostra notifiche dettagliate sui cambiamenti di stato del dispositivo, mostrando solo le notifiche selezionate.",
|
||||
"data": {
|
||||
"devices_filter": "Filtra Dispositivi",
|
||||
"ctrl_mode": "Modalità di Controllo",
|
||||
"action_debug": "Modalità di Debug delle Azioni",
|
||||
"hide_non_standard_entities": "Nascondi Entità Generate Non Standard",
|
||||
"display_binary_mode": "Modalità di visualizzazione del sensore binario",
|
||||
"display_devices_changed_notify": "Mostra Notifiche di Cambio di Stato del Dispositivo"
|
||||
}
|
||||
},
|
||||
"devices_filter": {
|
||||
"title": "Filtra Dispositivi",
|
||||
"description": "## Istruzioni per l'uso\r\nSupporta il filtraggio dei dispositivi per nome della stanza, tipo di accesso al dispositivo e modello del dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo. La logica di filtraggio è la seguente:\r\n- Prima, secondo la logica statistica, ottieni l'unione o l'intersezione di tutti gli elementi inclusi, poi ottieni l'intersezione o l'unione degli elementi esclusi e infine sottrai il [risultato riassuntivo incluso] dal [risultato riassuntivo escluso] per ottenere il [risultato del filtro].\r\n- Se non vengono selezionati elementi inclusi, significa che tutti sono inclusi.\r\n### Modalità di Filtro\r\n- Escludi: Rimuovi gli elementi indesiderati.\r\n- Includi: Includi gli elementi desiderati.\r\n### Logica Statistica\r\n- Logica AND: Prendi l'intersezione di tutti gli elementi nella stessa modalità.\r\n- Logica OR: Prendi l'unione di tutti gli elementi nella stessa modalità.\r\n\r\nPuoi anche andare alla pagina [Configurazione > Aggiorna Elenco Dispositivi] dell'elemento di integrazione e controllare [Filtra Dispositivi] per rifiltrare.",
|
||||
"data": {
|
||||
"room_filter_mode": "Filtra Stanze della Famiglia",
|
||||
"room_list": "Stanze della Famiglia",
|
||||
"type_filter_mode": "Filtra Tipo di Connessione del Dispositivo",
|
||||
"type_list": "Tipo di Connessione del Dispositivo",
|
||||
"model_filter_mode": "Filtra Modello del Dispositivo",
|
||||
"model_list": "Modello del Dispositivo",
|
||||
"devices_filter_mode": "Filtra Dispositivi",
|
||||
"device_list": "Elenco Dispositivi",
|
||||
"statistics_logic": "Logica Statistica"
|
||||
}
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"oauth": "### {link_left}Clicca qui per accedere{link_right}\r\n(Verrai reindirizzato automaticamente alla pagina successiva dopo un accesso riuscito)"
|
||||
},
|
||||
"error": {
|
||||
"eula_not_agree": "Si prega di leggere l'avviso sui rischi.",
|
||||
"get_token_error": "Impossibile recuperare le informazioni di autorizzazione per il login (token OAuth).",
|
||||
"get_homeinfo_error": "Impossibile recuperare le informazioni della casa.",
|
||||
"mdns_discovery_error": "Eccezione del servizio di scoperta dei dispositivi locali.",
|
||||
"get_cert_error": "Impossibile recuperare il certificato del gateway centrale.",
|
||||
"no_family_selected": "Nessuna casa selezionata.",
|
||||
"no_devices": "La casa selezionata non ha dispositivi. Si prega di scegliere una casa che contiene dispositivi e continuare.",
|
||||
"no_filter_devices": "I dispositivi filtrati sono vuoti. Si prega di selezionare criteri di filtro validi e continuare.",
|
||||
"no_central_device": "[Modalità Gateway Hub Centrale] richiede un gateway hub centrale Xiaomi disponibile nella rete locale in cui esiste Home Assistant. Si prega di verificare se la casa selezionata soddisfa il requisito.",
|
||||
"invalid_network_addr": "Rilevato indirizzo IP o indirizzo HTTP non valido, si prega di inserire un indirizzo valido.",
|
||||
"invalid_ip_addr": "Rilevato indirizzo IP non raggiungibile, si prega di inserire un indirizzo IP valido.",
|
||||
"invalid_http_addr": "Rilevato indirizzo HTTP non raggiungibile, si prega di inserire un indirizzo HTTP valido.",
|
||||
"invalid_default_addr": "Indirizzo di rilevamento della rete predefinito non raggiungibile, si prega di verificare la configurazione della rete o utilizzare un indirizzo di rilevamento della rete personalizzato.",
|
||||
"unreachable_oauth2_host": "Impossibile raggiungere l'indirizzo di autenticazione OAuth2, si prega di verificare la configurazione della rete.",
|
||||
"unreachable_http_host": "Impossibile raggiungere l'indirizzo API HTTP di Xiaomi, si prega di verificare la configurazione della rete.",
|
||||
"unreachable_spec_host": "Impossibile raggiungere l'indirizzo API SPEC di Xiaomi, si prega di verificare la configurazione della rete.",
|
||||
"unreachable_mqtt_broker": "Impossibile raggiungere l'indirizzo del broker MQTT di Xiaomi, si prega di verificare la configurazione della rete."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Impossibile ottenere l'UUID di Home Assistant.",
|
||||
"network_connect_error": "Configurazione fallita. La connessione di rete è anomala. Si prega di controllare la configurazione della rete del dispositivo.",
|
||||
"already_configured": "La configurazione per questo utente è già completata. Si prega di andare alla pagina dell'integrazione e cliccare sul pulsante CONFIGURA per le modifiche.",
|
||||
"invalid_auth_info": "Le informazioni di autenticazione sono scadute. Si prega di andare alla pagina dell'integrazione e cliccare sul pulsante CONFIGURA per ri-autenticarsi.",
|
||||
"config_flow_error": "Errore di configurazione dell'integrazione: {error}."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"auth_config": {
|
||||
"title": "Configurazione dell'Autenticazione",
|
||||
"description": "Le informazioni di autenticazione locale sono scadute. Si prega di riavviare il processo di autenticazione.\r\n### Regione di Login Corrente: {cloud_server}\r\n### URL di reindirizzamento OAuth2\r\nL'indirizzo di reindirizzamento dell'autenticazione OAuth2 è **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant deve trovarsi nella stessa rete locale del terminale operativo corrente (ad esempio, il computer personale) e il terminale operativo deve poter accedere alla home page di Home Assistant tramite questo indirizzo. Altrimenti, l'autenticazione del login potrebbe fallire.",
|
||||
"data": {
|
||||
"oauth_redirect_url": "URL di reindirizzamento OAuth2"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
"title": "Si è verificato un errore durante il login.",
|
||||
"description": "Clicca AVANTI per riprovare."
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Opzioni di Configurazione",
|
||||
"description": "### Ciao, {nick_name}\r\n\r\nID Xiaomi: {uid}\r\nRegione di Login Corrente: {cloud_server}\r\n\r\nSeleziona le opzioni che desideri configurare, poi clicca AVANTI.",
|
||||
"data": {
|
||||
"integration_language": "Lingua dell'Integrazione",
|
||||
"update_user_info": "Aggiorna le informazioni dell'utente",
|
||||
"update_devices": "Aggiorna l'elenco dei dispositivi",
|
||||
"action_debug": "Modalità debug per azione",
|
||||
"hide_non_standard_entities": "Nascondi entità create non standard",
|
||||
"display_binary_mode": "Modalità di visualizzazione del sensore binario",
|
||||
"display_devices_changed_notify": "Mostra notifiche di cambio stato del dispositivo",
|
||||
"update_trans_rules": "Aggiorna le regole di conversione delle entità",
|
||||
"update_lan_ctrl_config": "Aggiorna configurazione del controllo LAN",
|
||||
"network_detect_config": "Configurazione di Rete Integrata"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
"title": "Aggiorna il Nickname dell'Utente",
|
||||
"description": "Ciao {nick_name}, puoi modificare il tuo nickname personalizzato qui sotto.",
|
||||
"data": {
|
||||
"nick_name": "Nickname"
|
||||
}
|
||||
},
|
||||
"homes_select": {
|
||||
"title": "Seleziona Nuovamente Casa e Dispositivi",
|
||||
"description": "## Istruzioni per l'uso\r\n### Importa dispositivi da casa\r\nL'integrazione aggiungerà dispositivi dalle case selezionate.\r\n### Filtra Dispositivi\r\nSupporta il filtraggio dei dispositivi per nome della stanza, tipo di accesso al dispositivo e modello del dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo. **{local_count}** dispositivi sono stati filtrati.\r\n### Modalità di Controllo\r\n- Automatico: Quando è disponibile un gateway hub centrale Xiaomi nella rete locale, Home Assistant darà priorità all'invio dei comandi di controllo dei dispositivi tramite il gateway hub centrale per ottenere il controllo locale. Se non è presente un gateway hub centrale nella rete locale, tenterà di inviare comandi di controllo tramite la funzione di controllo LAN di Xiaomi. Solo quando le condizioni di controllo locale sopra indicate non sono soddisfatte, i comandi di controllo del dispositivo verranno inviati tramite il cloud.\r\n- Cloud: Tutti i comandi di controllo vengono inviati tramite il cloud.",
|
||||
"data": {
|
||||
"home_infos": "Importa dispositivi da casa",
|
||||
"devices_filter": "Filtra dispositivi",
|
||||
"ctrl_mode": "Modalità di controllo"
|
||||
}
|
||||
},
|
||||
"devices_filter": {
|
||||
"title": "Filtra Dispositivi",
|
||||
"description": "## Istruzioni per l'uso\r\nSupporta il filtraggio dei dispositivi per nome della stanza, tipo di accesso al dispositivo e modello del dispositivo, e supporta anche il filtraggio delle dimensioni del dispositivo. La logica di filtraggio è la seguente:\r\n- Prima, secondo la logica statistica, ottieni l'unione o l'intersezione di tutti gli elementi inclusi, poi ottieni l'intersezione o l'unione degli elementi esclusi e infine sottrai il [risultato riassuntivo incluso] dal [risultato riassuntivo escluso] per ottenere il [risultato del filtro].\r\n- Se non vengono selezionati elementi inclusi, significa che tutti sono inclusi.\r\n### Modalità di Filtro\r\n- Escludi: Rimuovi gli elementi indesiderati.\r\n- Includi: Includi gli elementi desiderati.\r\n### Logica Statistica\r\n- Logica AND: Prendi l'intersezione di tutti gli elementi nella stessa modalità.\r\n- Logica OR: Prendi l'unione di tutti gli elementi nella stessa modalità.\r\n\r\nPuoi anche andare alla pagina [Configurazione > Aggiorna Elenco Dispositivi] dell'elemento di integrazione e controllare [Filtra Dispositivi] per rifiltrare.",
|
||||
"data": {
|
||||
"room_filter_mode": "Filtra Stanze della Famiglia",
|
||||
"room_list": "Stanze della Famiglia",
|
||||
"type_filter_mode": "Filtra Tipo di Connessione del Dispositivo",
|
||||
"type_list": "Tipo di Connessione del Dispositivo",
|
||||
"model_filter_mode": "Filtra Modello del Dispositivo",
|
||||
"model_list": "Modello del Dispositivo",
|
||||
"devices_filter_mode": "Filtra Dispositivi",
|
||||
"device_list": "Elenco Dispositivi",
|
||||
"statistics_logic": "Logica Statistica"
|
||||
}
|
||||
},
|
||||
"update_trans_rules": {
|
||||
"title": "Aggiorna le Regole di Trasformazione delle Entità",
|
||||
"description": "## Istruzioni per l'uso\r\n- Aggiorna le informazioni delle entità dei dispositivi nell'istanza dell'integrazione corrente, incluse la configurazione multilingue MIoT-Spec-V2, la traduzione booleana e il filtro dei modelli.\r\n- **Avviso**: Questa è una configurazione globale e aggiornerà la cache locale. Influenzando tutte le istanze di integrazione.\r\n- Questa operazione richiederà del tempo, si prega di essere pazienti. Seleziona \"Conferma Aggiornamento\" e clicca \"Avanti\" per iniziare l'aggiornamento di **{urn_count}** regole, altrimenti salta l'aggiornamento.",
|
||||
"data": {
|
||||
"confirm": "Conferma l'aggiornamento"
|
||||
}
|
||||
},
|
||||
"update_lan_ctrl_config": {
|
||||
"title": "Aggiorna configurazione del controllo LAN",
|
||||
"description": "## Istruzioni per l'uso\r\nAggiorna le configurazioni per la funzione di controllo LAN di Xiaomi. Quando il cloud e il gateway centrale non possono controllare i dispositivi, l'integrazione tenterà di controllare i dispositivi tramite la LAN. Se nessuna scheda di rete è selezionata, la funzione di controllo LAN non avrà effetto.\r\n- Solo i dispositivi compatibili con MIoT-Spec-V2 nella LAN sono supportati. Alcuni dispositivi prodotti prima del 2020 potrebbero non supportare il controllo LAN o l'abbonamento LAN.\r\n- Seleziona la/le scheda/e di rete nella stessa rete dei dispositivi da controllare. È possibile selezionare più schede di rete. Se Home Assistant ha due o più connessioni alla rete locale a causa della selezione multipla delle schede di rete, si consiglia di selezionare quella con la migliore connessione di rete, altrimenti potrebbe avere un effetto negativo sui dispositivi.\r\n- Se ci sono dispositivi terminali (altoparlanti Xiaomi con schermo, telefono cellulare, ecc.) nella LAN che supportano il controllo locale, abilitare l'abbonamento LAN potrebbe causare anomalie nell'automazione locale e nei dispositivi.\r\n- **Avviso**: Questa è una configurazione globale. Influenzando tutte le istanze di integrazione. Usala con cautela.\r\n{notice_net_dup}",
|
||||
"data": {
|
||||
"net_interfaces": "Si prega di selezionare la scheda di rete da utilizzare",
|
||||
"enable_subscribe": "Abilita Sottoscrizione LAN"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configurazione di Rete Integrata",
|
||||
"description": "## Introduzione all'uso\r\n### Indirizzo di Rilevamento della Rete\r\nUtilizzato per verificare se la rete funziona correttamente. Se non impostato, verrà utilizzato l'indirizzo di default del sistema. Se il controllo dell'indirizzo predefinito fallisce, puoi provare a inserire un indirizzo personalizzato.\r\n- Puoi inserire più indirizzi di rilevamento, separati da virgole, come `8.8.8.8,https://www.bing.com`\r\n- Se è un indirizzo IP, il rilevamento verrà eseguito tramite ping. Se è un indirizzo HTTP(s), il rilevamento verrà eseguito tramite richiesta HTTP GET.\r\n- Se desideri ripristinare l'indirizzo di rilevamento predefinito del sistema, inserisci una virgola `,` e fai clic su 'Avanti'.\r\n- **Questa configurazione è globale e le modifiche influenzeranno altre istanze di integrazione. Si prega di modificare con cautela.**\r\n### Controlla le Dipendenze di Rete\r\nControlla una per una le seguenti dipendenze di rete per vedere se sono accessibili. Se gli indirizzi correlati non sono accessibili, causerà problemi di integrazione.\r\n- Indirizzo di Autenticazione OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Indirizzo API HTTP di Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Indirizzo API SPEC di Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Indirizzo del Broker MQTT di Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Indirizzo di Rilevamento della Rete",
|
||||
"check_network_deps": "Controlla le Dipendenze di Rete"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Conferma Configurazione",
|
||||
"description": "Ciao **{nick_name}**, si prega di confermare le informazioni di configurazione più recenti e poi fare clic su INVIA.\r\nL'integrazione verrà ricaricata utilizzando la configurazione aggiornata.\r\n\r\nLingua dell'Integrazione: \t{lang_new}\r\nSoprannome: \t{nick_name_new}\r\nModalità di debug per azione: \t{action_debug}\r\nNascondi entità create non standard: \t{hide_non_standard_entities}\r\nMostra notifiche di cambio stato del dispositivo:\t{display_devices_changed_notify}\r\nCambiamenti del Dispositivo: \tAggiungi **{devices_add}** dispositivi, Rimuovi **{devices_remove}** dispositivi\r\nCambiamenti delle regole di trasformazione: \tCi sono un totale di **{trans_rules_count}** regole, e aggiornate **{trans_rules_count_success}** regole",
|
||||
"data": {
|
||||
"confirm": "Conferma la modifica"
|
||||
}
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"oauth": "### {link_left}Clicca qui per riaccedere{link_right}"
|
||||
},
|
||||
"error": {
|
||||
"not_auth": "Non autenticato. Si prega di fare clic sul link di autenticazione per autenticare l'identità dell'utente.",
|
||||
"get_token_error": "Impossibile recuperare le informazioni di autorizzazione all'accesso (token OAuth).",
|
||||
"get_homeinfo_error": "Impossibile recuperare le informazioni sulla casa.",
|
||||
"get_cert_error": "Impossibile recuperare il certificato del gateway hub centrale.",
|
||||
"no_devices": "Non ci sono dispositivi nella casa selezionata. Si prega di selezionare una casa con dispositivi e continuare.",
|
||||
"no_filter_devices": "I dispositivi filtrati sono vuoti. Si prega di selezionare criteri di filtro validi e continuare.",
|
||||
"no_family_selected": "Nessuna casa selezionata.",
|
||||
"no_central_device": "[Modalità Gateway Hub Centrale] richiede un gateway hub centrale Xiaomi disponibile nella rete locale in cui esiste Home Assistant. Si prega di verificare se la casa selezionata soddisfa il requisito.",
|
||||
"mdns_discovery_error": "Eccezione nel servizio di rilevamento dei dispositivi locali.",
|
||||
"update_config_error": "Impossibile aggiornare le informazioni di configurazione.",
|
||||
"not_confirm": "Le modifiche non sono confermate. Si prega di confermare la modifica prima di inviare.",
|
||||
"invalid_network_addr": "Rilevato indirizzo IP o indirizzo HTTP non valido, si prega di inserire un indirizzo valido.",
|
||||
"invalid_ip_addr": "Rilevato indirizzo IP non raggiungibile, si prega di inserire un indirizzo IP valido.",
|
||||
"invalid_http_addr": "Rilevato indirizzo HTTP non raggiungibile, si prega di inserire un indirizzo HTTP valido.",
|
||||
"invalid_default_addr": "Indirizzo di rilevamento della rete predefinito non raggiungibile, si prega di verificare la configurazione della rete o utilizzare un indirizzo di rilevamento della rete personalizzato.",
|
||||
"unreachable_oauth2_host": "Impossibile raggiungere l'indirizzo di autenticazione OAuth2, si prega di verificare la configurazione della rete.",
|
||||
"unreachable_http_host": "Impossibile raggiungere l'indirizzo API HTTP di Xiaomi, si prega di verificare la configurazione della rete.",
|
||||
"unreachable_spec_host": "Impossibile raggiungere l'indirizzo API SPEC di Xiaomi, si prega di verificare la configurazione della rete.",
|
||||
"unreachable_mqtt_broker": "Impossibile raggiungere l'indirizzo del broker MQTT di Xiaomi, si prega di verificare la configurazione della rete."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "Configurazione fallita. La connessione di rete è anomala. Si prega di controllare la configurazione della rete del dispositivo.",
|
||||
"options_flow_error": "Errore di riconfigurazione dell'integrazione: {error}",
|
||||
"re_add": "Si prega di riaggiungere l'integrazione. Messaggio di errore: {error}",
|
||||
"storage_error": "Eccezione del modulo di archiviazione dell'integrazione. Si prega di riprovare o riaggiungere l'integrazione: {error}",
|
||||
"inconsistent_account": "Le informazioni dell'account sono incoerenti."
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "基本設定",
|
||||
"description": "### ログインエリア\r\nXiaomi アカウントが属する地域を選択します。 `Xiaomi Home アプリ> マイ(ボトムメニューにあります)> その他の設定> Xiaomi Home について` で確認できます。\r\n### 言語\r\nデバイスおよびエンティティ名に使用される言語を選択します。一部の翻訳が欠落している場合、英語が表示されます。\r\n### OAuth2 認証リダイレクトアドレス\r\nOAuth2 認証リダイレクトアドレスは **[http://homeassistant.local:8123](http://homeassistant.local:8123)** です。Home Assistant は、現在の操作端末(たとえば、パーソナルコンピュータ)と同じ LAN 内にあり、操作端末がこのアドレスで Home Assistant ホームページにアクセスできる場合にのみログイン認証が成功する場合があります。\r\n### 注意事項\r\n- 数百台以上のMi Homeデバイスをお持ちのユーザーの場合、統合の初回追加には時間がかかります。しばらくお待ちください。\r\n- Home AssistantがDocker環境で実行されている場合は、Dockerのネットワークモードがhostに設定されていることを確認してください。そうしないと、ローカル制御機能が正しく動作しない可能性があります。\r\n- 統合のローカル制御機能にはいくつかの依存関係があります。READMEを注意深く読んでください。",
|
||||
"description": "### ログインエリア\r\nXiaomi アカウントが属する地域を選択します。 `Xiaomi Home アプリ> マイ(ボトムメニューにあります)> その他の設定> Xiaomi Home について` で確認できます。\r\n### 言語\r\nデバイスおよびエンティティ名に使用される言語を選択します。一部の翻訳が欠落している場合、英語が表示されます。\r\n### OAuth2 認証リダイレクトアドレス\r\nOAuth2 認証リダイレクトアドレスは **[http://homeassistant.local:8123](http://homeassistant.local:8123)** です。Home Assistant は、現在の操作端末(たとえば、パーソナルコンピュータ)と同じ LAN 内にあり、操作端末がこのアドレスで Home Assistant ホームページにアクセスできる場合にのみログイン認証が成功する場合があります。\r\n### 統合ネットワーク構成\r\nローカルネットワークが正常に機能しているかどうか、および関連するネットワークリソースにアクセスできるかどうかを確認します。**初めて追加する場合は、これを選択することをお勧めします。**\r\n### 注意事項\r\n- 数百台以上のMi Homeデバイスをお持ちのユーザーの場合、統合の初回追加には時間がかかります。しばらくお待ちください。\r\n- Home AssistantがDocker環境で実行されている場合は、Dockerのネットワークモードがhostに設定されていることを確認してください。そうしないと、ローカル制御機能が正しく動作しない可能性があります。\r\n- 統合のローカル制御機能にはいくつかの依存関係があります。READMEを注意深く読んでください。",
|
||||
"data": {
|
||||
"cloud_server": "ログインエリア",
|
||||
"integration_language": "言語",
|
||||
"oauth_redirect_url": "認証リダイレクトアドレス"
|
||||
"oauth_redirect_url": "認証リダイレクトアドレス",
|
||||
"network_detect_config": "統合ネットワーク構成"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "統合ネットワーク構成",
|
||||
"description": "## 使用方法の紹介\r\n### ネットワーク検出アドレス\r\nネットワークが正常に機能しているかどうかを確認するために使用されます。設定されていない場合、システムのデフォルトアドレスが使用されます。デフォルトアドレスのチェックが失敗した場合は、カスタムアドレスを入力してみてください。\r\n- 複数の検出アドレスを入力できます。アドレスはコンマで区切ります。例:`8.8.8.8,https://www.bing.com`\r\n- IPアドレスの場合、pingによる検出が行われます。HTTP(s)アドレスの場合、HTTP GETリクエストによる検出が行われます。\r\n- システムのデフォルト検出アドレスを復元する場合は、カンマ `,` を入力して「次へ」をクリックしてください。\r\n- **この設定はグローバルであり、変更は他の統合インスタンスに影響を与えます。慎重に変更してください。**\r\n### ネットワーク依存関係のチェック\r\n次のネットワーク依存関係がアクセス可能かどうかを順番に確認します。関連するアドレスにアクセスできない場合、統合に問題が発生します。\r\n- OAuth2 認証アドレス: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API アドレス: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API アドレス: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT ブローカーアドレス: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "ネットワーク検出アドレス",
|
||||
"check_network_deps": "ネットワーク依存関係のチェック"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "高度な設定オプション",
|
||||
"description": "## 紹介\r\n### 以下のオプションの意味がよくわからない場合は、デフォルトのままにしてください。\r\n### デバイスのフィルタリング\r\n部屋名とデバイスタイプでデバイスをフィルタリングすることができます。デバイスの次元でフィルタリングすることもできます。\r\n### コントロールモード\r\n- 自動:ローカルネットワーク内に利用可能なXiaomi中央ゲートウェイがある場合、Home Assistantはデバイス制御命令を送信するために優先的に中央ゲートウェイを使用します。ローカルネットワークに中央ゲートウェイがない場合、Xiaomi OTプロトコルを使用してデバイス制御命令を送信し、ローカル制御機能を実現します。上記のローカル制御条件が満たされない場合のみ、デバイス制御命令はクラウドを介して送信されます。\r\n- クラウド:制御命令はクラウドを介してのみ送信されます。\r\n### Actionデバッグモード\r\nデバイスが定義するMIoT-Spec-V2のメソッドに対して、通知エンティティを生成するだけでなく、デバイスに制御命令を送信するためのテキスト入力ボックスエンティティも生成されます。デバッグ時にデバイスに制御命令を送信するために使用できます。\r\n### 非標準生成エンティティを隠す\r\n「*」で始まる名前の非標準MIoT-Spec-V2インスタンスによって生成されたエンティティを非表示にします。\r\n### デバイスの状態変化通知を表示\r\nデバイスの状態変化通知を詳細に表示し、選択された通知のみを表示します。",
|
||||
"description": "## 紹介\r\n### 以下のオプションの意味がよくわからない場合は、デフォルトのままにしてください。\r\n### デバイスのフィルタリング\r\n部屋名とデバイスタイプでデバイスをフィルタリングすることができます。デバイスの次元でフィルタリングすることもできます。\r\n### コントロールモード\r\n- 自動:ローカルネットワーク内に利用可能なXiaomi中央ゲートウェイがある場合、Home Assistantはデバイス制御命令を送信するために優先的に中央ゲートウェイを使用します。ローカルネットワークに中央ゲートウェイがない場合、Xiaomi OTプロトコルを使用してデバイス制御命令を送信し、ローカル制御機能を実現します。上記のローカル制御条件が満たされない場合のみ、デバイス制御命令はクラウドを介して送信されます。\r\n- クラウド:制御命令はクラウドを介してのみ送信されます。\r\n### Actionデバッグモード\r\nデバイスが定義するMIoT-Spec-V2のメソッドに対して、通知エンティティを生成するだけでなく、デバイスに制御命令を送信するためのテキスト入力ボックスエンティティも生成されます。デバッグ時にデバイスに制御命令を送信するために使用できます。\r\n### 非標準生成エンティティを隠す\r\n「*」で始まる名前の非標準MIoT-Spec-V2インスタンスによって生成されたエンティティを非表示にします。\r\n### バイナリセンサー表示モード\r\nXiaomi Homeのバイナリセンサーをテキストセンサーエンティティまたはバイナリセンサーエンティティとして表示します。\r\n### デバイスの状態変化通知を表示\r\nデバイスの状態変化通知を詳細に表示し、選択された通知のみを表示します。",
|
||||
"data": {
|
||||
"devices_filter": "デバイスをフィルタリング",
|
||||
"ctrl_mode": "コントロールモード",
|
||||
"action_debug": "Actionデバッグモード",
|
||||
"hide_non_standard_entities": "非標準生成エンティティを隠す",
|
||||
"display_binary_mode": "バイナリセンサー表示モード",
|
||||
"display_devices_changed_notify": "デバイスの状態変化通知を表示"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "ローカルデバイス検出サービスに異常があります。",
|
||||
"get_cert_error": "ゲートウェイ証明書を取得できませんでした。",
|
||||
"no_family_selected": "家庭が選択されていません。",
|
||||
"no_devices": "選択された家庭にデバイスがありません。デバイスがある家庭を選択して続行してください。",
|
||||
"no_central_device": "【中央ゲートウェイモード】Home Assistant が存在する LAN 内に使用可能な Xiaomi 中央ゲートウェイがある必要があります。選択された家庭がこの要件を満たしているかどうかを確認してください。"
|
||||
"no_devices": "選択した家庭にはデバイスがありません。デバイスがある家庭を選択して続行してください。",
|
||||
"no_filter_devices": "フィルタリングされたデバイスが空です。有効なフィルター条件を選択して続行してください。",
|
||||
"no_central_device": "【中央ゲートウェイモード】Home Assistant が存在する LAN 内に使用可能な Xiaomi 中央ゲートウェイがある必要があります。選択された家庭がこの要件を満たしているかどうかを確認してください。",
|
||||
"invalid_network_addr": "無効なIPアドレスまたはHTTPアドレスが検出されました。有効なアドレスを入力してください。",
|
||||
"invalid_ip_addr": "アクセスできないIPアドレスが検出されました。有効なIPアドレスを入力してください。",
|
||||
"invalid_http_addr": "アクセスできないHTTPアドレスが検出されました。有効なHTTPアドレスを入力してください。",
|
||||
"invalid_default_addr": "デフォルトのネットワーク検出アドレスにアクセスできません。ネットワーク設定を確認するか、カスタムネットワーク検出アドレスを使用してください。",
|
||||
"unreachable_oauth2_host": "OAuth2 認証アドレスにアクセスできません。ネットワーク設定を確認してください。",
|
||||
"unreachable_http_host": "Xiaomi HTTP API アドレスにアクセスできません。ネットワーク設定を確認してください。",
|
||||
"unreachable_spec_host": "Xiaomi SPEC API アドレスにアクセスできません。ネットワーク設定を確認してください。",
|
||||
"unreachable_mqtt_broker": "Xiaomi MQTT ブローカーアドレスにアクセスできません。ネットワーク設定を確認してください。"
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Home Assistant インスタンスIDを取得できませんでした。",
|
||||
"network_connect_error": "設定に失敗しました。ネットワーク接続に異常があります。デバイスのネットワーク設定を確認してください。",
|
||||
"already_configured": "このユーザーはすでに設定が完了しています。統合ページにアクセスして、「設定」ボタンをクリックして設定を変更してください。",
|
||||
"invalid_auth_info": "認証情報が期限切れになりました。統合ページにアクセスして、「設定」ボタンをクリックして再度認証してください。",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "設定オプション",
|
||||
"description": "### {nick_name} さん、こんにちは!\r\n\r\nXiaomi アカウントID:{uid}\r\n現在のログインエリア:{cloud_server}\r\n\r\n必要な構成オプションを選択して、[次へ] をクリックしてください。",
|
||||
"description": "### {nick_name} さん、こんにちは!\r\n\r\nXiaomi アカウントID:{uid}\r\n現在のログインエリア:{cloud_server}\r\n統合インスタンスID: {instance_id}\r\n\r\n必要な構成オプションを選択して、[次へ] をクリックしてください。",
|
||||
"data": {
|
||||
"integration_language": "統合言語",
|
||||
"update_user_info": "ユーザー情報を更新する",
|
||||
"update_devices": "デバイスリストを更新する",
|
||||
"action_debug": "Action デバッグモード",
|
||||
"hide_non_standard_entities": "非標準生成エンティティを非表示にする",
|
||||
"display_binary_mode": "バイナリセンサー表示モード",
|
||||
"display_devices_changed_notify": "デバイスの状態変化通知を表示",
|
||||
"update_trans_rules": "エンティティ変換ルールを更新する",
|
||||
"update_lan_ctrl_config": "LAN制御構成を更新する"
|
||||
"update_lan_ctrl_config": "LAN制御構成を更新する",
|
||||
"network_detect_config": "統合ネットワーク構成"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "LANサブスクリプションを有効にする"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "統合ネットワーク構成",
|
||||
"description": "## 使用方法の紹介\r\n### ネットワーク検出アドレス\r\nネットワークが正常に機能しているかどうかを確認するために使用されます。設定されていない場合、システムのデフォルトアドレスが使用されます。デフォルトアドレスのチェックが失敗した場合は、カスタムアドレスを入力してみてください。\r\n- 複数の検出アドレスを入力できます。アドレスはコンマで区切ります。例:`8.8.8.8,https://www.bing.com`\r\n- IPアドレスの場合、pingによる検出が行われます。HTTP(s)アドレスの場合、HTTP GETリクエストによる検出が行われます。\r\n- システムのデフォルト検出アドレスを復元する場合は、カンマ `,` を入力して「次へ」をクリックしてください。\r\n- **この設定はグローバルであり、変更は他の統合インスタンスに影響を与えます。慎重に変更してください。**\r\n### ネットワーク依存関係のチェック\r\n次のネットワーク依存関係がアクセス可能かどうかを順番に確認します。関連するアドレスにアクセスできない場合、統合に問題が発生します。\r\n- OAuth2 認証アドレス: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API アドレス: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API アドレス: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT ブローカーアドレス: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "ネットワーク検出アドレス",
|
||||
"check_network_deps": "ネットワーク依存関係のチェック"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "構成を確認する",
|
||||
"description": "**{nick_name}** さん、こんにちは! 最新の構成情報を確認してください。[送信] をクリックして、更新された構成を使用して再度読み込みます。\r\n\r\n統合言語:\t{lang_new}\r\nユーザー名:\t{nick_name_new}\r\nAction デバッグモード:\t{action_debug}\r\n非標準生成エンティティを非表示にする:\t{hide_non_standard_entities}\r\nデバイスの状態変化通知を表示:\t{display_devices_changed_notify}\r\nデバイス変更:\t追加 **{devices_add}** 個のデバイス、削除 **{devices_remove}** 個のデバイス\r\n変換ルール変更:\t合計 **{trans_rules_count}** 個の規則、更新 **{trans_rules_count_success}** 個の規則",
|
||||
@ -168,11 +198,20 @@
|
||||
"get_homeinfo_error": "家庭情報の取得に失敗しました。",
|
||||
"get_cert_error": "中枢証明書の取得に失敗しました。",
|
||||
"no_family_selected": "家族が選択されていません。",
|
||||
"no_devices": "選択された家庭にはデバイスがありません。デバイスがある家庭を選択してから続行してください。",
|
||||
"no_devices": "選択した家庭にはデバイスがありません。デバイスがある家庭を選択して続行してください。",
|
||||
"no_filter_devices": "フィルタリングされたデバイスが空です。有効なフィルター条件を選択して続行してください。",
|
||||
"no_central_device": "【中枢ゲートウェイモード】には、Home Assistantが存在するローカルネットワークに使用可能なXiaomi Central Hub Gatewayが存在する必要があります。選択された家庭がこの要件を満たしているかどうかを確認してください。",
|
||||
"mdns_discovery_error": "ローカルデバイス発見サービスが異常です。",
|
||||
"update_config_error": "構成情報の更新に失敗しました。",
|
||||
"not_confirm": "変更を確認していません。確認をチェックしてから送信してください。"
|
||||
"not_confirm": "変更を確認していません。確認をチェックしてから送信してください。",
|
||||
"invalid_network_addr": "無効なIPアドレスまたはHTTPアドレスが検出されました。有効なアドレスを入力してください。",
|
||||
"invalid_ip_addr": "アクセスできないIPアドレスが検出されました。有効なIPアドレスを入力してください。",
|
||||
"invalid_http_addr": "アクセスできないHTTPアドレスが検出されました。有効なHTTPアドレスを入力してください。",
|
||||
"invalid_default_addr": "デフォルトのネットワーク検出アドレスにアクセスできません。ネットワーク設定を確認するか、カスタムネットワーク検出アドレスを使用してください。",
|
||||
"unreachable_oauth2_host": "OAuth2 認証アドレスにアクセスできません。ネットワーク設定を確認してください。",
|
||||
"unreachable_http_host": "Xiaomi HTTP API アドレスにアクセスできません。ネットワーク設定を確認してください。",
|
||||
"unreachable_spec_host": "Xiaomi SPEC API アドレスにアクセスできません。ネットワーク設定を確認してください。",
|
||||
"unreachable_mqtt_broker": "Xiaomi MQTT ブローカーアドレスにアクセスできません。ネットワーク設定を確認してください。"
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "構成に失敗しました。ネットワーク接続に異常があります。デバイスのネットワーク構成を確認してください。",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Basisconfiguratie",
|
||||
"description": "### Inlogregio\r\nSelecteer de regio van uw Xiaomi-account. U kunt deze vinden in de Xiaomi Home APP > Profiel (onderin het menu) > Extra instellingen > Over Xiaomi Home.\r\n### Taal\r\nKies de taal voor de apparaats- en entiteitsnamen. Sommige zinnen zonder vertaling worden in het Engels weergegeven.\r\n### OAuth2 Omleidings-URL\r\nHet OAuth2 authenticatie omleidingsadres is **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant moet zich in hetzelfde lokale netwerk bevinden als de huidige werkterminal (bijv. de persoonlijke computer) en de werkterminal moet toegang hebben tot de startpagina van Home Assistant via dit adres. Anders kan de inlogauthenticatie mislukken.\r\n### Opmerking\r\n- Voor gebruikers met honderden of meer Mi Home-apparaten kan het aanvankelijke toevoegen van de integratie enige tijd duren. Wees geduldig.\r\n- Als Home Assistant draait in een Docker-omgeving, zorg er dan voor dat de Docker-netwerkmodus is ingesteld op host, anders werkt de lokale controlefunctionaliteit mogelijk niet correct.\r\n- De lokale controlefunctionaliteit van de integratie heeft enkele afhankelijkheden. Lees het README zorgvuldig.",
|
||||
"description": "### Inlogregio\r\nSelecteer de regio van uw Xiaomi-account. U kunt deze vinden in de Xiaomi Home APP > Profiel (onderin het menu) > Extra instellingen > Over Xiaomi Home.\r\n### Taal\r\nKies de taal voor de apparaats- en entiteitsnamen. Sommige zinnen zonder vertaling worden in het Engels weergegeven.\r\n### OAuth2 Omleidings-URL\r\nHet OAuth2 authenticatie omleidingsadres is **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. Home Assistant moet zich in hetzelfde lokale netwerk bevinden als de huidige werkterminal (bijv. de persoonlijke computer) en de werkterminal moet toegang hebben tot de startpagina van Home Assistant via dit adres. Anders kan de inlogauthenticatie mislukken.\r\n### Geïntegreerde Netwerkconfiguratie\r\nControleer of het lokale netwerk correct functioneert en of de gerelateerde netwerkbronnen toegankelijk zijn. **Het wordt aanbevolen om dit te selecteren bij het eerste toevoegen.**\r\n### Opmerking\r\n- Voor gebruikers met honderden of meer Mi Home-apparaten kan het aanvankelijke toevoegen van de integratie enige tijd duren. Wees geduldig.\r\n- Als Home Assistant draait in een Docker-omgeving, zorg er dan voor dat de Docker-netwerkmodus is ingesteld op host, anders werkt de lokale controlefunctionaliteit mogelijk niet correct.\r\n- De lokale controlefunctionaliteit van de integratie heeft enkele afhankelijkheden. Lees het README zorgvuldig.",
|
||||
"data": {
|
||||
"cloud_server": "Inlogregio",
|
||||
"integration_language": "Taal",
|
||||
"oauth_redirect_url": "OAuth2 Omleidings-URL"
|
||||
"oauth_redirect_url": "OAuth2 Omleidings-URL",
|
||||
"network_detect_config": "Geïntegreerde Netwerkconfiguratie"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Geïntegreerde Netwerkconfiguratie",
|
||||
"description": "## Gebruiksinstructie\r\n### Netwerkdetectieadres\r\nWordt gebruikt om te controleren of het netwerk correct functioneert. Als dit niet is ingesteld, wordt het standaardadres van het systeem gebruikt. Als de standaardadrescontrole mislukt, kunt u proberen een aangepast adres in te voeren.\r\n- U kunt meerdere detectieadressen invoeren, gescheiden door komma's, zoals `8.8.8.8,https://www.bing.com`\r\n- Als het een IP-adres is, wordt de detectie uitgevoerd via ping. Als het een HTTP(s)-adres is, wordt de detectie uitgevoerd via een HTTP GET-verzoek.\r\n- Als u het standaarddetectieadres van het systeem wilt herstellen, voert u een komma `,` in en klikt u op 'Volgende'.\r\n- **Deze configuratie is globaal en wijzigingen zullen andere integratie-instanties beïnvloeden. Wijzig met voorzichtigheid.**\r\n### Controleer Netwerkafhankelijkheden\r\nControleer een voor een de volgende netwerkafhankelijkheden om te zien of ze toegankelijk zijn. Als de gerelateerde adressen niet toegankelijk zijn, zal dit integratieproblemen veroorzaken.\r\n- OAuth2-authenticatieadres: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API-adres: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API-adres: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT Broker-adres: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Netwerkdetectieadres",
|
||||
"check_network_deps": "Controleer Netwerkafhankelijkheden"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Geavanceerde Instellingen",
|
||||
"description": "## Inleiding\r\n### Tenzij u zeer goed op de hoogte bent van de betekenis van de volgende opties, houdt u de standaardinstellingen.\r\n### Apparaten filteren\r\nOndersteunt het filteren van apparaten op basis van kamer- en apparaattypen, en ondersteunt ook apparaatdimensiefiltering.\r\n### Besturingsmodus\r\n- Automatisch: Wanneer er een beschikbare Xiaomi centrale hubgateway in het lokale netwerk is, zal Home Assistant eerst apparaatbesturingsinstructies via de centrale hubgateway verzenden om lokale controlefunctionaliteit te bereiken. Als er geen centrale hub in het lokale netwerk is, zal het proberen om besturingsinstructies via het Xiaomi OT-protocol te verzenden om lokale controlefunctionaliteit te bereiken. Alleen als de bovenstaande lokale controlevoorwaarden niet worden vervuld, worden apparaatbesturingsinstructies via de cloud verzonden.\r\n- Cloud: Besturingsinstructies worden alleen via de cloud verzonden.\r\n### Actie-debugmodus\r\nVoor methoden die zijn gedefinieerd in de MIoT-Spec-V2 van het apparaat, wordt naast het genereren van een meldingsentiteit ook een tekstinvoerveldentiteit gegenereerd. U kunt dit gebruiken om besturingsinstructies naar het apparaat te sturen tijdens het debuggen.\r\n### Niet-standaard entiteiten verbergen\r\nVerberg entiteiten die zijn gegenereerd door niet-standaard MIoT-Spec-V2-instanties die beginnen met \"*\".\r\n### Apparaatstatuswijzigingen weergeven\r\nGedetailleerde apparaatstatuswijzigingen weergeven, alleen de geselecteerde meldingen weergeven.",
|
||||
"description": "## Inleiding\r\n### Tenzij u zeer goed op de hoogte bent van de betekenis van de volgende opties, houdt u de standaardinstellingen.\r\n### Apparaten filteren\r\nOndersteunt het filteren van apparaten op basis van kamer- en apparaattypen, en ondersteunt ook apparaatdimensiefiltering.\r\n### Besturingsmodus\r\n- Automatisch: Wanneer er een beschikbare Xiaomi centrale hubgateway in het lokale netwerk is, zal Home Assistant eerst apparaatbesturingsinstructies via de centrale hubgateway verzenden om lokale controlefunctionaliteit te bereiken. Als er geen centrale hub in het lokale netwerk is, zal het proberen om besturingsinstructies via het Xiaomi OT-protocol te verzenden om lokale controlefunctionaliteit te bereiken. Alleen als de bovenstaande lokale controlevoorwaarden niet worden vervuld, worden apparaatbesturingsinstructies via de cloud verzonden.\r\n- Cloud: Besturingsinstructies worden alleen via de cloud verzonden.\r\n### Actie-debugmodus\r\nVoor methoden die zijn gedefinieerd in de MIoT-Spec-V2 van het apparaat, wordt naast het genereren van een meldingsentiteit ook een tekstinvoerveldentiteit gegenereerd. U kunt dit gebruiken om besturingsinstructies naar het apparaat te sturen tijdens het debuggen.\r\n### Niet-standaard entiteiten verbergen\r\nVerberg entiteiten die zijn gegenereerd door niet-standaard MIoT-Spec-V2-instanties die beginnen met \"*\".\r\n### Binaire sensorweergavemodus\r\nToont binaire sensoren in Xiaomi Home als tekstsensor-entiteit of binairesensor-entiteit。\r\n### Apparaatstatuswijzigingen weergeven\r\nGedetailleerde apparaatstatuswijzigingen weergeven, alleen de geselecteerde meldingen weergeven.",
|
||||
"data": {
|
||||
"devices_filter": "Apparaten filteren",
|
||||
"ctrl_mode": "Besturingsmodus",
|
||||
"action_debug": "Actie-debugmodus",
|
||||
"hide_non_standard_entities": "Niet-standaard entiteiten verbergen",
|
||||
"display_binary_mode": "Binaire sensorweergavemodus",
|
||||
"display_devices_changed_notify": "Apparaatstatuswijzigingen weergeven"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Lokaal apparaatsontdekkingsservice-exceptie.",
|
||||
"get_cert_error": "Mislukt bij het ophalen van het certificaat van de centrale hubgateway.",
|
||||
"no_family_selected": "Geen huis geselecteerd.",
|
||||
"no_devices": "Het geselecteerde huis heeft geen apparaten. Kies a.u.b. een huis met apparaten en ga verder.",
|
||||
"no_central_device": "[Centrale Hub Gateway Modus] vereist een beschikbare Xiaomi centrale hubgateway in het lokale netwerk waar Home Assistant zich bevindt. Controleer of het geselecteerde huis aan deze vereiste voldoet."
|
||||
"no_devices": "Er zijn geen apparaten in het geselecteerde huis. Selecteer een huis met apparaten en ga verder.",
|
||||
"no_filter_devices": "Gefilterde apparaten zijn leeg. Selecteer geldige filtercriteria en ga verder.",
|
||||
"no_central_device": "[Centrale Hub Gateway Modus] vereist een beschikbare Xiaomi centrale hubgateway in het lokale netwerk waar Home Assistant zich bevindt. Controleer of het geselecteerde huis aan deze vereiste voldoet.",
|
||||
"invalid_network_addr": "Ongeldig IP-adres of HTTP-adres gedetecteerd, voer een geldig adres in.",
|
||||
"invalid_ip_addr": "Onbereikbaar IP-adres gedetecteerd, voer een geldig IP-adres in.",
|
||||
"invalid_http_addr": "Onbereikbaar HTTP-adres gedetecteerd, voer een geldig HTTP-adres in.",
|
||||
"invalid_default_addr": "Standaard netwerkdetectieadres is onbereikbaar, controleer de netwerkconfiguratie of gebruik een aangepast netwerkdetectieadres.",
|
||||
"unreachable_oauth2_host": "Kan OAuth2-authenticatieadres niet bereiken, controleer de netwerkconfiguratie.",
|
||||
"unreachable_http_host": "Kan Xiaomi HTTP API-adres niet bereiken, controleer de netwerkconfiguratie.",
|
||||
"unreachable_spec_host": "Kan Xiaomi SPEC API-adres niet bereiken, controleer de netwerkconfiguratie.",
|
||||
"unreachable_mqtt_broker": "Kan Xiaomi MQTT Broker-adres niet bereiken, controleer de netwerkconfiguratie."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Mislukt bij het ophalen van Home Assistant UUID.",
|
||||
"network_connect_error": "Configuratie mislukt. De netwerkverbinding is abnormaal. Controleer de netwerkinstellingen van de apparatuur.",
|
||||
"already_configured": "Configuratie voor deze gebruiker is al voltooid. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om wijzigingen aan te brengen.",
|
||||
"invalid_auth_info": "Authenticatie-informatie is verlopen. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om opnieuw te authentiseren.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Configuratie-opties",
|
||||
"description": "### Hallo, {nick_name}\r\n\r\nXiaomi ID: {uid}\r\nHuidige inlogregio: {cloud_server}\r\n\r\nKies de opties die u wilt configureren en klik vervolgens op VOLGENDE.",
|
||||
"description": "### Hallo, {nick_name}\r\n\r\nXiaomi ID: {uid}\r\nHuidige inlogregio: {cloud_server}\r\nIntegratie-instantie-ID: {instance_id}\r\n\r\nKies de opties die u wilt configureren en klik vervolgens op VOLGENDE.",
|
||||
"data": {
|
||||
"integration_language": "Integratietaal",
|
||||
"update_user_info": "Werk gebruikersinformatie bij",
|
||||
"update_devices": "Werk apparatenlijst bij",
|
||||
"action_debug": "Debugmodus voor actie",
|
||||
"hide_non_standard_entities": "Verberg niet-standaard gemaakte entiteiten",
|
||||
"display_binary_mode": "Binaire sensorweergavemodus",
|
||||
"display_devices_changed_notify": "Apparaatstatuswijzigingen weergeven",
|
||||
"update_trans_rules": "Werk entiteitsconversieregels bij",
|
||||
"update_lan_ctrl_config": "Werk LAN controleconfiguratie bij"
|
||||
"update_lan_ctrl_config": "Werk LAN controleconfiguratie bij",
|
||||
"network_detect_config": "Geïntegreerde Netwerkconfiguratie"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "Zet LAN-abonnement aan"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Geïntegreerde Netwerkconfiguratie",
|
||||
"description": "## Gebruiksinstructie\r\n### Netwerkdetectieadres\r\nWordt gebruikt om te controleren of het netwerk correct functioneert. Als dit niet is ingesteld, wordt het standaardadres van het systeem gebruikt. Als de standaardadrescontrole mislukt, kunt u proberen een aangepast adres in te voeren.\r\n- U kunt meerdere detectieadressen invoeren, gescheiden door komma's, zoals `8.8.8.8,https://www.bing.com`\r\n- Als het een IP-adres is, wordt de detectie uitgevoerd via ping. Als het een HTTP(s)-adres is, wordt de detectie uitgevoerd via een HTTP GET-verzoek.\r\n- Als u het standaarddetectieadres van het systeem wilt herstellen, voert u een komma `,` in en klikt u op 'Volgende'.\r\n- **Deze configuratie is globaal en wijzigingen zullen andere integratie-instanties beïnvloeden. Wijzig met voorzichtigheid.**\r\n### Controleer Netwerkafhankelijkheden\r\nControleer een voor een de volgende netwerkafhankelijkheden om te zien of ze toegankelijk zijn. Als de gerelateerde adressen niet toegankelijk zijn, zal dit integratieproblemen veroorzaken.\r\n- OAuth2-authenticatieadres: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Xiaomi HTTP API-adres: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Xiaomi SPEC API-adres: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Xiaomi MQTT Broker-adres: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Netwerkdetectieadres",
|
||||
"check_network_deps": "Controleer Netwerkafhankelijkheden"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Bevestig Configuratie",
|
||||
"description": "Hallo **{nick_name}**, bevestig alstublieft de nieuwste configuratie-informatie en klik vervolgens op INDENKEN.\r\nDe integratie zal opnieuw laden met de bijgewerkte configuratie.\r\n\r\nIntegratietaal: \t{lang_new}\r\nBijnaam: \t{nick_name_new}\r\nDebugmodus voor actie: \t{action_debug}\r\nVerberg niet-standaard gemaakte entiteiten: \t{hide_non_standard_entities}\r\nApparaatstatuswijzigingen weergeven:\t{display_devices_changed_notify}\r\nWijzigingen in apparaten: \tVoeg **{devices_add}** apparaten toe, Verwijder **{devices_remove}** apparaten\r\nWijzigingen in transformateregels: \tEr zijn in totaal **{trans_rules_count}** regels, en **{trans_rules_count_success}** regels zijn bijgewerkt",
|
||||
@ -167,12 +197,21 @@
|
||||
"get_token_error": "Mislukt bij het ophalen van inlogautorisatie-informatie (OAuth-token).",
|
||||
"get_homeinfo_error": "Mislukt bij het ophalen van huisinformatie.",
|
||||
"get_cert_error": "Mislukt bij het ophalen van het certificaat van de centrale hubgateway.",
|
||||
"no_devices": "Het geselecteerde huis heeft geen apparaten. Kies a.u.b. een huis met apparaten en ga verder.",
|
||||
"no_devices": "Er zijn geen apparaten in het geselecteerde huis. Selecteer een huis met apparaten en ga verder.",
|
||||
"no_filter_devices": "Gefilterde apparaten zijn leeg. Selecteer geldige filtercriteria en ga verder.",
|
||||
"no_family_selected": "Geen huis geselecteerd.",
|
||||
"no_central_device": "[Centrale Hub Gateway Modus] vereist een beschikbare Xiaomi centrale hubgateway in het lokale netwerk waar Home Assistant zich bevindt. Controleer of het geselecteerde huis aan deze vereiste voldoet.",
|
||||
"mdns_discovery_error": "Lokaal apparaatsontdekkingsservice-exceptie.",
|
||||
"update_config_error": "Mislukt bij het bijwerken van configuratie-informatie.",
|
||||
"not_confirm": "Wijzigingen zijn niet bevestigd. Bevestig de wijziging voordat u deze indient."
|
||||
"not_confirm": "Wijzigingen zijn niet bevestigd. Bevestig de wijziging voordat u deze indient.",
|
||||
"invalid_network_addr": "Ongeldig IP-adres of HTTP-adres gedetecteerd, voer een geldig adres in.",
|
||||
"invalid_ip_addr": "Onbereikbaar IP-adres gedetecteerd, voer een geldig IP-adres in.",
|
||||
"invalid_http_addr": "Onbereikbaar HTTP-adres gedetecteerd, voer een geldig HTTP-adres in.",
|
||||
"invalid_default_addr": "Standaard netwerkdetectieadres is onbereikbaar, controleer de netwerkconfiguratie of gebruik een aangepast netwerkdetectieadres.",
|
||||
"unreachable_oauth2_host": "Kan OAuth2-authenticatieadres niet bereiken, controleer de netwerkconfiguratie.",
|
||||
"unreachable_http_host": "Kan Xiaomi HTTP API-adres niet bereiken, controleer de netwerkconfiguratie.",
|
||||
"unreachable_spec_host": "Kan Xiaomi SPEC API-adres niet bereiken, controleer de netwerkconfiguratie.",
|
||||
"unreachable_mqtt_broker": "Kan Xiaomi MQTT Broker-adres niet bereiken, controleer de netwerkconfiguratie."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "Configuratie mislukt. De netwerkverbinding is abnormaal. Controleer de netwerkinstellingen van de apparatuur.",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Configuração básica",
|
||||
"description": "### Região de Login\r\nSelecione a região da sua conta Xiaomi. Você pode encontrá-la no aplicativo Xiaomi Home > Perfil (localizado no menu inferior) > Configurações adicionais > Sobre o Xiaomi Home.\r\n### Idioma\r\nSelecione o idioma dos nomes dos dispositivos e entidades. Algumas frases sem tradução serão exibidas em inglês.\r\n### URL de Redirecionamento OAuth2\r\nO endereço de redirecionamento da autenticação OAuth2 é **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. O Home Assistant precisa estar na mesma rede local que o terminal atual (por exemplo, o computador pessoal) e o terminal precisa acessar a página inicial do Home Assistant através desse endereço. Caso contrário, a autenticação de login pode falhar.\r\n### Observações\r\n- Para usuários com centenas ou mais dispositivos Mi Home, a adição inicial da integração levará algum tempo. Seja paciente.\r\n- Se o Home Assistant estiver sendo executado em um ambiente Docker, certifique-se de que o modo de rede do Docker esteja definido como host, caso contrário a funcionalidade de controle local pode não funcionar corretamente.\r\n- A funcionalidade de controle local da integração tem algumas dependências. Por favor, leia o README atentamente.",
|
||||
"description": "### Região de Login\r\nSelecione a região da sua conta Xiaomi. Você pode encontrá-la no aplicativo Xiaomi Home > Perfil (localizado no menu inferior) > Configurações adicionais > Sobre o Xiaomi Home.\r\n### Idioma\r\nSelecione o idioma dos nomes dos dispositivos e entidades. Algumas frases sem tradução serão exibidas em inglês.\r\n### URL de Redirecionamento OAuth2\r\nO endereço de redirecionamento da autenticação OAuth2 é **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. O Home Assistant precisa estar na mesma rede local que o terminal atual (por exemplo, o computador pessoal) e o terminal precisa acessar a página inicial do Home Assistant através desse endereço. Caso contrário, a autenticação de login pode falhar.\r\n### Configuração de Rede Integrada\r\nVerifique se a rede local está funcionando corretamente e se os recursos de rede relacionados estão acessíveis. **Recomenda-se selecionar isso ao adicionar pela primeira vez.**\r\n### Observações\r\n- Para usuários com centenas ou mais dispositivos Mi Home, a adição inicial da integração levará algum tempo. Seja paciente.\r\n- Se o Home Assistant estiver sendo executado em um ambiente Docker, certifique-se de que o modo de rede do Docker esteja definido como host, caso contrário a funcionalidade de controle local pode não funcionar corretamente.\r\n- A funcionalidade de controle local da integração tem algumas dependências. Por favor, leia o README atentamente.",
|
||||
"data": {
|
||||
"cloud_server": "Região de Login",
|
||||
"integration_language": "Idioma",
|
||||
"oauth_redirect_url": "URL de Redirecionamento OAuth2"
|
||||
"oauth_redirect_url": "URL de Redirecionamento OAuth2",
|
||||
"network_detect_config": "Configuração de Rede Integrada"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuração de Detecção de Rede",
|
||||
"description": "## Introdução ao Uso\r\n### Endereço de Detecção de Rede\r\nUsado para verificar se a rede está funcionando corretamente. Se não for definido, o endereço padrão do sistema será usado. Se a verificação do endereço padrão falhar, você pode tentar inserir um endereço personalizado.\r\n- Você pode inserir vários endereços de detecção, separados por vírgulas, como `8.8.8.8,https://www.bing.com`\r\n- Se for um endereço IP, a detecção será feita via ping. Se for um endereço HTTP(s), a detecção será feita via solicitação HTTP GET.\r\n- Se você deseja restaurar o endereço de detecção padrão do sistema, insira uma vírgula `,` e clique em 'Próximo'.\r\n- **Esta configuração é global e as alterações afetarão outras instâncias de integração. Modifique com cautela.**\r\n### Verificar Dependências de Rede\r\nVerifique uma por uma as seguintes dependências de rede para ver se são acessíveis. Se os endereços relacionados não forem acessíveis, isso causará problemas de integração.\r\n- Endereço de Autenticação OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Endereço da API HTTP da Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Endereço da API SPEC da Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Endereço do Broker MQTT da Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Endereço de Detecção de Rede",
|
||||
"check_network_deps": "Verificar Dependências de Rede"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Configurações Avançadas",
|
||||
"description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções a seguir, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi disponível na rede local está disponível, o Home Assistant enviará comandos de controle de dispositivo através do gateway central para realizar a função de controle local. Quando não há gateway central na rede local, ele tentará enviar comandos de controle através do protocolo OT da Xiaomi para realizar a função de controle local. Somente quando as condições de controle local acima não forem atendidas, os comandos de controle do dispositivo serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controle são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2 do dispositivo, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para você enviar comandos de controle ao dispositivo durante a depuração.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão que começam com \"*\".\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
|
||||
"description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções a seguir, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi disponível na rede local está disponível, o Home Assistant enviará comandos de controle de dispositivo através do gateway central para realizar a função de controle local. Quando não há gateway central na rede local, ele tentará enviar comandos de controle através do protocolo OT da Xiaomi para realizar a função de controle local. Somente quando as condições de controle local acima não forem atendidas, os comandos de controle do dispositivo serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controle são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2 do dispositivo, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para você enviar comandos de controle ao dispositivo durante a depuração.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão que começam com \"*\".\r\n### Modo de exibição do sensor binário\r\nExibe sensores binários no Xiaomi Home como entidade de sensor de texto ou entidade de sensor binário。\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
|
||||
"data": {
|
||||
"devices_filter": "Filtrar Dispositivos",
|
||||
"ctrl_mode": "Modo de Controle",
|
||||
"action_debug": "Modo de Depuração de Ações",
|
||||
"hide_non_standard_entities": "Ocultar Entidades Geradas Não Padrão",
|
||||
"display_binary_mode": "Modo de exibição do sensor binário",
|
||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Exceção no serviço de descoberta de dispositivos locais.",
|
||||
"get_cert_error": "Falha ao obter o certificado do gateway central.",
|
||||
"no_family_selected": "Nenhuma casa selecionada.",
|
||||
"no_devices": "A casa selecionada não possui nenhum dispositivo. Por favor, escolha uma casa que contenha dispositivos e continue.",
|
||||
"no_central_device": "[Modo Gateway Central] requer um gateway central Xiaomi disponível na rede local onde o Home Assistant está. Verifique se a casa selecionada atende a esse requisito."
|
||||
"no_devices": "Não há dispositivos na casa selecionada. Por favor, selecione uma casa com dispositivos e continue.",
|
||||
"no_filter_devices": "Os dispositivos filtrados estão vazios. Por favor, selecione critérios de filtro válidos e continue.",
|
||||
"no_central_device": "[Modo Gateway Central] requer um gateway central Xiaomi disponível na rede local onde o Home Assistant está. Verifique se a casa selecionada atende a esse requisito.",
|
||||
"invalid_network_addr": "Endereço IP ou HTTP inválido detectado, por favor insira um endereço válido.",
|
||||
"invalid_ip_addr": "Endereço IP inacessível detectado, por favor insira um endereço IP válido.",
|
||||
"invalid_http_addr": "Endereço HTTP inacessível detectado, por favor insira um endereço HTTP válido.",
|
||||
"invalid_default_addr": "O endereço de detecção de rede padrão está inacessível, por favor verifique a configuração da rede ou use um endereço de detecção de rede personalizado.",
|
||||
"unreachable_oauth2_host": "Não é possível acessar o endereço de autenticação OAuth2, verifique a configuração da rede.",
|
||||
"unreachable_http_host": "Não é possível acessar o endereço da API HTTP da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_spec_host": "Não é possível acessar o endereço da API SPEC da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Falha ao obter o UUID do Home Assistant.",
|
||||
"network_connect_error": "Configuração falhou. A conexão de rede está anormal. Verifique a configuração de rede do equipamento.",
|
||||
"already_configured": "A configuração para este usuário já foi concluída. Vá para a página de integrações e clique no botão CONFIGURAR para modificações.",
|
||||
"invalid_auth_info": "As informações de autenticação expiraram. Vá para a página de integrações e clique em CONFIGURAR para reautenticar.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Opções de Configuração",
|
||||
"description": "### Olá, {nick_name}\r\n\r\nID Xiaomi: {uid}\r\nRegião de Login Atual: {cloud_server}\r\n\r\nSelecione as opções que você deseja configurar e clique em AVANÇAR.",
|
||||
"description": "### Olá, {nick_name}\r\n\r\nID Xiaomi: {uid}\r\nRegião de Login Atual: {cloud_server}\r\nID da Instância de Integração: {instance_id}\r\n\r\nSelecione as opções que você deseja configurar e clique em AVANÇAR.",
|
||||
"data": {
|
||||
"integration_language": "Idioma da Integração",
|
||||
"update_user_info": "Atualizar informações do usuário",
|
||||
"update_devices": "Atualizar lista de dispositivos",
|
||||
"action_debug": "Modo de depuração para ação",
|
||||
"hide_non_standard_entities": "Ocultar entidades não padrão criadas",
|
||||
"display_binary_mode": "Modo de exibição do sensor binário",
|
||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
|
||||
"update_trans_rules": "Atualizar regras de conversão de entidades",
|
||||
"update_lan_ctrl_config": "Atualizar configuração de controle LAN"
|
||||
"update_lan_ctrl_config": "Atualizar configuração de controle LAN",
|
||||
"network_detect_config": "Configuração de Rede Integrada"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "Habilitar assinatura LAN"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuração de Detecção de Rede",
|
||||
"description": "## Introdução ao Uso\r\n### Endereço de Detecção de Rede\r\nUsado para verificar se a rede está funcionando corretamente. Se não for definido, o endereço padrão do sistema será usado. Se a verificação do endereço padrão falhar, você pode tentar inserir um endereço personalizado.\r\n- Você pode inserir vários endereços de detecção, separados por vírgulas, como `8.8.8.8,https://www.bing.com`\r\n- Se for um endereço IP, a detecção será feita via ping. Se for um endereço HTTP(s), a detecção será feita via solicitação HTTP GET.\r\n- Se você deseja restaurar o endereço de detecção padrão do sistema, insira uma vírgula `,` e clique em 'Próximo'.\r\n- **Esta configuração é global e as alterações afetarão outras instâncias de integração. Modifique com cautela.**\r\n### Verificar Dependências de Rede\r\nVerifique uma por uma as seguintes dependências de rede para ver se são acessíveis. Se os endereços relacionados não forem acessíveis, isso causará problemas de integração.\r\n- Endereço de Autenticação OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Endereço da API HTTP da Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Endereço da API SPEC da Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Endereço do Broker MQTT da Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Endereço de Detecção de Rede",
|
||||
"check_network_deps": "Verificar Dependências de Rede"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmar Configuração",
|
||||
"description": "Olá **{nick_name}**, confirme as informações da configuração mais recente e depois clique em ENVIAR.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nApelido:\t{nick_name_new}\r\nModo de depuração para ação:\t{action_debug}\r\nOcultar entidades não padrão criadas:\t{hide_non_standard_entities}\r\nExibir notificações de mudança de status do dispositivo:\t{display_devices_changed_notify}\r\nAlterações de Dispositivos:\tAdicionar **{devices_add}** dispositivos, Remover **{devices_remove}** dispositivos\r\nAlteração nas Regras de Transformação:\tUm total de **{trans_rules_count}** regras, e **{trans_rules_count_success}** regras atualizadas",
|
||||
@ -167,12 +197,21 @@
|
||||
"get_token_error": "Falha ao obter as informações de autorização de login (token OAuth).",
|
||||
"get_homeinfo_error": "Falha ao obter as informações da casa.",
|
||||
"get_cert_error": "Falha ao obter o certificado do gateway central.",
|
||||
"no_devices": "A casa selecionada não possui nenhum dispositivo. Por favor, escolha uma casa com dispositivos e continue.",
|
||||
"no_devices": "Não há dispositivos na casa selecionada. Por favor, selecione uma casa com dispositivos e continue.",
|
||||
"no_filter_devices": "Os dispositivos filtrados estão vazios. Por favor, selecione critérios de filtro válidos e continue.",
|
||||
"no_family_selected": "Nenhuma casa selecionada.",
|
||||
"no_central_device": "[Modo Gateway Central] requer um gateway central Xiaomi disponível na rede local onde o Home Assistant está. Verifique se a casa selecionada atende a esse requisito.",
|
||||
"mdns_discovery_error": "Exceção no serviço de descoberta de dispositivos locais.",
|
||||
"update_config_error": "Falha ao atualizar as informações de configuração.",
|
||||
"not_confirm": "As alterações não foram confirmadas. Por favor, confirme a mudança antes de enviar."
|
||||
"not_confirm": "As alterações não foram confirmadas. Por favor, confirme a mudança antes de enviar.",
|
||||
"invalid_network_addr": "Endereço IP ou HTTP inválido detectado, por favor insira um endereço válido.",
|
||||
"invalid_ip_addr": "Endereço IP inacessível detectado, por favor insira um endereço IP válido.",
|
||||
"invalid_http_addr": "Endereço HTTP inacessível detectado, por favor insira um endereço HTTP válido.",
|
||||
"invalid_default_addr": "O endereço de detecção de rede padrão está inacessível, por favor verifique a configuração da rede ou use um endereço de detecção de rede personalizado.",
|
||||
"unreachable_oauth2_host": "Não é possível acessar o endereço de autenticação OAuth2, verifique a configuração da rede.",
|
||||
"unreachable_http_host": "Não é possível acessar o endereço da API HTTP da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_spec_host": "Não é possível acessar o endereço da API SPEC da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "Configuração falhou. A conexão de rede está anormal. Verifique a configuração da rede do equipamento.",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Configuração Básica",
|
||||
"description": "### Região de Login\r\nSelecione a região da sua conta Xiaomi. Pode encontrá-la na aplicação Xiaomi Home > Perfil (menu inferior) > Configurações adicionais > Sobre o Xiaomi Home.\r\n### Idioma\r\nSelecione o idioma para os nomes de dispositivos e entidades. Algumas frases sem tradução serão apresentadas em inglês.\r\n### URL de Redirecionamento OAuth2\r\nO endereço de redirecionamento para a autenticação OAuth2 é **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. O Home Assistant deve estar na mesma rede local que o terminal atual (por exemplo, o computador pessoal) e esse terminal deve conseguir aceder à página inicial do Home Assistant através deste endereço. Caso contrário, a autenticação de login pode falhar.\r\n### Notas\r\n- Para utilizadores com centenas (ou mais) de dispositivos Mi Home, a adição inicial da integração demorará algum tempo. Seja paciente.\r\n- Se o Home Assistant estiver a ser executado num ambiente Docker, assegure-se de que o modo de rede do Docker está configurado como host; caso contrário, a funcionalidade de controlo local pode não funcionar corretamente.\r\n- A funcionalidade de controlo local da integração tem algumas dependências. Leia cuidadosamente o README.",
|
||||
"description": "### Região de Login\r\nSelecione a região da sua conta Xiaomi. Pode encontrá-la na aplicação Xiaomi Home > Perfil (menu inferior) > Configurações adicionais > Sobre o Xiaomi Home.\r\n### Idioma\r\nSelecione o idioma para os nomes de dispositivos e entidades. Algumas frases sem tradução serão apresentadas em inglês.\r\n### URL de Redirecionamento OAuth2\r\nO endereço de redirecionamento para a autenticação OAuth2 é **[http://homeassistant.local:8123](http://homeassistant.local:8123)**. O Home Assistant deve estar na mesma rede local que o terminal atual (por exemplo, o computador pessoal) e esse terminal deve conseguir aceder à página inicial do Home Assistant através deste endereço. Caso contrário, a autenticação de login pode falhar.\r\n### Configuração de Rede Integrada\r\nVerifique se a rede local está funcionando corretamente e se os recursos de rede relacionados estão acessíveis. **Recomenda-se selecionar isso ao adicionar pela primeira vez.**\r\n### Notas\r\n- Para utilizadores com centenas (ou mais) de dispositivos Mi Home, a adição inicial da integração demorará algum tempo. Seja paciente.\r\n- Se o Home Assistant estiver a ser executado num ambiente Docker, assegure-se de que o modo de rede do Docker está configurado como host; caso contrário, a funcionalidade de controlo local pode não funcionar corretamente.\r\n- A funcionalidade de controlo local da integração tem algumas dependências. Leia cuidadosamente o README.",
|
||||
"data": {
|
||||
"cloud_server": "Região de Login",
|
||||
"integration_language": "Idioma",
|
||||
"oauth_redirect_url": "URL de Redirecionamento OAuth2"
|
||||
"oauth_redirect_url": "URL de Redirecionamento OAuth2",
|
||||
"network_detect_config": "Configuração de Rede Integrada"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuração de Rede Integrada",
|
||||
"description": "## Introdução ao Uso\r\n### Endereço de Detecção de Rede\r\nUsado para verificar se a rede está funcionando corretamente. Se não for definido, o endereço padrão do sistema será usado. Se a verificação do endereço padrão falhar, você pode tentar inserir um endereço personalizado.\r\n- Você pode inserir vários endereços de detecção, separados por vírgulas, como `8.8.8.8,https://www.bing.com`\r\n- Se for um endereço IP, a detecção será feita via ping. Se for um endereço HTTP(s), a detecção será feita via solicitação HTTP GET.\r\n- Se você deseja restaurar o endereço de detecção padrão do sistema, insira uma vírgula `,` e clique em 'Próximo'.\r\n- **Esta configuração é global e as alterações afetarão outras instâncias de integração. Modifique com cautela.**\r\n### Verificar Dependências de Rede\r\nVerifique uma por uma as seguintes dependências de rede para ver se são acessíveis. Se os endereços relacionados não forem acessíveis, isso causará problemas de integração.\r\n- Endereço de Autenticação OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Endereço da API HTTP da Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Endereço da API SPEC da Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Endereço do Broker MQTT da Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Endereço de Detecção de Rede",
|
||||
"check_network_deps": "Verificar Dependências de Rede"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Opções Avançadas",
|
||||
"description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções abaixo, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi está disponível na rede local, o Home Assistant enviará comandos de controlo de dispositivos através do gateway central para realizar o controlo local. Quando não há gateway central na rede local, tentará enviar comandos de controlo através do protocolo Xiaomi OT para realizar o controlo local. Apenas quando as condições de controlo local acima não são atendidas, os comandos de controlo de dispositivos serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controlo são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para depuração de controlo de dispositivos.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão, cujos nomes começam com \"*\".\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
|
||||
"description": "## Introdução\r\n### A menos que você entenda claramente o significado das opções abaixo, mantenha as configurações padrão.\r\n### Filtrar Dispositivos\r\nSuporte para filtrar dispositivos por nome da sala e tipo de dispositivo, bem como filtragem por família.\r\n### Modo de Controle\r\n- Automático: Quando um gateway central Xiaomi está disponível na rede local, o Home Assistant enviará comandos de controlo de dispositivos através do gateway central para realizar o controlo local. Quando não há gateway central na rede local, tentará enviar comandos de controlo através do protocolo Xiaomi OT para realizar o controlo local. Apenas quando as condições de controlo local acima não são atendidas, os comandos de controlo de dispositivos serão enviados através da nuvem.\r\n- Nuvem: Os comandos de controlo são enviados apenas através da nuvem.\r\n### Modo de Depuração de Ações\r\nPara métodos definidos pelo MIoT-Spec-V2, além de gerar uma entidade de notificação, também será gerada uma entidade de caixa de texto para depuração de controlo de dispositivos.\r\n### Ocultar Entidades Geradas Não Padrão\r\nOcultar entidades geradas por instâncias MIoT-Spec-V2 não padrão, cujos nomes começam com \"*\".\r\n### Modo de exibição do sensor binário\r\nExibe sensores binários no Xiaomi Home como entidade de sensor de texto ou entidade de sensor binário。\r\n### Exibir notificações de mudança de status do dispositivo\r\nExibir notificações detalhadas de mudança de status do dispositivo, mostrando apenas as notificações selecionadas.",
|
||||
"data": {
|
||||
"devices_filter": "Filtrar Dispositivos",
|
||||
"ctrl_mode": "Modo de Controlo",
|
||||
"action_debug": "Modo de Depuração de Ações",
|
||||
"hide_non_standard_entities": "Ocultar Entidades Geradas Não Padrão",
|
||||
"display_binary_mode": "Modo de exibição do sensor binário",
|
||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Exceção no serviço de descoberta de dispositivos locais.",
|
||||
"get_cert_error": "Não foi possível obter o certificado do gateway central.",
|
||||
"no_family_selected": "Nenhuma casa selecionada.",
|
||||
"no_devices": "A casa selecionada não possui quaisquer dispositivos. Por favor, selecione uma casa que contenha dispositivos e continue.",
|
||||
"no_central_device": "O [Modo Gateway Central] requer um gateway central Xiaomi disponível na rede local onde o Home Assistant está. Verifique se a casa selecionada cumpre este requisito."
|
||||
"no_devices": "Não há dispositivos na casa selecionada. Por favor, selecione uma casa com dispositivos e continue.",
|
||||
"no_filter_devices": "Os dispositivos filtrados estão vazios. Por favor, selecione critérios de filtro válidos e continue.",
|
||||
"no_central_device": "O [Modo Gateway Central] requer um gateway central Xiaomi disponível na rede local onde o Home Assistant está. Verifique se a casa selecionada cumpre este requisito.",
|
||||
"invalid_network_addr": "Endereço IP ou HTTP inválido detectado, por favor insira um endereço válido.",
|
||||
"invalid_ip_addr": "Endereço IP inacessível detectado, por favor insira um endereço IP válido.",
|
||||
"invalid_http_addr": "Endereço HTTP inacessível detectado, por favor insira um endereço HTTP válido.",
|
||||
"invalid_default_addr": "O endereço de detecção de rede padrão está inacessível, por favor verifique a configuração da rede ou use um endereço de detecção de rede personalizado.",
|
||||
"unreachable_oauth2_host": "Não é possível acessar o endereço de autenticação OAuth2, verifique a configuração da rede.",
|
||||
"unreachable_http_host": "Não é possível acessar o endereço da API HTTP da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_spec_host": "Não é possível acessar o endereço da API SPEC da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Não foi possível obter o UUID do Home Assistant.",
|
||||
"network_connect_error": "A configuração falhou. A ligação de rede é anormal. Verifique a configuração de rede do equipamento.",
|
||||
"already_configured": "A configuração para este utilizador já foi concluída. Vá à página de integrações e clique em CONFIGURAR para efetuar alterações.",
|
||||
"invalid_auth_info": "A informação de autenticação expirou. Vá à página de integrações e clique em CONFIGURAR para reautenticar.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Opções de Configuração",
|
||||
"description": "### Olá, {nick_name}\r\n\r\nID Xiaomi: {uid}\r\nRegião de Login Atual: {cloud_server}\r\n\r\nSelecione as opções que pretende configurar e depois clique em SEGUINTE.",
|
||||
"description": "### Olá, {nick_name}\r\n\r\nID Xiaomi: {uid}\r\nRegião de Login Atual: {cloud_server}\r\nID da Instância de Integração: {instance_id}\r\n\r\nSelecione as opções que pretende configurar e depois clique em SEGUINTE.",
|
||||
"data": {
|
||||
"integration_language": "Idioma da Integração",
|
||||
"update_user_info": "Atualizar informação do utilizador",
|
||||
"update_devices": "Atualizar lista de dispositivos",
|
||||
"action_debug": "Modo de depuração de ação",
|
||||
"hide_non_standard_entities": "Ocultar entidades não padrão",
|
||||
"display_binary_mode": "Modo de exibição do sensor binário",
|
||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
|
||||
"update_trans_rules": "Atualizar regras de conversão de entidades",
|
||||
"update_lan_ctrl_config": "Atualizar configuração de controlo LAN"
|
||||
"update_lan_ctrl_config": "Atualizar configuração de controlo LAN",
|
||||
"network_detect_config": "Configuração de Rede Integrada"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "Ativar subscrição LAN"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Configuração de Rede Integrada",
|
||||
"description": "## Introdução ao Uso\r\n### Endereço de Detecção de Rede\r\nUsado para verificar se a rede está funcionando corretamente. Se não for definido, o endereço padrão do sistema será usado. Se a verificação do endereço padrão falhar, você pode tentar inserir um endereço personalizado.\r\n- Você pode inserir vários endereços de detecção, separados por vírgulas, como `8.8.8.8,https://www.bing.com`\r\n- Se for um endereço IP, a detecção será feita via ping. Se for um endereço HTTP(s), a detecção será feita via solicitação HTTP GET.\r\n- Se você deseja restaurar o endereço de detecção padrão do sistema, insira uma vírgula `,` e clique em 'Próximo'.\r\n- **Esta configuração é global e as alterações afetarão outras instâncias de integração. Modifique com cautela.**\r\n### Verificar Dependências de Rede\r\nVerifique uma por uma as seguintes dependências de rede para ver se são acessíveis. Se os endereços relacionados não forem acessíveis, isso causará problemas de integração.\r\n- Endereço de Autenticação OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Endereço da API HTTP da Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Endereço da API SPEC da Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Endereço do Broker MQTT da Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Endereço de Detecção de Rede",
|
||||
"check_network_deps": "Verificar Dependências de Rede"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmar Configuração",
|
||||
"description": "Olá **{nick_name}**, confirme a informação da configuração mais recente e depois clique em SUBMETER.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nAlcunha:\t{nick_name_new}\r\nModo de depuração de ação:\t{action_debug}\r\nOcultar entidades não padrão:\t{hide_non_standard_entities}\r\nExibir notificações de mudança de status do dispositivo:\t{display_devices_changed_notify}\r\nAlterações aos Dispositivos:\tAdicionar **{devices_add}** dispositivos, Remover **{devices_remove}** dispositivos\r\nAlteração das Regras de Transformação:\tExistem **{trans_rules_count}** regras no total, com **{trans_rules_count_success}** regras atualizadas",
|
||||
@ -167,12 +197,21 @@
|
||||
"get_token_error": "Não foi possível obter a informação de autorização de login (token OAuth).",
|
||||
"get_homeinfo_error": "Não foi possível obter a informação da casa.",
|
||||
"get_cert_error": "Não foi possível obter o certificado do gateway central.",
|
||||
"no_devices": "A casa selecionada não possui quaisquer dispositivos. Por favor, selecione uma casa com dispositivos e continue.",
|
||||
"no_devices": "Não há dispositivos na casa selecionada. Por favor, selecione uma casa com dispositivos e continue.",
|
||||
"no_filter_devices": "Os dispositivos filtrados estão vazios. Por favor, selecione critérios de filtro válidos e continue.",
|
||||
"no_family_selected": "Nenhuma casa selecionada.",
|
||||
"no_central_device": "O [Modo Gateway Central] requer um gateway central Xiaomi disponível na rede local onde o Home Assistant está. Verifique se a casa selecionada cumpre este requisito.",
|
||||
"mdns_discovery_error": "Exceção no serviço de descoberta de dispositivos locais.",
|
||||
"update_config_error": "Não foi possível atualizar a informação de configuração.",
|
||||
"not_confirm": "As alterações não foram confirmadas. Por favor, confirme a alteração antes de submeter."
|
||||
"not_confirm": "As alterações não foram confirmadas. Por favor, confirme a alteração antes de submeter.",
|
||||
"invalid_network_addr": "Endereço IP ou HTTP inválido detectado, por favor insira um endereço válido.",
|
||||
"invalid_ip_addr": "Endereço IP inacessível detectado, por favor insira um endereço IP válido.",
|
||||
"invalid_http_addr": "Endereço HTTP inacessível detectado, por favor insira um endereço HTTP válido.",
|
||||
"invalid_default_addr": "O endereço de detecção de rede padrão está inacessível, por favor verifique a configuração da rede ou use um endereço de detecção de rede personalizado.",
|
||||
"unreachable_oauth2_host": "Não é possível acessar o endereço de autenticação OAuth2, verifique a configuração da rede.",
|
||||
"unreachable_http_host": "Não é possível acessar o endereço da API HTTP da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_spec_host": "Não é possível acessar o endereço da API SPEC da Xiaomi, verifique a configuração da rede.",
|
||||
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "A configuração falhou. A ligação de rede é anormal. Verifique a configuração da rede do equipamento.",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "Основные настройки",
|
||||
"description": "### Регион входа в систему\r\nВыберите регион, в котором находится ваша учетная запись Xiaomi. Вы можете узнать об этом в `Xiaomi Home> Мой (в нижнем меню)> Дополнительные настройки> О Xiaomi Home`.\r\n### Язык\r\nВыберите язык, используемый для имен устройств и сущностей. Части предложений, которые не имеют перевода, будут отображаться на английском языке.\r\n### Адрес перенаправления для аутентификации OAuth2\r\nАдрес перенаправления для аутентификации OAuth2 - ** [http: //homeassistant.local: 8123] (http: //homeassistant.local: 8123) **, Home Assistant должен находиться в одной локальной сети с текущим терминалом (например, персональный компьютер), и терминал должен иметь доступ к домашней странице Home Assistant по этому адресу, в противном случае аутентификация входа может завершиться неудачно.\r\n### Примечание\r\n- Для пользователей с сотнями или более устройств Mi Home первоначальное добавление интеграции займет некоторое время. Пожалуйста, будьте терпеливы.\r\n- Если Home Assistant работает в среде Docker, убедитесь, что сетевой режим Docker установлен на host, иначе функция локального управления может работать неправильно.\r\n- Функция локального управления интеграции имеет некоторые зависимости. Пожалуйста, внимательно прочитайте README.",
|
||||
"description": "### Регион входа в систему\r\nВыберите регион, в котором находится ваша учетная запись Xiaomi. Вы можете узнать об этом в `Xiaomi Home> Мой (в нижнем меню)> Дополнительные настройки> О Xiaomi Home`.\r\n### Язык\r\nВыберите язык, используемый для имен устройств и сущностей. Части предложений, которые не имеют перевода, будут отображаться на английском языке.\r\n### Адрес перенаправления для аутентификации OAuth2\r\nАдрес перенаправления для аутентификации OAuth2 - ** [http: //homeassistant.local: 8123] (http: //homeassistant.local: 8123) **, Home Assistant должен находиться в одной локальной сети с текущим терминалом (например, персональный компьютер), и терминал должен иметь доступ к домашней странице Home Assistant по этому адресу, в противном случае аутентификация входа может завершиться неудачно.\r\n### Интегрированная Сетевая Конфигурация\r\nПроверьте, нормально ли функционирует локальная сеть и доступны ли связанные сетевые ресурсы. **Рекомендуется выбрать это при первом добавлении.**\r\n### Примечание\r\n- Для пользователей с сотнями или более устройств Mi Home первоначальное добавление интеграции займет некоторое время. Пожалуйста, будьте терпеливы.\r\n- Если Home Assistant работает в среде Docker, убедитесь, что сетевой режим Docker установлен на host, иначе функция локального управления может работать неправильно.\r\n- Функция локального управления интеграции имеет некоторые зависимости. Пожалуйста, внимательно прочитайте README.",
|
||||
"data": {
|
||||
"cloud_server": "Регион входа в систему",
|
||||
"integration_language": "Язык",
|
||||
"oauth_redirect_url": "Адрес перенаправления для аутентификации OAuth2"
|
||||
"oauth_redirect_url": "Адрес перенаправления для аутентификации OAuth2",
|
||||
"network_detect_config": "Интегрированная Сетевая Конфигурация"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Интегрированная Сетевая Конфигурация",
|
||||
"description": "## Введение в Использование\r\n### Адрес Обнаружения Сети\r\nИспользуется для проверки работоспособности сети. Если не задано, будет использоваться адрес по умолчанию. Если проверка адреса по умолчанию не удалась, вы можете попробовать ввести пользовательский адрес.\r\n- Вы можете ввести несколько адресов для проверки, разделенных запятыми, например `8.8.8.8,https://www.bing.com`\r\n- Если это IP-адрес, проверка будет выполняться с помощью ping. Если это HTTP(s)-адрес, проверка будет выполняться с помощью HTTP GET запроса.\r\n- Если вы хотите восстановить адрес обнаружения по умолчанию, введите запятую `,` и нажмите 'Далее'.\r\n- **Эта конфигурация является глобальной, и изменения повлияют на другие экземпляры интеграции. Пожалуйста, изменяйте с осторожностью.**\r\n### Проверка Сетевых Зависимостей\r\nПроверьте поочередно следующие сетевые зависимости, чтобы убедиться, что они доступны. Если соответствующие адреса недоступны, это приведет к проблемам с интеграцией.\r\n- Адрес аутентификации OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Адрес HTTP API Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Адрес SPEC API Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Адрес MQTT брокера Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Адрес Обнаружения Сети",
|
||||
"check_network_deps": "Проверка Сетевых Зависимостей"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "Расширенные настройки",
|
||||
"description": "## Введение\r\n### Если вы не очень хорошо понимаете значение следующих параметров, оставьте их по умолчанию.\r\n### Фильтрация устройств\r\nПоддерживает фильтрацию устройств по названию комнаты и типу устройства, а также фильтрацию по уровню устройства.\r\n### Режим управления\r\n- Автоматически: при наличии доступного центрального шлюза Xiaomi в локальной сети Home Assistant Home Assistant будет отправлять команды управления устройствами через центральный шлюз для локального управления. Если центрального шлюза нет в локальной сети, Home Assistant попытается отправить команды управления устройствами через протокол OT Xiaomi для локального управления. Только если вышеуказанные условия локального управления не выполняются, команды управления устройствами будут отправляться через облако.\r\n- Облако: команды управления отправляются только через облако.\r\n### Режим отладки действий\r\nДля методов, определенных устройством MIoT-Spec-V2, помимо создания уведомления, будет создана сущность текстового поля, которую вы можете использовать для отправки команд управления устройством во время отладки.\r\n### Скрыть нестандартные сущности\r\nСкрыть сущности, созданные нестандартными экземплярами MIoT-Spec-V2, имена которых начинаются с «*».\r\n### Отображать уведомления о изменении состояния устройства\r\nОтображать подробные уведомления о изменении состояния устройства, показывая только выбранные уведомления.",
|
||||
"description": "## Введение\r\n### Если вы не очень хорошо понимаете значение следующих параметров, оставьте их по умолчанию.\r\n### Фильтрация устройств\r\nПоддерживает фильтрацию устройств по названию комнаты и типу устройства, а также фильтрацию по уровню устройства.\r\n### Режим управления\r\n- Автоматически: при наличии доступного центрального шлюза Xiaomi в локальной сети Home Assistant Home Assistant будет отправлять команды управления устройствами через центральный шлюз для локального управления. Если центрального шлюза нет в локальной сети, Home Assistant попытается отправить команды управления устройствами через протокол OT Xiaomi для локального управления. Только если вышеуказанные условия локального управления не выполняются, команды управления устройствами будут отправляться через облако.\r\n- Облако: команды управления отправляются только через облако.\r\n### Режим отладки действий\r\nДля методов, определенных устройством MIoT-Spec-V2, помимо создания уведомления, будет создана сущность текстового поля, которую вы можете использовать для отправки команд управления устройством во время отладки.\r\n### Скрыть нестандартные сущности\r\nСкрыть сущности, созданные нестандартными экземплярами MIoT-Spec-V2, имена которых начинаются с «*».\r\n### Режим отображения бинарного датчика\r\nОтображает бинарные датчики в Xiaomi Home как сущность текстового датчика или сущность бинарного датчика。\r\n### Отображать уведомления о изменении состояния устройства\r\nОтображать подробные уведомления о изменении состояния устройства, показывая только выбранные уведомления.",
|
||||
"data": {
|
||||
"devices_filter": "Фильтрация устройств",
|
||||
"ctrl_mode": "Режим управления",
|
||||
"action_debug": "Режим отладки действий",
|
||||
"hide_non_standard_entities": "Скрыть нестандартные сущности",
|
||||
"display_binary_mode": "Режим отображения бинарного датчика",
|
||||
"display_devices_changed_notify": "Отображать уведомления о изменении состояния устройства"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "Сервис обнаружения локальных устройств недоступен.",
|
||||
"get_cert_error": "Не удалось получить сертификат центрального шлюза.",
|
||||
"no_family_selected": "Не выбрана домашняя сеть.",
|
||||
"no_devices": "В выбранной домашней сети нет устройств. Пожалуйста, выберите домашнюю сеть с устройствами и продолжайте.",
|
||||
"no_central_device": "Для режима центрального шлюза Xiaomi необходимо наличие доступного центрального шлюза Xiaomi в локальной сети Home Assistant. Проверьте, соответствует ли выбранная домашняя сеть этому требованию."
|
||||
"no_devices": "В выбранном доме нет устройств. Пожалуйста, выберите дом с устройствами и продолжите.",
|
||||
"no_filter_devices": "Список устройств после фильтрации пуст. Пожалуйста, выберите действительные условия фильтрации и продолжите.",
|
||||
"no_central_device": "Для режима центрального шлюза Xiaomi необходимо наличие доступного центрального шлюза Xiaomi в локальной сети Home Assistant. Проверьте, соответствует ли выбранная домашняя сеть этому требованию.",
|
||||
"invalid_network_addr": "Обнаружен недействительный IP-адрес или HTTP-адрес, пожалуйста, введите действительный адрес.",
|
||||
"invalid_ip_addr": "Обнаружен недоступный IP-адрес, пожалуйста, введите действительный IP-адрес.",
|
||||
"invalid_http_addr": "Обнаружен недоступный HTTP-адрес, пожалуйста, введите действительный HTTP-адрес.",
|
||||
"invalid_default_addr": "Адрес обнаружения сети по умолчанию недоступен, пожалуйста, проверьте конфигурацию сети или используйте пользовательский адрес обнаружения сети.",
|
||||
"unreachable_oauth2_host": "Не удается подключиться к адресу аутентификации OAuth2, проверьте настройки сети.",
|
||||
"unreachable_http_host": "Не удается подключиться к адресу HTTP API Xiaomi, проверьте настройки сети.",
|
||||
"unreachable_spec_host": "Не удается подключиться к адресу SPEC API Xiaomi, проверьте настройки сети.",
|
||||
"unreachable_mqtt_broker": "Не удается подключиться к адресу MQTT брокера Xiaomi, проверьте настройки сети."
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "Не удалось получить UUID Home Assistant.",
|
||||
"network_connect_error": "Ошибка настройки. Сетевое подключение недоступно. Проверьте настройки сети устройства.",
|
||||
"already_configured": "Этот пользователь уже настроен. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы изменить настройки.",
|
||||
"invalid_auth_info": "Информация об авторизации истекла. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы переавторизоваться.",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "Параметры настройки",
|
||||
"description": "### {nick_name} Здравствуйте!\r\n\r\nID учетной записи Xiaomi: {uid}\r\nТекущий регион входа в систему: {cloud_server}\r\n\r\nВыберите параметры, которые нужно настроить заново, а затем нажмите «Далее».",
|
||||
"description": "### {nick_name} Здравствуйте!\r\n\r\nID учетной записи Xiaomi: {uid}\r\nТекущий регион входа в систему: {cloud_server}\r\nID экземпляра интеграции: {instance_id}\r\n\r\nВыберите параметры, которые нужно настроить заново, а затем нажмите «Далее».",
|
||||
"data": {
|
||||
"integration_language": "Язык интеграции",
|
||||
"update_user_info": "Обновить информацию о пользователе",
|
||||
"update_devices": "Обновить список устройств",
|
||||
"action_debug": "Режим отладки Action",
|
||||
"hide_non_standard_entities": "Скрыть нестандартные сущности",
|
||||
"display_binary_mode": "Режим отображения бинарного датчика",
|
||||
"display_devices_changed_notify": "Отображать уведомления о изменении состояния устройства",
|
||||
"update_trans_rules": "Обновить правила преобразования сущностей",
|
||||
"update_lan_ctrl_config": "Обновить конфигурацию управления LAN"
|
||||
"update_lan_ctrl_config": "Обновить конфигурацию управления LAN",
|
||||
"network_detect_config": "Интегрированная Сетевая Конфигурация"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "Включить подписку LAN"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "Интегрированная Сетевая Конфигурация",
|
||||
"description": "## Введение в Использование\r\n### Адрес Обнаружения Сети\r\nИспользуется для проверки работоспособности сети. Если не задано, будет использоваться адрес по умолчанию. Если проверка адреса по умолчанию не удалась, вы можете попробовать ввести пользовательский адрес.\r\n- Вы можете ввести несколько адресов для проверки, разделенных запятыми, например `8.8.8.8,https://www.bing.com`\r\n- Если это IP-адрес, проверка будет выполняться с помощью ping. Если это HTTP(s)-адрес, проверка будет выполняться с помощью HTTP GET запроса.\r\n- Если вы хотите восстановить адрес обнаружения по умолчанию, введите запятую `,` и нажмите 'Далее'.\r\n- **Эта конфигурация является глобальной, и изменения повлияют на другие экземпляры интеграции. Пожалуйста, изменяйте с осторожностью.**\r\n### Проверка Сетевых Зависимостей\r\nПроверьте поочередно следующие сетевые зависимости, чтобы убедиться, что они доступны. Если соответствующие адреса недоступны, это приведет к проблемам с интеграцией.\r\n- Адрес аутентификации OAuth2: `https://account.xiaomi.com/oauth2/authorize`.\r\n- Адрес HTTP API Xiaomi: `https://{http_host}/app/v2/ha/oauth/get_token`.\r\n- Адрес SPEC API Xiaomi: `https://miot-spec.org/miot-spec-v2/template/list/device`.\r\n- Адрес MQTT брокера Xiaomi: `mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`.",
|
||||
"data": {
|
||||
"network_detect_addr": "Адрес Обнаружения Сети",
|
||||
"check_network_deps": "Проверка Сетевых Зависимостей"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Подтверждение настройки",
|
||||
"description": "**{nick_name}** Здравствуйте! Подтвердите последнюю информацию о настройке и нажмите «Отправить». Интеграция будет перезагружена с использованием обновленных настроек.\r\n\r\nЯзык интеграции:\t{lang_new}\r\nИмя пользователя:\t{nick_name_new}\r\nРежим отладки Action:\t{action_debug}\r\nСкрыть непроизводственные сущности:\t{hide_non_standard_entities}\r\nОтображать уведомления о изменении состояния устройства:\t{display_devices_changed_notify}\r\nИзменение устройства:\tДобавлено **{devices_add}** устройство, удалено **{devices_remove}** устройства\r\nИзменение правил преобразования:\tВсего **{trans_rules_count}** правил, обновлено **{trans_rules_count_success}** правил",
|
||||
@ -168,11 +198,20 @@
|
||||
"get_homeinfo_error": "Не удалось получить информацию о домашней сети.",
|
||||
"get_cert_error": "Не удалось получить центральный сертификат.",
|
||||
"no_family_selected": "Не выбрана семья.",
|
||||
"no_devices": "В выбранной семье нет устройств. Пожалуйста, выберите семью с устройствами и продолжайте.",
|
||||
"no_devices": "В выбранном доме нет устройств. Пожалуйста, выберите дом с устройствами и продолжите.",
|
||||
"no_filter_devices": "Список устройств после фильтрации пуст. Пожалуйста, выберите действительные условия фильтрации и продолжите.",
|
||||
"no_central_device": "Для режима центрального шлюза необходим существующий в локальной сети Home Assistant с доступным Xiaomi-шлюзом. Пожалуйста, проверьте, соответствует ли выбранная семья этому требованию.",
|
||||
"mdns_discovery_error": "Ошибка сервиса поиска локальных устройств.",
|
||||
"update_config_error": "Не удалось обновить информацию о конфигурации.",
|
||||
"not_confirm": "Изменение не подтверждено. Пожалуйста, отметьте для подтверждения и отправки."
|
||||
"not_confirm": "Изменение не подтверждено. Пожалуйста, отметьте для подтверждения и отправки.",
|
||||
"invalid_network_addr": "Обнаружен недействительный IP-адрес или HTTP-адрес, пожалуйста, введите действительный адрес.",
|
||||
"invalid_ip_addr": "Обнаружен недоступный IP-адрес, пожалуйста, введите действительный IP-адрес.",
|
||||
"invalid_http_addr": "Обнаружен недоступный HTTP-адрес, пожалуйста, введите действительный HTTP-адрес.",
|
||||
"invalid_default_addr": "Адрес обнаружения сети по умолчанию недоступен, пожалуйста, проверьте конфигурацию сети или используйте пользовательский адрес обнаружения сети.",
|
||||
"unreachable_oauth2_host": "Не удается подключиться к адресу аутентификации OAuth2, проверьте настройки сети.",
|
||||
"unreachable_http_host": "Не удается подключиться к адресу HTTP API Xiaomi, проверьте настройки сети.",
|
||||
"unreachable_spec_host": "Не удается подключиться к адресу SPEC API Xiaomi, проверьте настройки сети.",
|
||||
"unreachable_mqtt_broker": "Не удается подключиться к адресу MQTT брокера Xiaomi, проверьте настройки сети."
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "Ошибка конфигурации. Сбой сетевого подключения. Проверьте настройки сети устройства.",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "基础配置",
|
||||
"description": "### 登录地区\r\n选择小米账号所在的地区。您可以在 `米家APP > 我的(位于底部菜单) > 更多设置 > 关于米家` 中查看。\r\n### 语言\r\n选择设备及实体名称所用的语言。缺少翻译的部分语句将使用英文显示。\r\n### OAuth2 认证跳转地址\r\nOAuth2 认证跳转地址为 **[http://homeassistant.local:8123](http://homeassistant.local:8123)**,Home Assistant 需要与当前操作终端(例如,个人电脑)在同一局域网内,且操作终端能通过该地址访问 Home Assistant 首页,否则登录认证可能会失败。\r\n### 注意事项\r\n- 对于数百个及以上米家设备的用户,首次添加集成会耗费一些时间,请耐心等待。\r\n- 如果 Home Assistant 运行在docker环境下,请确保docker网络模式为host,否则会导致本地控制功能异常。\r\n- 集成本地控制功能存在一些依赖项,请仔细阅读README。",
|
||||
"description": "### 登录地区\r\n选择小米账号所在的地区。您可以在 `米家APP > 我的(位于底部菜单) > 更多设置 > 关于米家` 中查看。\r\n### 语言\r\n选择设备及实体名称所用的语言。缺少翻译的部分语句将使用英文显示。\r\n### OAuth2 认证跳转地址\r\nOAuth2 认证跳转地址为 **[http://homeassistant.local:8123](http://homeassistant.local:8123)**,Home Assistant 需要与当前操作终端(例如,个人电脑)在同一局域网内,且操作终端能通过该地址访问 Home Assistant 首页,否则登录认证可能会失败。\r\n### 集成网络配置\r\n检测本地网络是否正常,相关网络资源是否可访问。**首次添加时建议勾选。**\r\n### 注意事项\r\n- 对于数百个及以上米家设备的用户,首次添加集成会耗费一些时间,请耐心等待。\r\n- 如果 Home Assistant 运行在docker环境下,请确保docker网络模式为host,否则会导致本地控制功能异常。\r\n- 集成本地控制功能存在一些依赖项,请仔细阅读README。",
|
||||
"data": {
|
||||
"cloud_server": "登录地区",
|
||||
"integration_language": "语言",
|
||||
"oauth_redirect_url": "认证跳转地址"
|
||||
"oauth_redirect_url": "认证跳转地址",
|
||||
"network_detect_config": "集成网络配置"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "网络检测配置",
|
||||
"description": "## 使用介绍\r\n### 网络检测地址\r\n用于检测网络是否正常,未设置时将使用系统默认地址检测。如果默认地址检测异常时,可尝试输入可用的自定义地址检测。\r\n- 可输入多个检测地址,地址之间使用`,`号间隔,如`8.8.8.8,https://www.bing.com`\r\n- 如果为IP地址,将采用ping方式检测,如果为http(s)地址,将采用 HTTP GET 访问该地址检测。\r\n- 如果想恢复系统默认检测地址,请输入`,`号,然后点击'下一步'。\r\n- **该配置为全局配置,修改会影响其它集成实例的网络检测,请谨慎修改。**\r\n### 检测网络依赖项\r\n依次检查下述网络依赖项是否可访问。如果相关地址无法访问,将会导致集成异常。\r\n- OAuth2 认证地址:`https://account.xiaomi.com/oauth2/authorize`。\r\n- 小米 HTTP API 地址:`https://{http_host}/app/v2/ha/oauth/get_token`。\r\n- 小米 SPEC API 地址:`https://miot-spec.org/miot-spec-v2/template/list/device`。\r\n- 小米 MQTT Broker 地址:`mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`。",
|
||||
"data": {
|
||||
"network_detect_addr": "网络检测地址",
|
||||
"check_network_deps": "检测网络依赖项"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "高级设置选项",
|
||||
"description": "## 使用介绍\r\n### 除非您非常清楚下列选项的含义,否则请保持默认。\r\n### 筛选设备\r\n支持按照家庭房间名称、设备接入类型、设备型号筛选设备,同时也支持设备维度筛选。\r\n### 控制模式\r\n- 自动:本地局域网内存在可用的小米中枢网关时, Home Assistant 会优先通过中枢网关发送设备控制指令,以实现本地化控制功能。本地局域网不存在中枢时,会尝试通过小米OT协议发送控制指令,以实现本地化控制功能。只有当上述本地化控制条件不满足时,设备控制指令才会通过云端发送。\r\n- 云端:控制指令仅通过云端发送。\r\n### Action 调试模式\r\n对于设备 MIoT-Spec-V2 定义的方法,在生成通知实体之外,还会生成一个文本输入框实体,您可以在调试时用它向设备发送控制指令。\r\n### 隐藏非标准生成实体\r\n隐藏名称以“*”开头的非标准 MIoT-Spec-V2 实例生成的实体。\r\n### 显示设备状态变化通知\r\n细化显示设备状态变化通知,只显示勾选的通知消息。",
|
||||
"description": "## 使用介绍\r\n### 除非您非常清楚下列选项的含义,否则请保持默认。\r\n### 筛选设备\r\n支持按照家庭房间名称、设备接入类型、设备型号筛选设备,同时也支持设备维度筛选。\r\n### 控制模式\r\n- 自动:本地局域网内存在可用的小米中枢网关时, Home Assistant 会优先通过中枢网关发送设备控制指令,以实现本地化控制功能。本地局域网不存在中枢时,会尝试通过小米OT协议发送控制指令,以实现本地化控制功能。只有当上述本地化控制条件不满足时,设备控制指令才会通过云端发送。\r\n- 云端:控制指令仅通过云端发送。\r\n### Action 调试模式\r\n对于设备 MIoT-Spec-V2 定义的方法,在生成通知实体之外,还会生成一个文本输入框实体,您可以在调试时用它向设备发送控制指令。\r\n### 隐藏非标准生成实体\r\n隐藏名称以“*”开头的非标准 MIoT-Spec-V2 实例生成的实体。\r\n### 二进制传感器显示模式\r\n将米家中的二进制传感器显示为文本传感器实体或者二进制传感器实体。\r\n### 显示设备状态变化通知\r\n细化显示设备状态变化通知,只显示勾选的通知消息。",
|
||||
"data": {
|
||||
"devices_filter": "筛选设备",
|
||||
"ctrl_mode": "控制模式",
|
||||
"action_debug": "Action 调试模式",
|
||||
"hide_non_standard_entities": "隐藏非标准生成实体",
|
||||
"display_binary_mode": "二进制传感器显示模式",
|
||||
"display_devices_changed_notify": "显示设备状态变化通知"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "本地设备发现服务异常。",
|
||||
"get_cert_error": "获取中枢证书失败。",
|
||||
"no_family_selected": "未选择家庭。",
|
||||
"no_devices": "选择的家庭中没有设备。请选择有设备的家庭,而后继续。",
|
||||
"no_central_device": "【中枢网关模式】需要 Home Assistant 所在的局域网中存在可用的小米中枢网关。请检查选择的家庭是否符合该要求。"
|
||||
"no_devices": "选择的家庭中没有设备。请选择有设备的家庭,然后继续。",
|
||||
"no_filter_devices": "筛选设备为空。请选择有效的筛选条件,然后继续。",
|
||||
"no_central_device": "【中枢网关模式】需要 Home Assistant 所在的局域网中存在可用的小米中枢网关。请检查选择的家庭是否符合该要求。",
|
||||
"invalid_network_addr": "存在无效的IP地址或者HTTP地址,请输入有效的地址。",
|
||||
"invalid_ip_addr": "存在无法访问的IP地址,请输入有效的IP地址。",
|
||||
"invalid_http_addr": "存在无法访问的HTTP地址,请输入有效的HTTP地址。",
|
||||
"invalid_default_addr": "默认网络检测地址无法访问,请检查网络配置或者使用自定义网络检测地址。",
|
||||
"unreachable_oauth2_host": "无法访问 OAuth2 认证地址,请检查网络配置。",
|
||||
"unreachable_http_host": "无法访问小米 HTTP API 地址,请检查网络配置。",
|
||||
"unreachable_spec_host": "无法访问小米 SPEC API 地址,请检查网络配置。",
|
||||
"unreachable_mqtt_broker": "无法访问小米 MQTT Broker 地址,请检查网络配置。"
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "获取 Home Assistant UUID 失败。",
|
||||
"network_connect_error": "配置失败。网络连接异常,请检查设备网络配置。",
|
||||
"already_configured": "该用户已配置完成。请进入集成页面,点击“配置”按钮修改配置。",
|
||||
"invalid_auth_info": "认证信息已过期。请进入集成页面,点击“配置”按钮重新认证。",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "配置选项",
|
||||
"description": "### {nick_name} 您好!\r\n\r\n小米账号ID:{uid}\r\n当前登录区域:{cloud_server}\r\n\r\n请勾选需要重新配置的选项,然后点击“下一步”。",
|
||||
"description": "### {nick_name} 您好!\r\n\r\n小米账号ID:{uid}\r\n当前登录区域:{cloud_server}\r\n集成实例ID:{instance_id}\r\n\r\n请勾选需要重新配置的选项,然后点击“下一步”。",
|
||||
"data": {
|
||||
"integration_language": "集成语言",
|
||||
"update_user_info": "更新用户信息",
|
||||
"update_devices": "更新设备列表",
|
||||
"action_debug": "Action 调试模式",
|
||||
"hide_non_standard_entities": "隐藏非标准生成实体",
|
||||
"display_binary_mode": "二进制传感器显示模式",
|
||||
"display_devices_changed_notify": "显示设备状态变化通知",
|
||||
"update_trans_rules": "更新实体转换规则",
|
||||
"update_lan_ctrl_config": "更新局域网控制配置"
|
||||
"update_lan_ctrl_config": "更新局域网控制配置",
|
||||
"network_detect_config": "集成网络配置"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "启用局域网订阅"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "网络检测配置",
|
||||
"description": "## 使用介绍\r\n### 网络检测地址\r\n用于检测网络是否正常,未设置时将使用系统默认地址检测。如果默认地址检测异常时,可尝试输入可用的自定义地址检测。\r\n- 可输入多个检测地址,地址之间使用`,`号间隔,如`8.8.8.8,https://www.bing.com`\r\n- 如果为IP地址,将采用ping方式检测,如果为http(s)地址,将采用 HTTP GET 访问该地址检测。\r\n- 如果想恢复系统默认检测地址,请输入`,`号,然后点击'下一步'。\r\n- **该配置为全局配置,修改会影响其它集成实例的网络检测,请谨慎修改。**\r\n### 检测网络依赖项\r\n依次检查下述网络依赖项是否可访问。如果相关地址无法访问,将会导致集成异常。\r\n- OAuth2 认证地址:`https://account.xiaomi.com/oauth2/authorize`。\r\n- 小米 HTTP API 地址:`https://{http_host}/app/v2/ha/oauth/get_token`。\r\n- 小米 SPEC API 地址:`https://miot-spec.org/miot-spec-v2/template/list/device`。\r\n- 小米 MQTT Broker 地址:`mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`。",
|
||||
"data": {
|
||||
"network_detect_addr": "网络检测地址",
|
||||
"check_network_deps": "检测网络依赖项"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "确认配置",
|
||||
"description": "**{nick_name}** 您好!请确认最新的配置信息,然后点击“提交”。\r\n集成将会使用更新后的配置重新载入。\r\n\r\n集成语言:\t{lang_new}\r\n用户昵称:\t{nick_name_new}\r\nAction 调试模式:\t{action_debug}\r\n隐藏非标准生成实体:\t{hide_non_standard_entities}\r\n显示设备状态变化通知:\t{display_devices_changed_notify}\r\n设备变化:\t新增 **{devices_add}** 个设备,移除 **{devices_remove}** 个设备\r\n转换规则变化:\t共条 **{trans_rules_count}** 规则,更新 **{trans_rules_count_success}** 条规则",
|
||||
@ -168,11 +198,20 @@
|
||||
"get_homeinfo_error": "获取家庭信息失败。",
|
||||
"get_cert_error": "获取中枢证书失败。",
|
||||
"no_family_selected": "未选择家庭。",
|
||||
"no_devices": "选择的家庭中没有设备,请选择有设备的家庭,而后继续。",
|
||||
"no_devices": "选择的家庭中没有设备,请选择有设备的家庭,然后继续。",
|
||||
"no_filter_devices": "筛选设备为空。请选择有效的筛选条件,然后继续。",
|
||||
"no_central_device": "【中枢网关模式】需要 Home Assistant 所在的局域网中存在可用的小米中枢网关。请检查选择的家庭是否符合该要求。",
|
||||
"mdns_discovery_error": "本地设备发现服务异常。",
|
||||
"update_config_error": "配置信息更新失败。",
|
||||
"not_confirm": "未确认修改项。请勾选确认后再提交。"
|
||||
"not_confirm": "未确认修改项。请勾选确认后再提交。",
|
||||
"invalid_network_addr": "存在无效的IP地址或者HTTP地址,请输入有效的地址。",
|
||||
"invalid_ip_addr": "存在无法访问的IP地址,请输入有效的IP地址。",
|
||||
"invalid_http_addr": "存在无法访问的HTTP地址,请输入有效的HTTP地址。",
|
||||
"invalid_default_addr": "默认网络检测地址无法访问,请检查网络配置或者使用自定义网络检测地址。",
|
||||
"unreachable_oauth2_host": "无法访问 OAuth2 认证地址,请检查网络配置。",
|
||||
"unreachable_http_host": "无法访问小米 HTTP API 地址,请检查网络配置。",
|
||||
"unreachable_spec_host": "无法访问小米 SPEC API 地址,请检查网络配置。",
|
||||
"unreachable_mqtt_broker": "无法访问小米 MQTT Broker 地址,请检查网络配置。"
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "配置失败。网络连接异常,请检查设备网络配置。",
|
||||
|
@ -11,11 +11,20 @@
|
||||
},
|
||||
"auth_config": {
|
||||
"title": "基礎配置",
|
||||
"description": "### 登錄地區\r\n選擇小米帳號所在的地區。您可以在 `米家APP > 我的(位於底部菜單) > 更多設置 > 關於米家` 中查看。\r\n### 語言\r\n選擇設備及實體名稱所用的語言。缺少翻譯的部分語句將使用英文顯示。\r\n### OAuth2 認證跳轉地址\r\nOAuth2 認證跳轉地址為 **[http://homeassistant.local:8123](http://homeassistant.local:8123)**,Home Assistant 需要與當前操作終端(例如,個人電腦)在同一局域網內,且操作終端能通過該地址訪問 Home Assistant 首頁,否則登錄認證可能會失敗。\r\n### 注意事項\r\n- 對於數百個及以上米家設備的用戶,首次添加集成會耗費一些時間,請耐心等待。\r\n- 如果 Home Assistant 運行在docker環境下,請確保docker網絡模式為host,否則會導致本地控制功能異常。\r\n- 集成本地控制功能存在一些依賴項,請仔細閱讀README。",
|
||||
"description": "### 登錄地區\r\n選擇小米帳號所在的地區。您可以在 `米家APP > 我的(位於底部菜單) > 更多設置 > 關於米家` 中查看。\r\n### 語言\r\n選擇設備及實體名稱所用的語言。缺少翻譯的部分語句將使用英文顯示。\r\n### OAuth2 認證跳轉地址\r\nOAuth2 認證跳轉地址為 **[http://homeassistant.local:8123](http://homeassistant.local:8123)**,Home Assistant 需要與當前操作終端(例如,個人電腦)在同一局域網內,且操作終端能通過該地址訪問 Home Assistant 首頁,否則登錄認證可能會失敗。\r\n### 集成網絡配置\r\n檢測本地網絡是否正常,相關網絡資源是否可訪問。**首次添加時建議勾選。**\r\n### 注意事項\r\n- 對於數百個及以上米家設備的用戶,首次添加集成會耗費一些時間,請耐心等待。\r\n- 如果 Home Assistant 運行在docker環境下,請確保docker網絡模式為host,否則會導致本地控制功能異常。\r\n- 集成本地控制功能存在一些依賴項,請仔細閱讀README。",
|
||||
"data": {
|
||||
"cloud_server": "登錄地區",
|
||||
"integration_language": "語言",
|
||||
"oauth_redirect_url": "認證跳轉地址"
|
||||
"oauth_redirect_url": "認證跳轉地址",
|
||||
"network_detect_config": "集成網絡配置"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "集成網絡配置",
|
||||
"description": "## 使用介紹\r\n### 網絡檢測地址\r\n用於檢測網絡是否正常,未設置時將使用系統默認地址檢測。如果默認地址檢測異常時,可嘗試輸入可用的自定義地址檢測。\r\n- 可輸入多個檢測地址,地址之間使用`,`號間隔,如`8.8.8.8,https://www.bing.com`\r\n- 如果為IP地址,將採用ping方式檢測,如果為http(s)地址,將採用 HTTP GET 訪問該地址檢測。\r\n- 如果想恢復系統默認檢測地址,請輸入`,`號,然後點擊'下一步'。\r\n- **該配置為全局配置,修改會影響其它集成實例的網絡檢測,請謹慎修改。**\r\n### 檢測網絡依賴項\r\n依次檢查下述網絡依賴項是否可訪問。如果相關地址無法訪問,將會導致集成異常。\r\n- OAuth2 認證地址:`https://account.xiaomi.com/oauth2/authorize`。\r\n- 小米 HTTP API 地址:`https://{http_host}/app/v2/ha/oauth/get_token`。\r\n- 小米 SPEC API 地址:`https://miot-spec.org/miot-spec-v2/template/list/device`。\r\n- 小米 MQTT Broker 地址:`mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`。",
|
||||
"data": {
|
||||
"network_detect_addr": "網絡檢測地址",
|
||||
"check_network_deps": "檢測網絡依賴項"
|
||||
}
|
||||
},
|
||||
"oauth_error": {
|
||||
@ -33,12 +42,13 @@
|
||||
},
|
||||
"advanced_options": {
|
||||
"title": "高級設置選項",
|
||||
"description": "## 使用介紹\r\n### 除非您非常清楚下列選項的含義,否則請保持默認。\r\n### 篩選設備\r\n支持按照房間名稱和設備類型篩選設備,同時也支持設備維度篩選。\r\n### 控制模式\r\n- 自動:本地局域網內存在可用的小米中樞網關時, Home Assistant 會優先通過中樞網關發送設備控制指令,以實現本地化控制功能。本地局域網不存在中樞時,會嘗試通過小米OT協議發送控制指令,以實現本地化控制功能。只有當上述本地化控制條件不滿足時,設備控制指令才會通過雲端發送。\r\n- 雲端:控制指令僅通過雲端發送。\r\n### Action 調試模式\r\n對於設備 MIoT-Spec-V2 定義的方法,在生成通知實體之外,還會生成一個文本輸入框實體,您可以在調試時用它向設備發送控制指令。\r\n### 隱藏非標準生成實體\r\n隱藏名稱以“*”開頭的非標準 MIoT-Spec-V2 實例生成的實體。\r\n### 顯示設備狀態變化通知\r\n細化顯示設備狀態變化通知,只顯示勾選的通知消息。",
|
||||
"description": "## 使用介紹\r\n### 除非您非常清楚下列選項的含義,否則請保持默認。\r\n### 篩選設備\r\n支持按照房間名稱和設備類型篩選設備,同時也支持設備維度篩選。\r\n### 控制模式\r\n- 自動:本地局域網內存在可用的小米中樞網關時, Home Assistant 會優先通過中樞網關發送設備控制指令,以實現本地化控制功能。本地局域網不存在中樞時,會嘗試通過小米OT協議發送控制指令,以實現本地化控制功能。只有當上述本地化控制條件不滿足時,設備控制指令才會通過雲端發送。\r\n- 雲端:控制指令僅通過雲端發送。\r\n### Action 調試模式\r\n對於設備 MIoT-Spec-V2 定義的方法,在生成通知實體之外,還會生成一個文本輸入框實體,您可以在調試時用它向設備發送控制指令。\r\n### 隱藏非標準生成實體\r\n隱藏名稱以“*”開頭的非標準 MIoT-Spec-V2 實例生成的實體。\r\n### 二進制傳感器顯示模式\r\n將米家中的二進制傳感器顯示為文本傳感器實體或者二進制傳感器實體。\r\n### 顯示設備狀態變化通知\r\n細化顯示設備狀態變化通知,只顯示勾選的通知消息。",
|
||||
"data": {
|
||||
"devices_filter": "篩選設備",
|
||||
"ctrl_mode": "控制模式",
|
||||
"action_debug": "Action 調試模式",
|
||||
"hide_non_standard_entities": "隱藏非標準生成實體",
|
||||
"display_binary_mode": "二進制傳感器顯示模式",
|
||||
"display_devices_changed_notify": "顯示設備狀態變化通知"
|
||||
}
|
||||
},
|
||||
@ -68,10 +78,20 @@
|
||||
"mdns_discovery_error": "本地設備發現服務異常。",
|
||||
"get_cert_error": "獲取中樞證書失敗。",
|
||||
"no_family_selected": "未選擇家庭。",
|
||||
"no_devices": "選擇的家庭中沒有設備。請選擇有設備的家庭,而後繼續。",
|
||||
"no_central_device": "【中樞網關模式】需要 Home Assistant 所在的局域網中存在可用的小米中樞網關。請檢查選擇的家庭是否符合該要求。"
|
||||
"no_devices": "選擇的家庭中沒有設備。請選擇有設備的家庭,然後繼續。",
|
||||
"no_filter_devices": "篩選設備為空。請選擇有效的篩選條件,然後繼續。",
|
||||
"no_central_device": "【中樞網關模式】需要 Home Assistant 所在的局域網中存在可用的小米中樞網關。請檢查選擇的家庭是否符合該要求。",
|
||||
"invalid_network_addr": "存在無效的IP地址或者HTTP地址,請輸入有效的地址。",
|
||||
"invalid_ip_addr": "存在無法訪問的IP地址,請輸入有效的IP地址。",
|
||||
"invalid_http_addr": "存在無法訪問的HTTP地址,請輸入有效的HTTP地址。",
|
||||
"invalid_default_addr": "默認網絡檢測地址無法訪問,請檢查網絡配置或者使用自定義網絡檢測地址。",
|
||||
"unreachable_oauth2_host": "無法訪問 OAuth2 認證地址,請檢查網絡配置。",
|
||||
"unreachable_http_host": "無法訪問小米 HTTP API 地址,請檢查網絡配置。",
|
||||
"unreachable_spec_host": "無法訪問小米 SPEC API 地址,請檢查網絡配置。",
|
||||
"unreachable_mqtt_broker": "無法訪問小米 MQTT Broker 地址,請檢查網絡配置。"
|
||||
},
|
||||
"abort": {
|
||||
"ha_uuid_get_failed": "獲取 Home Assistant UUID 失敗。",
|
||||
"network_connect_error": "配置失敗。網絡連接異常,請檢查設備網絡配置。",
|
||||
"already_configured": "該用戶已配置完成。請進入集成頁面,點擊“配置”按鈕修改配置。",
|
||||
"invalid_auth_info": "認證信息已過期。請進入集成頁面,點擊“配置”按鈕重新認證。",
|
||||
@ -93,16 +113,18 @@
|
||||
},
|
||||
"config_options": {
|
||||
"title": "配置選項",
|
||||
"description": "### {nick_name} 您好!\r\n\r\n小米帳號ID:{uid}\r\n當前登錄區域:{cloud_server}\r\n\r\n請勾選需要重新配置的選項,然後點擊“下一步”。",
|
||||
"description": "### {nick_name} 您好!\r\n\r\n小米帳號ID:{uid}\r\n當前登錄區域:{cloud_server}\r\n集成實例ID:{instance_id}\r\n\r\n請勾選需要重新配置的選項,然後點擊“下一步”。",
|
||||
"data": {
|
||||
"integration_language": "集成語言",
|
||||
"update_user_info": "更新用戶信息",
|
||||
"update_devices": "更新設備列表",
|
||||
"action_debug": "Action 調試模式",
|
||||
"hide_non_standard_entities": "隱藏非標準生成實體",
|
||||
"display_binary_mode": "二進制傳感器顯示模式",
|
||||
"display_devices_changed_notify": "顯示設備狀態變化通知",
|
||||
"update_trans_rules": "更新實體轉換規則",
|
||||
"update_lan_ctrl_config": "更新局域網控制配置"
|
||||
"update_lan_ctrl_config": "更新局域網控制配置",
|
||||
"network_detect_config": "集成網絡配置"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -151,6 +173,14 @@
|
||||
"enable_subscribe": "啟用局域網訂閱"
|
||||
}
|
||||
},
|
||||
"network_detect_config": {
|
||||
"title": "集成網絡配置",
|
||||
"description": "## 使用介紹\r\n### 網絡檢測地址\r\n用於檢測網絡是否正常,未設置時將使用系統默認地址檢測。如果默認地址檢測異常時,可嘗試輸入可用的自定義地址檢測。\r\n- 可輸入多個檢測地址,地址之間使用`,`號間隔,如`8.8.8.8,https://www.bing.com`\r\n- 如果為IP地址,將採用ping方式檢測,如果為http(s)地址,將採用 HTTP GET 訪問該地址檢測。\r\n- 如果想恢復系統默認檢測地址,請輸入`,`號,然後點擊'下一步'。\r\n- **該配置為全局配置,修改會影響其它集成實例的網絡檢測,請謹慎修改。**\r\n### 檢測網絡依賴項\r\n依次檢查下述網絡依賴項是否可訪問。如果相關地址無法訪問,將會導致集成異常。\r\n- OAuth2 認證地址:`https://account.xiaomi.com/oauth2/authorize`。\r\n- 小米 HTTP API 地址:`https://{http_host}/app/v2/ha/oauth/get_token`。\r\n- 小米 SPEC API 地址:`https://miot-spec.org/miot-spec-v2/template/list/device`。\r\n- 小米 MQTT Broker 地址:`mqtts://{cloud_server}-ha.mqtt.io.mi.com:8883`。",
|
||||
"data": {
|
||||
"network_detect_addr": "網絡檢測地址",
|
||||
"check_network_deps": "檢測網絡依賴項"
|
||||
}
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "確認配置",
|
||||
"description": "**{nick_name}** 您好!請確認最新的配置信息,然後點擊“提交”。\r\n集成將會使用更新後的配置重新載入。\r\n\r\n集成語言:\t{lang_new}\r\n用戶暱稱:\t{nick_name_new}\r\nAction 調試模式:\t{action_debug}\r\n隱藏非標準生成實體:\t{hide_non_standard_entities}\r\n顯示設備狀態變化通知:\t{display_devices_changed_notify}\r\n設備變化:\t新增 **{devices_add}** 個設備,移除 **{devices_remove}** 個設備\r\n轉換規則變化:\t共條 **{trans_rules_count}** 規則,更新 **{trans_rules_count_success}** 條規則",
|
||||
@ -168,11 +198,20 @@
|
||||
"get_homeinfo_error": "獲取家庭信息失敗。",
|
||||
"get_cert_error": "獲取中樞證書失敗。",
|
||||
"no_family_selected": "未選擇家庭。",
|
||||
"no_devices": "選擇的家庭中沒有設備,請選擇有設備的家庭,而後繼續。",
|
||||
"no_devices": "選擇的家庭中沒有設備。請選擇有設備的家庭,然後繼續。",
|
||||
"no_filter_devices": "篩選設備為空。請選擇有效的篩選條件,然後繼續。",
|
||||
"no_central_device": "【中樞網關模式】需要 Home Assistant 所在的局域網中存在可用的小米中樞網關。請檢查選擇的家庭是否符合該要求。",
|
||||
"mdns_discovery_error": "本地設備發現服務異常。",
|
||||
"update_config_error": "配置信息更新失敗。",
|
||||
"not_confirm": "未確認修改項。請勾選確認後再提交。"
|
||||
"not_confirm": "未確認修改項。請勾選確認後再提交。",
|
||||
"invalid_network_addr": "存在無效的IP地址或者HTTP地址,請輸入有效的地址。",
|
||||
"invalid_ip_addr": "存在無法訪問的IP地址,請輸入有效的IP地址。",
|
||||
"invalid_http_addr": "存在無法訪問的HTTP地址,請輸入有效的HTTP地址。",
|
||||
"invalid_default_addr": "默認網絡檢測地址無法訪問,請檢查網絡配置或者使用自定義網絡檢測地址。",
|
||||
"unreachable_oauth2_host": "無法訪問 OAuth2 認證地址,請檢查網絡配置。",
|
||||
"unreachable_http_host": "無法訪問小米 HTTP API 地址,請檢查網絡配置。",
|
||||
"unreachable_spec_host": "無法訪問小米 SPEC API 地址,請檢查網絡配置。",
|
||||
"unreachable_mqtt_broker": "無法訪問小米 MQTT Broker 地址,請檢查網絡配置。"
|
||||
},
|
||||
"abort": {
|
||||
"network_connect_error": "配置失敗。網絡連接異常,請檢查設備網絡配置。",
|
||||
|
@ -120,28 +120,18 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
# properties
|
||||
for prop in entity_data.props:
|
||||
if prop.name == 'status':
|
||||
if (
|
||||
not isinstance(prop.value_list, list)
|
||||
or not prop.value_list
|
||||
):
|
||||
if not prop.value_list:
|
||||
_LOGGER.error(
|
||||
'invalid status value_list, %s', self.entity_id)
|
||||
continue
|
||||
self._status_map = {
|
||||
item['value']: item['description']
|
||||
for item in prop.value_list}
|
||||
self._status_map = prop.value_list.to_map()
|
||||
self._prop_status = prop
|
||||
elif prop.name == 'fan-level':
|
||||
if (
|
||||
not isinstance(prop.value_list, list)
|
||||
or not prop.value_list
|
||||
):
|
||||
if not prop.value_list:
|
||||
_LOGGER.error(
|
||||
'invalid fan-level value_list, %s', self.entity_id)
|
||||
continue
|
||||
self._fan_level_map = {
|
||||
item['value']: item['description']
|
||||
for item in prop.value_list}
|
||||
self._fan_level_map = prop.value_list.to_map()
|
||||
self._attr_fan_speed_list = list(self._fan_level_map.values())
|
||||
self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED
|
||||
self._prop_fan_level = prop
|
||||
@ -202,7 +192,7 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
@property
|
||||
def state(self) -> Optional[str]:
|
||||
"""Return the current state of the vacuum cleaner."""
|
||||
return self.get_map_description(
|
||||
return self.get_map_value(
|
||||
map_=self._status_map,
|
||||
key=self.get_prop_value(prop=self._prop_status))
|
||||
|
||||
@ -214,6 +204,6 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
@property
|
||||
def fan_speed(self) -> Optional[str]:
|
||||
"""Return the current fan speed of the vacuum cleaner."""
|
||||
return self.get_map_description(
|
||||
return self.get_map_value(
|
||||
map_=self._fan_level_map,
|
||||
key=self.get_prop_value(prop=self._prop_fan_level))
|
||||
|
@ -93,44 +93,49 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
||||
_prop_target_temp: Optional[MIoTSpecProperty]
|
||||
_prop_mode: Optional[MIoTSpecProperty]
|
||||
|
||||
_mode_list: Optional[dict[Any, Any]]
|
||||
_mode_map: Optional[dict[Any, Any]]
|
||||
|
||||
def __init__(
|
||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||
) -> None:
|
||||
"""Initialize the Water heater."""
|
||||
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||
self._attr_temperature_unit = None
|
||||
self._attr_temperature_unit = None # type: ignore
|
||||
self._attr_supported_features = WaterHeaterEntityFeature(0)
|
||||
self._prop_on = None
|
||||
self._prop_temp = None
|
||||
self._prop_target_temp = None
|
||||
self._prop_mode = None
|
||||
self._mode_list = None
|
||||
self._mode_map = None
|
||||
|
||||
# properties
|
||||
for prop in entity_data.props:
|
||||
# on
|
||||
if prop.name == 'on':
|
||||
self._attr_supported_features |= WaterHeaterEntityFeature.ON_OFF
|
||||
self._prop_on = prop
|
||||
# temperature
|
||||
if prop.name == 'temperature':
|
||||
if isinstance(prop.value_range, dict):
|
||||
if (
|
||||
self._attr_temperature_unit is None
|
||||
and prop.external_unit
|
||||
):
|
||||
self._attr_temperature_unit = prop.external_unit
|
||||
self._prop_temp = prop
|
||||
else:
|
||||
if not prop.value_range:
|
||||
_LOGGER.error(
|
||||
'invalid temperature value_range format, %s',
|
||||
self.entity_id)
|
||||
continue
|
||||
if prop.external_unit:
|
||||
self._attr_temperature_unit = prop.external_unit
|
||||
self._attr_min_temp = prop.value_range.min_
|
||||
self._attr_max_temp = prop.value_range.max_
|
||||
self._prop_temp = prop
|
||||
# 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_target_temperature_low = prop.value_range.min_
|
||||
self._attr_target_temperature_high = 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 |= (
|
||||
@ -138,17 +143,12 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
||||
self._prop_target_temp = prop
|
||||
# mode
|
||||
if prop.name == 'mode':
|
||||
if (
|
||||
not isinstance(prop.value_list, list)
|
||||
or not prop.value_list
|
||||
):
|
||||
if not prop.value_list:
|
||||
_LOGGER.error(
|
||||
'mode value_list is None, %s', self.entity_id)
|
||||
continue
|
||||
self._mode_list = {
|
||||
item['value']: item['description']
|
||||
for item in prop.value_list}
|
||||
self._attr_operation_list = list(self._mode_list.values())
|
||||
self._mode_map = prop.value_list.to_map()
|
||||
self._attr_operation_list = list(self._mode_map.values())
|
||||
self._attr_supported_features |= (
|
||||
WaterHeaterEntityFeature.OPERATION_MODE)
|
||||
self._prop_mode = prop
|
||||
@ -166,6 +166,8 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set the temperature the water heater should heat water to."""
|
||||
if not self._prop_target_temp:
|
||||
return
|
||||
await self.set_property_async(
|
||||
prop=self._prop_target_temp, value=kwargs[ATTR_TEMPERATURE])
|
||||
|
||||
@ -181,14 +183,11 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
||||
return
|
||||
if self.get_prop_value(prop=self._prop_on) is False:
|
||||
await self.set_property_async(
|
||||
prop=self._prop_on, value=True, update=False)
|
||||
prop=self._prop_on, value=True, write_ha_state=False)
|
||||
await self.set_property_async(
|
||||
prop=self._prop_mode,
|
||||
value=self.__get_mode_value(description=operation_mode))
|
||||
|
||||
async def async_turn_away_mode_on(self) -> None:
|
||||
"""Set the water heater to away mode."""
|
||||
await self.hass.async_add_executor_job(self.turn_away_mode_on)
|
||||
value=self.get_map_key(
|
||||
map_=self._mode_map, value=operation_mode))
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> Optional[float]:
|
||||
@ -198,6 +197,8 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
||||
@property
|
||||
def target_temperature(self) -> Optional[float]:
|
||||
"""Return the target temperature."""
|
||||
if not self._prop_target_temp:
|
||||
return None
|
||||
return self.get_prop_value(prop=self._prop_target_temp)
|
||||
|
||||
@property
|
||||
@ -207,20 +208,6 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
||||
return STATE_OFF
|
||||
if not self._prop_mode and self.get_prop_value(prop=self._prop_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))
|
||||
|
||||
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
|
||||
|
@ -98,6 +98,8 @@ footer :(可选)关联的 issue 或 pull request 编号。
|
||||
|
||||
在为本项目做出贡献时,您同意您的贡献遵循本项目的[许可证](../LICENSE.md) 。
|
||||
|
||||
当您第一次提交拉取请求时,GitHub Action 会提示您签署贡献者许可协议(Contributor License Agreement,CLA)。只有签署了 CLA ,本项目才会合入您的拉取请求。
|
||||
|
||||
## 获取帮助
|
||||
|
||||
如果您需要帮助或有疑问,可在 GitHub 的[讨论区](https://github.com/XiaoMi/ha_xiaomi_home/discussions/)询问。
|
||||
|
@ -26,6 +26,7 @@ cd ha_xiaomi_home
|
||||
|
||||
```bash
|
||||
cd config/ha_xiaomi_home
|
||||
git fetch
|
||||
git checkout v1.0.0
|
||||
./install.sh /config
|
||||
```
|
||||
@ -375,7 +376,7 @@ siid、piid、eiid、aiid、value 均为十进制三位整数。
|
||||
}
|
||||
```
|
||||
|
||||
> 在 Home Assistant 中修改了 `custom_components/xiaomi_home/translations/` 路径下的 `specv2entity.py`、`spec_filter.json`、`multi_lang.json` 文件的内容,需要在集成配置中更新实体转换规则才能生效。方法:[设置 > 设备与服务 > 已配置 > Xiaomi Home](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home) > 配置 > 更新实体转换规则
|
||||
> 在 Home Assistant 中修改了 `custom_components/xiaomi_home/miot/specs` 路径下的 `specv2entity.py`、`spec_filter.json`、`multi_lang.json` 文件的内容,需要在集成配置中更新实体转换规则才能生效。方法:[设置 > 设备与服务 > 已配置 > Xiaomi Home](https://my.home-assistant.io/redirect/integration/?domain=xiaomi_home) > 配置 > 更新实体转换规则
|
||||
|
||||
## 文档
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test rule format."""
|
||||
import json
|
||||
import logging
|
||||
from os import listdir, path
|
||||
from typing import Optional
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ROOT_PATH: str = path.dirname(path.abspath(__file__))
|
||||
TRANS_RELATIVE_PATH: str = path.join(
|
||||
ROOT_PATH, '../custom_components/xiaomi_home/translations')
|
||||
@ -13,13 +16,13 @@ MIOT_I18N_RELATIVE_PATH: str = path.join(
|
||||
ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n')
|
||||
SPEC_BOOL_TRANS_FILE = path.join(
|
||||
ROOT_PATH,
|
||||
'../custom_components/xiaomi_home/miot/specs/bool_trans.json')
|
||||
SPEC_MULTI_LANG_FILE = path.join(
|
||||
ROOT_PATH,
|
||||
'../custom_components/xiaomi_home/miot/specs/multi_lang.json')
|
||||
'../custom_components/xiaomi_home/miot/specs/bool_trans.yaml')
|
||||
SPEC_FILTER_FILE = path.join(
|
||||
ROOT_PATH,
|
||||
'../custom_components/xiaomi_home/miot/specs/spec_filter.json')
|
||||
'../custom_components/xiaomi_home/miot/specs/spec_filter.yaml')
|
||||
SPEC_MODIFY_FILE = path.join(
|
||||
ROOT_PATH,
|
||||
'../custom_components/xiaomi_home/miot/specs/spec_modify.yaml')
|
||||
|
||||
|
||||
def load_json_file(file_path: str) -> Optional[dict]:
|
||||
@ -27,10 +30,10 @@ def load_json_file(file_path: str) -> Optional[dict]:
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
return json.load(file)
|
||||
except FileNotFoundError:
|
||||
print(file_path, 'is not found.')
|
||||
_LOGGER.info('%s is not found.', file_path,)
|
||||
return None
|
||||
except json.JSONDecodeError:
|
||||
print(file_path, 'is not a valid JSON file.')
|
||||
_LOGGER.info('%s is not a valid JSON file.', file_path)
|
||||
return None
|
||||
|
||||
|
||||
@ -44,13 +47,20 @@ def load_yaml_file(file_path: str) -> Optional[dict]:
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
return yaml.safe_load(file)
|
||||
except FileNotFoundError:
|
||||
print(file_path, 'is not found.')
|
||||
_LOGGER.info('%s is not found.', file_path)
|
||||
return None
|
||||
except yaml.YAMLError:
|
||||
print(file_path, 'is not a valid YAML file.')
|
||||
_LOGGER.info('%s, is not a valid YAML file.', file_path)
|
||||
return None
|
||||
|
||||
|
||||
def save_yaml_file(file_path: str, data: dict) -> None:
|
||||
with open(file_path, 'w', encoding='utf-8') as file:
|
||||
yaml.safe_dump(
|
||||
data, file, default_flow_style=False,
|
||||
allow_unicode=True, indent=2, sort_keys=False)
|
||||
|
||||
|
||||
def dict_str_str(d: dict) -> bool:
|
||||
"""restricted format: dict[str, str]"""
|
||||
if not isinstance(d, dict):
|
||||
@ -116,93 +126,116 @@ def bool_trans(d: dict) -> bool:
|
||||
return False
|
||||
default_trans: dict = d['translate'].pop('default')
|
||||
if not default_trans:
|
||||
print('default trans is empty')
|
||||
_LOGGER.info('default trans is empty')
|
||||
return False
|
||||
default_keys: set[str] = set(default_trans.keys())
|
||||
for key, trans in d['translate'].items():
|
||||
trans_keys: set[str] = set(trans.keys())
|
||||
if set(trans.keys()) != default_keys:
|
||||
print('bool trans inconsistent', key, default_keys, trans_keys)
|
||||
_LOGGER.info(
|
||||
'bool trans inconsistent, %s, %s, %s',
|
||||
key, default_keys, trans_keys)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def spec_modify(data: dict) -> bool:
|
||||
"""dict[str, str | dict[str, dict]]"""
|
||||
if not isinstance(data, dict):
|
||||
return False
|
||||
for urn, content in data.items():
|
||||
if not isinstance(urn, str) or not isinstance(content, (dict, str)):
|
||||
return False
|
||||
if isinstance(content, str):
|
||||
continue
|
||||
for key, value in content.items():
|
||||
if not isinstance(key, str) or not isinstance(value, dict):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def compare_dict_structure(dict1: dict, dict2: dict) -> bool:
|
||||
if not isinstance(dict1, dict) or not isinstance(dict2, dict):
|
||||
print('invalid type')
|
||||
_LOGGER.info('invalid type')
|
||||
return False
|
||||
if dict1.keys() != dict2.keys():
|
||||
print('inconsistent key values, ', dict1.keys(), dict2.keys())
|
||||
_LOGGER.info(
|
||||
'inconsistent key values, %s, %s', dict1.keys(), dict2.keys())
|
||||
return False
|
||||
for key in dict1:
|
||||
if isinstance(dict1[key], dict) and isinstance(dict2[key], dict):
|
||||
if not compare_dict_structure(dict1[key], dict2[key]):
|
||||
print('inconsistent key values, dict, ', key)
|
||||
_LOGGER.info(
|
||||
'inconsistent key values, dict, %s', key)
|
||||
return False
|
||||
elif isinstance(dict1[key], list) and isinstance(dict2[key], list):
|
||||
if not all(
|
||||
isinstance(i, type(j))
|
||||
for i, j in zip(dict1[key], dict2[key])):
|
||||
print('inconsistent key values, list, ', key)
|
||||
_LOGGER.info(
|
||||
'inconsistent key values, list, %s', key)
|
||||
return False
|
||||
elif not isinstance(dict1[key], type(dict2[key])):
|
||||
print('inconsistent key values, type, ', key)
|
||||
_LOGGER.info(
|
||||
'inconsistent key values, type, %s', key)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def sort_bool_trans(file_path: str):
|
||||
trans_data: dict = load_json_file(file_path=file_path)
|
||||
trans_data = load_yaml_file(file_path=file_path)
|
||||
assert isinstance(trans_data, dict), f'{file_path} format error'
|
||||
trans_data['data'] = dict(sorted(trans_data['data'].items()))
|
||||
for key, trans in trans_data['translate'].items():
|
||||
trans_data['translate'][key] = dict(sorted(trans.items()))
|
||||
return trans_data
|
||||
|
||||
|
||||
def sort_multi_lang(file_path: str):
|
||||
multi_lang: dict = load_json_file(file_path=file_path)
|
||||
multi_lang = dict(sorted(multi_lang.items()))
|
||||
for urn, trans in multi_lang.items():
|
||||
multi_lang[urn] = dict(sorted(trans.items()))
|
||||
for lang, spec in multi_lang[urn].items():
|
||||
multi_lang[urn][lang] = dict(sorted(spec.items()))
|
||||
return multi_lang
|
||||
|
||||
|
||||
def sort_spec_filter(file_path: str):
|
||||
filter_data: dict = load_json_file(file_path=file_path)
|
||||
filter_data = load_yaml_file(file_path=file_path)
|
||||
assert isinstance(filter_data, dict), f'{file_path} format error'
|
||||
filter_data = dict(sorted(filter_data.items()))
|
||||
for urn, spec in filter_data.items():
|
||||
filter_data[urn] = dict(sorted(spec.items()))
|
||||
return filter_data
|
||||
|
||||
|
||||
def sort_spec_modify(file_path: str):
|
||||
filter_data = load_yaml_file(file_path=file_path)
|
||||
assert isinstance(filter_data, dict), f'{file_path} format error'
|
||||
return dict(sorted(filter_data.items()))
|
||||
|
||||
|
||||
@pytest.mark.github
|
||||
def test_bool_trans():
|
||||
data: dict = load_json_file(SPEC_BOOL_TRANS_FILE)
|
||||
data = load_yaml_file(SPEC_BOOL_TRANS_FILE)
|
||||
assert isinstance(data, dict)
|
||||
assert data, f'load {SPEC_BOOL_TRANS_FILE} failed'
|
||||
assert bool_trans(data), f'{SPEC_BOOL_TRANS_FILE} format error'
|
||||
|
||||
|
||||
@pytest.mark.github
|
||||
def test_spec_filter():
|
||||
data: dict = load_json_file(SPEC_FILTER_FILE)
|
||||
data = load_yaml_file(SPEC_FILTER_FILE)
|
||||
assert isinstance(data, dict)
|
||||
assert data, f'load {SPEC_FILTER_FILE} failed'
|
||||
assert spec_filter(data), f'{SPEC_FILTER_FILE} format error'
|
||||
|
||||
|
||||
@pytest.mark.github
|
||||
def test_multi_lang():
|
||||
data: dict = load_json_file(SPEC_MULTI_LANG_FILE)
|
||||
assert data, f'load {SPEC_MULTI_LANG_FILE} failed'
|
||||
assert nested_3_dict_str_str(data), f'{SPEC_MULTI_LANG_FILE} format error'
|
||||
def test_spec_modify():
|
||||
data = load_yaml_file(SPEC_MODIFY_FILE)
|
||||
assert isinstance(data, dict)
|
||||
assert data, f'load {SPEC_MODIFY_FILE} failed'
|
||||
assert spec_modify(data), f'{SPEC_MODIFY_FILE} format error'
|
||||
|
||||
|
||||
@pytest.mark.github
|
||||
def test_miot_i18n():
|
||||
for file_name in listdir(MIOT_I18N_RELATIVE_PATH):
|
||||
file_path: str = path.join(MIOT_I18N_RELATIVE_PATH, file_name)
|
||||
data: dict = load_json_file(file_path)
|
||||
data = load_json_file(file_path)
|
||||
assert isinstance(data, dict)
|
||||
assert data, f'load {file_path} failed'
|
||||
assert nested_3_dict_str_str(data), f'{file_path} format error'
|
||||
|
||||
@ -211,7 +244,8 @@ def test_miot_i18n():
|
||||
def test_translations():
|
||||
for file_name in listdir(TRANS_RELATIVE_PATH):
|
||||
file_path: str = path.join(TRANS_RELATIVE_PATH, file_name)
|
||||
data: dict = load_json_file(file_path)
|
||||
data = load_json_file(file_path)
|
||||
assert isinstance(data, dict)
|
||||
assert data, f'load {file_path} failed'
|
||||
assert dict_str_dict(data), f'{file_path} format error'
|
||||
|
||||
@ -228,27 +262,30 @@ def test_miot_lang_integrity():
|
||||
i18n_names: set[str] = set(listdir(MIOT_I18N_RELATIVE_PATH))
|
||||
assert len(i18n_names) == len(translations_names)
|
||||
assert i18n_names == translations_names
|
||||
bool_trans_data: set[str] = load_json_file(SPEC_BOOL_TRANS_FILE)
|
||||
bool_trans_data = load_yaml_file(SPEC_BOOL_TRANS_FILE)
|
||||
assert isinstance(bool_trans_data, dict)
|
||||
bool_trans_names: set[str] = set(
|
||||
bool_trans_data['translate']['default'].keys())
|
||||
assert len(bool_trans_names) == len(translations_names)
|
||||
# Check translation files structure
|
||||
default_dict: dict = load_json_file(
|
||||
default_dict = load_json_file(
|
||||
path.join(TRANS_RELATIVE_PATH, integration_lang_list[0]))
|
||||
for name in list(integration_lang_list)[1:]:
|
||||
compare_dict: dict = load_json_file(
|
||||
compare_dict = load_json_file(
|
||||
path.join(TRANS_RELATIVE_PATH, name))
|
||||
if not compare_dict_structure(default_dict, compare_dict):
|
||||
print('compare_dict_structure failed /translations, ', name)
|
||||
_LOGGER.info(
|
||||
'compare_dict_structure failed /translations, %s', name)
|
||||
assert False
|
||||
# Check i18n files structure
|
||||
default_dict = load_json_file(
|
||||
path.join(MIOT_I18N_RELATIVE_PATH, integration_lang_list[0]))
|
||||
for name in list(integration_lang_list)[1:]:
|
||||
compare_dict: dict = load_json_file(
|
||||
compare_dict = load_json_file(
|
||||
path.join(MIOT_I18N_RELATIVE_PATH, name))
|
||||
if not compare_dict_structure(default_dict, compare_dict):
|
||||
print('compare_dict_structure failed /miot/i18n, ', name)
|
||||
_LOGGER.info(
|
||||
'compare_dict_structure failed /miot/i18n, %s', name)
|
||||
assert False
|
||||
|
||||
|
||||
@ -261,19 +298,13 @@ def test_miot_data_sort():
|
||||
'INTEGRATION_LANGUAGES not sorted, correct order\r\n'
|
||||
f'{list(sort_langs.keys())}')
|
||||
assert json.dumps(
|
||||
load_json_file(file_path=SPEC_BOOL_TRANS_FILE)) == json.dumps(
|
||||
load_yaml_file(file_path=SPEC_BOOL_TRANS_FILE)) == json.dumps(
|
||||
sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)), (
|
||||
f'{SPEC_BOOL_TRANS_FILE} not sorted, goto project root path'
|
||||
' and run the following command sorting, ',
|
||||
'pytest -s -v -m update ./test/check_rule_format.py')
|
||||
assert json.dumps(
|
||||
load_json_file(file_path=SPEC_MULTI_LANG_FILE)) == json.dumps(
|
||||
sort_multi_lang(file_path=SPEC_MULTI_LANG_FILE)), (
|
||||
f'{SPEC_MULTI_LANG_FILE} not sorted, goto project root path'
|
||||
' and run the following command sorting, ',
|
||||
'pytest -s -v -m update ./test/check_rule_format.py')
|
||||
assert json.dumps(
|
||||
load_json_file(file_path=SPEC_FILTER_FILE)) == json.dumps(
|
||||
load_yaml_file(file_path=SPEC_FILTER_FILE)) == json.dumps(
|
||||
sort_spec_filter(file_path=SPEC_FILTER_FILE)), (
|
||||
f'{SPEC_FILTER_FILE} not sorted, goto project root path'
|
||||
' and run the following command sorting, ',
|
||||
@ -283,11 +314,11 @@ def test_miot_data_sort():
|
||||
@pytest.mark.update
|
||||
def test_sort_spec_data():
|
||||
sort_data: dict = sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)
|
||||
save_json_file(file_path=SPEC_BOOL_TRANS_FILE, data=sort_data)
|
||||
print(SPEC_BOOL_TRANS_FILE, 'formatted.')
|
||||
sort_data = sort_multi_lang(file_path=SPEC_MULTI_LANG_FILE)
|
||||
save_json_file(file_path=SPEC_MULTI_LANG_FILE, data=sort_data)
|
||||
print(SPEC_MULTI_LANG_FILE, 'formatted.')
|
||||
save_yaml_file(file_path=SPEC_BOOL_TRANS_FILE, data=sort_data)
|
||||
_LOGGER.info('%s formatted.', SPEC_BOOL_TRANS_FILE)
|
||||
sort_data = sort_spec_filter(file_path=SPEC_FILTER_FILE)
|
||||
save_json_file(file_path=SPEC_FILTER_FILE, data=sort_data)
|
||||
print(SPEC_FILTER_FILE, 'formatted.')
|
||||
save_yaml_file(file_path=SPEC_FILTER_FILE, data=sort_data)
|
||||
_LOGGER.info('%s formatted.', SPEC_FILTER_FILE)
|
||||
sort_data = sort_spec_modify(file_path=SPEC_MODIFY_FILE)
|
||||
save_yaml_file(file_path=SPEC_MODIFY_FILE, data=sort_data)
|
||||
_LOGGER.info('%s formatted.', SPEC_MODIFY_FILE)
|
||||
|
@ -1,16 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Pytest fixtures."""
|
||||
import logging
|
||||
import random
|
||||
import shutil
|
||||
import pytest
|
||||
from os import path, makedirs
|
||||
from uuid import uuid4
|
||||
|
||||
TEST_ROOT_PATH: str = path.dirname(path.abspath(__file__))
|
||||
TEST_FILES_PATH: str = path.join(TEST_ROOT_PATH, 'miot')
|
||||
TEST_CACHE_PATH: str = path.join(TEST_ROOT_PATH, 'test_cache')
|
||||
TEST_OAUTH2_REDIRECT_URL: str = 'http://homeassistant.local:8123'
|
||||
TEST_LANG: str = 'zh-Hans'
|
||||
TEST_UID: str = '123456789'
|
||||
TEST_CLOUD_SERVER: str = 'cn'
|
||||
|
||||
DOMAIN_CLOUD_CACHE: str = 'cloud_cache'
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def set_logger():
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
console_handler.setFormatter(formatter)
|
||||
logger.addHandler(console_handler)
|
||||
_LOGGER.info('set logger, %s', logger)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def load_py_file():
|
||||
@ -20,10 +41,10 @@ def load_py_file():
|
||||
'const.py',
|
||||
'miot_cloud.py',
|
||||
'miot_error.py',
|
||||
'miot_ev.py',
|
||||
'miot_i18n.py',
|
||||
'miot_lan.py',
|
||||
'miot_mdns.py',
|
||||
'miot_mips.py',
|
||||
'miot_network.py',
|
||||
'miot_spec.py',
|
||||
'miot_storage.py']
|
||||
@ -35,31 +56,35 @@ def load_py_file():
|
||||
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot',
|
||||
file_name),
|
||||
path.join(TEST_FILES_PATH, file_name))
|
||||
print('\nloaded test py files, ', file_list)
|
||||
_LOGGER.info('\nloaded test py files, %s', file_list)
|
||||
# Copy spec files to test folder
|
||||
shutil.copytree(
|
||||
src=path.join(
|
||||
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot/specs'),
|
||||
dst=path.join(TEST_FILES_PATH, 'specs'),
|
||||
dirs_exist_ok=True)
|
||||
print('loaded spec test folder, specs')
|
||||
_LOGGER.info('loaded spec test folder, specs')
|
||||
# Copy lan files to test folder
|
||||
shutil.copytree(
|
||||
src=path.join(
|
||||
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot/lan'),
|
||||
dst=path.join(TEST_FILES_PATH, 'lan'),
|
||||
dirs_exist_ok=True)
|
||||
print('loaded lan test folder, lan')
|
||||
_LOGGER.info('loaded lan test folder, lan')
|
||||
# Copy i18n files to test folder
|
||||
shutil.copytree(
|
||||
src=path.join(
|
||||
TEST_ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n'),
|
||||
dst=path.join(TEST_FILES_PATH, 'i18n'),
|
||||
dirs_exist_ok=True)
|
||||
print('loaded i18n test folder, i18n')
|
||||
_LOGGER.info('loaded i18n test folder, i18n')
|
||||
|
||||
yield
|
||||
|
||||
# NOTICE: All test files and data (tokens, device information, etc.) will
|
||||
# be deleted after the test is completed. For some test cases that
|
||||
# require caching data, you can comment out the following code.
|
||||
|
||||
if path.exists(TEST_FILES_PATH):
|
||||
shutil.rmtree(TEST_FILES_PATH)
|
||||
print('\nremoved test files, ', TEST_FILES_PATH)
|
||||
@ -80,6 +105,11 @@ def test_cache_path() -> str:
|
||||
return TEST_CACHE_PATH
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_oauth2_redirect_url() -> str:
|
||||
return TEST_OAUTH2_REDIRECT_URL
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_lang() -> str:
|
||||
return TEST_LANG
|
||||
@ -90,6 +120,53 @@ def test_uid() -> str:
|
||||
return TEST_UID
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_random_did() -> str:
|
||||
# Gen random did
|
||||
return str(random.getrandbits(64))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_uuid() -> str:
|
||||
# Gen uuid
|
||||
return uuid4().hex
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_cloud_server() -> str:
|
||||
return TEST_CLOUD_SERVER
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_domain_cloud_cache() -> str:
|
||||
return DOMAIN_CLOUD_CACHE
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_name_oauth2_info() -> str:
|
||||
return f'{TEST_CLOUD_SERVER}_oauth2_info'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_name_uid() -> str:
|
||||
return f'{TEST_CLOUD_SERVER}_uid'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_name_uuid() -> str:
|
||||
return f'{TEST_CLOUD_SERVER}_uuid'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_name_rd_did() -> str:
|
||||
return f'{TEST_CLOUD_SERVER}_rd_did'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_name_homes() -> str:
|
||||
return f'{TEST_CLOUD_SERVER}_homes'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def test_name_devices() -> str:
|
||||
return f'{TEST_CLOUD_SERVER}_devices'
|
||||
|
587
test/test_cloud.py
Executable file
587
test/test_cloud.py
Executable file
@ -0,0 +1,587 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_cloud.py."""
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
import webbrowser
|
||||
import pytest
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-argument
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_oauth_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_oauth2_redirect_url: str,
|
||||
test_uuid: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_uuid: str
|
||||
) -> dict:
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTOauthClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
local_uuid = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
|
||||
uuid = str(local_uuid or test_uuid)
|
||||
_LOGGER.info('uuid: %s', uuid)
|
||||
miot_oauth = MIoTOauthClient(
|
||||
client_id=OAUTH2_CLIENT_ID,
|
||||
redirect_url=test_oauth2_redirect_url,
|
||||
cloud_server=test_cloud_server,
|
||||
uuid=uuid)
|
||||
|
||||
oauth_info = None
|
||||
load_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
if (
|
||||
isinstance(load_info, dict)
|
||||
and 'access_token' in load_info
|
||||
and 'expires_ts' in load_info
|
||||
and load_info['expires_ts'] > int(time.time())
|
||||
):
|
||||
_LOGGER.info('load oauth info, %s', load_info)
|
||||
oauth_info = load_info
|
||||
if oauth_info is None:
|
||||
# gen oauth url
|
||||
auth_url: str = miot_oauth.gen_auth_url()
|
||||
assert isinstance(auth_url, str)
|
||||
_LOGGER.info('auth url: %s', auth_url)
|
||||
# get code
|
||||
webbrowser.open(auth_url)
|
||||
code: str = input('input code: ')
|
||||
assert code is not None
|
||||
# get access_token
|
||||
res_obj = await miot_oauth.get_access_token_async(code=code)
|
||||
assert res_obj is not None
|
||||
oauth_info = res_obj
|
||||
_LOGGER.info('get_access_token result: %s', res_obj)
|
||||
rc = await miot_storage.save_async(
|
||||
test_domain_cloud_cache, test_name_oauth2_info, oauth_info)
|
||||
assert rc
|
||||
_LOGGER.info('save oauth info')
|
||||
rc = await miot_storage.save_async(
|
||||
test_domain_cloud_cache, test_name_uuid, uuid)
|
||||
assert rc
|
||||
_LOGGER.info('save uuid')
|
||||
|
||||
access_token = oauth_info.get('access_token', None)
|
||||
assert isinstance(access_token, str)
|
||||
_LOGGER.info('access_token: %s', access_token)
|
||||
refresh_token = oauth_info.get('refresh_token', None)
|
||||
assert isinstance(refresh_token, str)
|
||||
_LOGGER.info('refresh_token: %s', refresh_token)
|
||||
|
||||
await miot_oauth.deinit_async()
|
||||
return oauth_info
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency(on=['test_miot_oauth_async'])
|
||||
async def test_miot_oauth_refresh_token(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_oauth2_redirect_url: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_uuid: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTOauthClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
uuid = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
|
||||
assert isinstance(uuid, str)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict)
|
||||
assert 'access_token' in oauth_info
|
||||
assert 'refresh_token' in oauth_info
|
||||
assert 'expires_ts' in oauth_info
|
||||
remaining_time = oauth_info['expires_ts'] - int(time.time())
|
||||
_LOGGER.info('token remaining valid time: %ss', remaining_time)
|
||||
# Refresh token
|
||||
miot_oauth = MIoTOauthClient(
|
||||
client_id=OAUTH2_CLIENT_ID,
|
||||
redirect_url=test_oauth2_redirect_url,
|
||||
cloud_server=test_cloud_server,
|
||||
uuid=uuid)
|
||||
refresh_token = oauth_info.get('refresh_token', None)
|
||||
assert refresh_token
|
||||
update_info = await miot_oauth.refresh_access_token_async(
|
||||
refresh_token=refresh_token)
|
||||
assert update_info
|
||||
assert 'access_token' in update_info
|
||||
assert 'refresh_token' in update_info
|
||||
assert 'expires_ts' in update_info
|
||||
remaining_time = update_info['expires_ts'] - int(time.time())
|
||||
assert remaining_time > 0
|
||||
_LOGGER.info('refresh token, remaining valid time: %ss', remaining_time)
|
||||
# Save oauth2 info
|
||||
rc = await miot_storage.save_async(
|
||||
test_domain_cloud_cache, test_name_oauth2_info, update_info)
|
||||
assert rc
|
||||
_LOGGER.info('refresh token success, %s', update_info)
|
||||
|
||||
await miot_oauth.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_get_nickname_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Get nickname
|
||||
user_info = await miot_http.get_user_info_async()
|
||||
assert isinstance(user_info, dict) and 'miliaoNick' in user_info
|
||||
nickname = user_info['miliaoNick']
|
||||
_LOGGER.info('your nickname: %s', nickname)
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_get_uid_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_uid: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
uid = await miot_http.get_uid_async()
|
||||
assert isinstance(uid, str)
|
||||
_LOGGER.info('your uid: %s', uid)
|
||||
# Save uid
|
||||
rc = await miot_storage.save_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uid, data=uid)
|
||||
assert rc
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_get_homeinfos_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_uid: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Get homeinfos
|
||||
homeinfos = await miot_http.get_homeinfos_async()
|
||||
assert isinstance(homeinfos, dict)
|
||||
assert 'uid' in homeinfos and isinstance(homeinfos['uid'], str)
|
||||
assert 'home_list' in homeinfos and isinstance(
|
||||
homeinfos['home_list'], dict)
|
||||
assert 'share_home_list' in homeinfos and isinstance(
|
||||
homeinfos['share_home_list'], dict)
|
||||
# Get uid
|
||||
uid = homeinfos.get('uid', '')
|
||||
# Compare uid with uid in storage
|
||||
uid2 = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
|
||||
assert uid == uid2
|
||||
_LOGGER.info('your uid: %s', uid)
|
||||
# Get homes
|
||||
home_list = homeinfos.get('home_list', {})
|
||||
_LOGGER.info('your home_list: ,%s', home_list)
|
||||
# Get share homes
|
||||
share_home_list = homeinfos.get('share_home_list', {})
|
||||
_LOGGER.info('your share_home_list: %s', share_home_list)
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_get_devices_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_uid: str,
|
||||
test_name_homes: str,
|
||||
test_name_devices: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Get devices
|
||||
devices = await miot_http.get_devices_async()
|
||||
assert isinstance(devices, dict)
|
||||
assert 'uid' in devices and isinstance(devices['uid'], str)
|
||||
assert 'homes' in devices and isinstance(devices['homes'], dict)
|
||||
assert 'devices' in devices and isinstance(devices['devices'], dict)
|
||||
# Compare uid with uid in storage
|
||||
uid = devices.get('uid', '')
|
||||
uid2 = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
|
||||
assert uid == uid2
|
||||
_LOGGER.info('your uid: %s', uid)
|
||||
# Get homes
|
||||
homes = devices['homes']
|
||||
_LOGGER.info('your homes: %s', homes)
|
||||
# Get devices
|
||||
devices = devices['devices']
|
||||
_LOGGER.info('your devices count: %s', len(devices))
|
||||
# Storage homes and devices
|
||||
rc = await miot_storage.save_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_homes, data=homes)
|
||||
assert rc
|
||||
rc = await miot_storage.save_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_devices, data=devices)
|
||||
assert rc
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_get_devices_with_dids_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_devices: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Load devices
|
||||
local_devices = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
|
||||
assert isinstance(local_devices, dict)
|
||||
did_list = list(local_devices.keys())
|
||||
assert len(did_list) > 0
|
||||
# Get device with dids
|
||||
test_list = did_list[:6]
|
||||
devices_info = await miot_http.get_devices_with_dids_async(
|
||||
dids=test_list)
|
||||
assert isinstance(devices_info, dict)
|
||||
_LOGGER.info('test did list, %s, %s', len(test_list), test_list)
|
||||
_LOGGER.info(
|
||||
'test result: %s, %s', len(devices_info), list(devices_info.keys()))
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_miot_cloud_get_cert(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_random_did: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_uid: str,
|
||||
test_name_rd_did: str
|
||||
):
|
||||
"""
|
||||
NOTICE: Currently, only certificate acquisition in the CN region is
|
||||
supported.
|
||||
"""
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTCert, MIoTStorage
|
||||
|
||||
if test_cloud_server.lower() != 'cn':
|
||||
_LOGGER.info('only support CN region')
|
||||
return
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
uid = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
|
||||
assert isinstance(uid, str)
|
||||
_LOGGER.info('your uid: %s', uid)
|
||||
random_did = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_rd_did, type_=str)
|
||||
if not random_did:
|
||||
random_did = test_random_did
|
||||
rc = await miot_storage.save_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_rd_did,
|
||||
data=random_did)
|
||||
assert rc
|
||||
assert isinstance(random_did, str)
|
||||
_LOGGER.info('your random_did: %s', random_did)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict)
|
||||
assert 'access_token' in oauth_info
|
||||
access_token = oauth_info['access_token']
|
||||
|
||||
# Get certificates
|
||||
miot_cert = MIoTCert(storage=miot_storage, uid=uid, cloud_server='CN')
|
||||
assert await miot_cert.verify_ca_cert_async(), 'invalid ca cert'
|
||||
remaining_time: int = await miot_cert.user_cert_remaining_time_async()
|
||||
if remaining_time > 0:
|
||||
_LOGGER.info(
|
||||
'user cert is valid, remaining time, %ss', remaining_time)
|
||||
_LOGGER.info((
|
||||
'if you want to obtain it again, please delete the '
|
||||
'key, csr, and cert files in %s.'), test_cache_path)
|
||||
return
|
||||
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server,
|
||||
client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=access_token)
|
||||
|
||||
user_key = miot_cert.gen_user_key()
|
||||
assert isinstance(user_key, str)
|
||||
_LOGGER.info('user_key str, %s', user_key)
|
||||
user_csr = miot_cert.gen_user_csr(user_key=user_key, did=random_did)
|
||||
assert isinstance(user_csr, str)
|
||||
_LOGGER.info('user_csr str, %s', user_csr)
|
||||
cert_str = await miot_http.get_central_cert_async(csr=user_csr)
|
||||
assert isinstance(cert_str, str)
|
||||
_LOGGER.info('user_cert str, %s', cert_str)
|
||||
rc = await miot_cert.update_user_key_async(key=user_key)
|
||||
assert rc
|
||||
rc = await miot_cert.update_user_cert_async(cert=cert_str)
|
||||
assert rc
|
||||
# verify user certificates
|
||||
remaining_time = await miot_cert.user_cert_remaining_time_async(
|
||||
cert_data=cert_str.encode('utf-8'), did=random_did)
|
||||
assert remaining_time > 0
|
||||
_LOGGER.info('user cert remaining time, %ss', remaining_time)
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_get_prop_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_devices: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Load devices
|
||||
local_devices = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
|
||||
assert isinstance(local_devices, dict)
|
||||
did_list = list(local_devices.keys())
|
||||
assert len(did_list) > 0
|
||||
# Get prop
|
||||
test_list = did_list[:6]
|
||||
for did in test_list:
|
||||
prop_value = await miot_http.get_prop_async(did=did, siid=2, piid=1)
|
||||
device_name = local_devices[did]['name']
|
||||
_LOGGER.info('%s(%s), prop.2.1: %s', device_name, did, prop_value)
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_get_props_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_devices: str
|
||||
):
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Load devices
|
||||
local_devices = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
|
||||
assert isinstance(local_devices, dict)
|
||||
did_list = list(local_devices.keys())
|
||||
assert len(did_list) > 0
|
||||
# Get props
|
||||
test_list = did_list[:6]
|
||||
prop_values = await miot_http.get_props_async(params=[
|
||||
{'did': did, 'siid': 2, 'piid': 1} for did in test_list])
|
||||
|
||||
_LOGGER.info('test did list, %s, %s', len(test_list), test_list)
|
||||
_LOGGER.info('test result, %s, %s', len(prop_values), prop_values)
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.skip(reason='skip danger operation')
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_set_prop_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_devices: str
|
||||
):
|
||||
"""
|
||||
WARNING: This test case will control the actual device and is not enabled
|
||||
by default. You can uncomment @pytest.mark.skip to enable it.
|
||||
"""
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Load devices
|
||||
local_devices = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
|
||||
assert isinstance(local_devices, dict)
|
||||
assert len(local_devices) > 0
|
||||
# Set prop
|
||||
# Find central hub gateway, control its indicator light switch
|
||||
# You can replace it with the device you want to control.
|
||||
test_did = ''
|
||||
for did, dev in local_devices.items():
|
||||
if dev['model'] == 'xiaomi.gateway.hub1':
|
||||
test_did = did
|
||||
break
|
||||
assert test_did != '', 'no central hub gateway found'
|
||||
result = await miot_http.set_prop_async(params=[{
|
||||
'did': test_did, 'siid': 3, 'piid': 1, 'value': False}])
|
||||
_LOGGER.info('test did, %s, prop.3.1=False -> %s', test_did, result)
|
||||
await asyncio.sleep(1)
|
||||
result = await miot_http.set_prop_async(params=[{
|
||||
'did': test_did, 'siid': 3, 'piid': 1, 'value': True}])
|
||||
_LOGGER.info('test did, %s, prop.3.1=True -> %s', test_did, result)
|
||||
|
||||
await miot_http.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.skip(reason='skip danger operation')
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.dependency()
|
||||
async def test_miot_cloud_action_async(
|
||||
test_cache_path: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_devices: str
|
||||
):
|
||||
"""
|
||||
WARNING: This test case will control the actual device and is not enabled
|
||||
by default. You can uncomment @pytest.mark.skip to enable it.
|
||||
"""
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
from miot.miot_storage import MIoTStorage
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=oauth_info['access_token'])
|
||||
|
||||
# Load devices
|
||||
local_devices = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
|
||||
assert isinstance(local_devices, dict)
|
||||
assert len(local_devices) > 0
|
||||
# Action
|
||||
# Find central hub gateway, trigger its virtual events
|
||||
# You can replace it with the device you want to control.
|
||||
test_did = ''
|
||||
for did, dev in local_devices.items():
|
||||
if dev['model'] == 'xiaomi.gateway.hub1':
|
||||
test_did = did
|
||||
break
|
||||
assert test_did != '', 'no central hub gateway found'
|
||||
result = await miot_http.action_async(
|
||||
did=test_did, siid=4, aiid=1,
|
||||
in_list=[{'piid': 1, 'value': 'hello world.'}])
|
||||
_LOGGER.info('test did, %s, action.4.1 -> %s', test_did, result)
|
||||
|
||||
await miot_http.deinit_async()
|
@ -18,7 +18,7 @@ def test_miot_matcher():
|
||||
if not matcher.get(topic=f'test/+/{l2}'):
|
||||
matcher[f'test/+/{l2}'] = f'test/+/{l2}'
|
||||
# Match
|
||||
match_result: list[(str, dict)] = list(matcher.iter_all_nodes())
|
||||
match_result: list[str] = list(matcher.iter_all_nodes())
|
||||
assert len(match_result) == 120
|
||||
match_result: list[str] = list(matcher.iter_match(topic='test/1/1'))
|
||||
assert len(match_result) == 3
|
||||
|
@ -1,55 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_ev.py."""
|
||||
import os
|
||||
import pytest
|
||||
|
||||
# pylint: disable=import-outside-toplevel, disable=unused-argument
|
||||
|
||||
|
||||
@pytest.mark.github
|
||||
def test_mev_timer_and_fd():
|
||||
from miot.miot_ev import MIoTEventLoop, TimeoutHandle
|
||||
|
||||
mev = MIoTEventLoop()
|
||||
assert mev
|
||||
event_fd: os.eventfd = os.eventfd(0, os.O_NONBLOCK)
|
||||
assert event_fd
|
||||
timer4: TimeoutHandle = None
|
||||
|
||||
def event_handler(event_fd):
|
||||
value: int = os.eventfd_read(event_fd)
|
||||
if value == 1:
|
||||
mev.clear_timeout(timer4)
|
||||
print('cancel timer4')
|
||||
elif value == 2:
|
||||
print('event write twice in a row')
|
||||
elif value == 3:
|
||||
mev.set_read_handler(event_fd, None, None)
|
||||
os.close(event_fd)
|
||||
event_fd = None
|
||||
print('close event fd')
|
||||
|
||||
def timer1_handler(event_fd):
|
||||
os.eventfd_write(event_fd, 1)
|
||||
|
||||
def timer2_handler(event_fd):
|
||||
os.eventfd_write(event_fd, 1)
|
||||
os.eventfd_write(event_fd, 1)
|
||||
|
||||
def timer3_handler(event_fd):
|
||||
os.eventfd_write(event_fd, 3)
|
||||
|
||||
def timer4_handler(event_fd):
|
||||
raise ValueError('unreachable code')
|
||||
|
||||
mev.set_read_handler(
|
||||
event_fd, event_handler, event_fd)
|
||||
|
||||
mev.set_timeout(500, timer1_handler, event_fd)
|
||||
mev.set_timeout(1000, timer2_handler, event_fd)
|
||||
mev.set_timeout(1500, timer3_handler, event_fd)
|
||||
timer4 = mev.set_timeout(2000, timer4_handler, event_fd)
|
||||
|
||||
mev.loop_forever()
|
||||
# Loop will exit when there are no timers or fd handlers.
|
||||
mev.loop_stop()
|
@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_lan.py."""
|
||||
import logging
|
||||
from typing import Any
|
||||
import pytest
|
||||
import asyncio
|
||||
from zeroconf import IPVersion
|
||||
from zeroconf.asyncio import AsyncZeroconf
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-argument
|
||||
|
||||
|
||||
@ -53,7 +56,7 @@ async def test_lan_async(test_devices: dict):
|
||||
|
||||
# Your central hub gateway did
|
||||
test_did = '111111'
|
||||
# Your central hub gateway did
|
||||
# Your central hub gateway token
|
||||
test_token = '11223344556677d9a03d43936fc384205'
|
||||
test_model = 'xiaomi.gateway.hub1'
|
||||
# Your computer interface list, such as enp3s0, wlp5s0
|
||||
@ -67,7 +70,7 @@ async def test_lan_async(test_devices: dict):
|
||||
|
||||
miot_network = MIoTNetwork()
|
||||
await miot_network.init_async()
|
||||
print('miot_network, ', miot_network.network_info)
|
||||
_LOGGER.info('miot_network, %s', miot_network.network_info)
|
||||
mips_service = MipsService(
|
||||
aiozc=AsyncZeroconf(ip_version=IPVersion.V4Only))
|
||||
await mips_service.init_async()
|
||||
@ -81,7 +84,7 @@ async def test_lan_async(test_devices: dict):
|
||||
await miot_lan.vote_for_lan_ctrl_async(key='test', vote=True)
|
||||
|
||||
async def device_state_change(did: str, state: dict, ctx: Any):
|
||||
print('device state change, ', did, state)
|
||||
_LOGGER.info('device state change, %s, %s', did, state)
|
||||
if did != test_did:
|
||||
return
|
||||
if (
|
||||
@ -91,10 +94,10 @@ async def test_lan_async(test_devices: dict):
|
||||
# Test sub prop
|
||||
miot_lan.sub_prop(
|
||||
did=did, siid=3, piid=1, handler=lambda msg, ctx:
|
||||
print(f'sub prop.3.1 msg, {did}={msg}'))
|
||||
_LOGGER.info('sub prop.3.1 msg, %s=%s', did, msg))
|
||||
miot_lan.sub_prop(
|
||||
did=did, handler=lambda msg, ctx:
|
||||
print(f'sub all device msg, {did}={msg}'))
|
||||
_LOGGER.info('sub all device msg, %s=%s', did, msg))
|
||||
evt_push_available.set()
|
||||
else:
|
||||
# miot_lan.unsub_prop(did=did, siid=3, piid=1)
|
||||
@ -102,7 +105,7 @@ async def test_lan_async(test_devices: dict):
|
||||
evt_push_unavailable.set()
|
||||
|
||||
async def lan_state_change(state: bool):
|
||||
print('lan state change, ', state)
|
||||
_LOGGER.info('lan state change, %s', state)
|
||||
if not state:
|
||||
return
|
||||
miot_lan.update_devices(devices={
|
||||
@ -149,3 +152,5 @@ async def test_lan_async(test_devices: dict):
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
await miot_lan.deinit_async()
|
||||
await mips_service.deinit_async()
|
||||
await miot_network.deinit_async()
|
||||
|
@ -1,28 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_mdns.py."""
|
||||
import asyncio
|
||||
import logging
|
||||
import pytest
|
||||
from zeroconf import IPVersion
|
||||
from zeroconf.asyncio import AsyncZeroconf
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-argument
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_service_loop_async():
|
||||
from miot.miot_mdns import MipsService, MipsServiceData, MipsServiceState
|
||||
from miot.miot_mdns import MipsService, MipsServiceState
|
||||
|
||||
async def on_service_state_change(
|
||||
group_id: str, state: MipsServiceState, data: MipsServiceData):
|
||||
print(
|
||||
group_id: str, state: MipsServiceState, data: dict):
|
||||
_LOGGER.info(
|
||||
'on_service_state_change, %s, %s, %s', group_id, state, data)
|
||||
|
||||
async with AsyncZeroconf(ip_version=IPVersion.V4Only) as aiozc:
|
||||
mips_service = MipsService(aiozc)
|
||||
mips_service.sub_service_change('test', '*', on_service_state_change)
|
||||
await mips_service.init_async()
|
||||
# Wait for service to discover
|
||||
await asyncio.sleep(3)
|
||||
services_detail = mips_service.get_services()
|
||||
print('get all service, ', services_detail.keys())
|
||||
_LOGGER.info('get all service, %s', list(services_detail.keys()))
|
||||
for name, data in services_detail.items():
|
||||
print(
|
||||
'\tinfo, ', name, data['did'], data['addresses'], data['port'])
|
||||
_LOGGER.info(
|
||||
'\tinfo, %s, %s, %s, %s',
|
||||
name, data['did'], data['addresses'], data['port'])
|
||||
await mips_service.deinit_async()
|
||||
|
264
test/test_mips.py
Normal file
264
test/test_mips.py
Normal file
@ -0,0 +1,264 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_mips.py.
|
||||
NOTICE: When running this test case, you need to run test_cloud.py first to
|
||||
obtain the token and certificate information, and at the same time avoid data
|
||||
deletion.
|
||||
"""
|
||||
import ipaddress
|
||||
from typing import Any, Tuple
|
||||
import pytest
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable = import-outside-toplevel, unused-argument
|
||||
|
||||
@pytest.mark.parametrize('central_info', [
|
||||
('<Group id>', 'Gateway did', 'Gateway ip', 8883),
|
||||
])
|
||||
@pytest.mark.asyncio
|
||||
async def test_mips_local_async(
|
||||
test_cache_path: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_uid: str,
|
||||
test_name_rd_did: str,
|
||||
central_info: Tuple[str, str, str, int]
|
||||
):
|
||||
"""
|
||||
NOTICE:
|
||||
- Mips local is used to connect to the central gateway and is only
|
||||
supported in the Chinese mainland region.
|
||||
- Before running this test case, you need to run test_mdns.py first to
|
||||
obtain the group_id, did, ip, and port of the hub, and then fill in this
|
||||
information in the parametrize. you can enter multiple central connection
|
||||
information items for separate tests.
|
||||
- This test case requires running test_cloud.py first to obtain the
|
||||
central connection certificate.
|
||||
- This test case will control the indicator light switch of the central
|
||||
gateway.
|
||||
"""
|
||||
from miot.miot_storage import MIoTStorage, MIoTCert
|
||||
from miot.miot_mips import MipsLocalClient
|
||||
|
||||
central_group_id: str = central_info[0]
|
||||
assert isinstance(central_group_id, str)
|
||||
central_did: str = central_info[1]
|
||||
assert central_did.isdigit()
|
||||
central_ip: str = central_info[2]
|
||||
assert ipaddress.ip_address(central_ip)
|
||||
central_port: int = central_info[3]
|
||||
assert isinstance(central_port, int)
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
uid = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
|
||||
assert isinstance(uid, str)
|
||||
random_did = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_rd_did, type_=str)
|
||||
assert isinstance(random_did, str)
|
||||
miot_cert = MIoTCert(storage=miot_storage, uid=uid, cloud_server='CN')
|
||||
assert miot_cert.ca_file
|
||||
assert miot_cert.cert_file
|
||||
assert miot_cert.key_file
|
||||
_LOGGER.info(
|
||||
'cert info, %s, %s, %s', miot_cert.ca_file, miot_cert.cert_file,
|
||||
miot_cert.key_file)
|
||||
|
||||
mips_local = MipsLocalClient(
|
||||
did=random_did,
|
||||
host=central_ip,
|
||||
group_id=central_group_id,
|
||||
ca_file=miot_cert.ca_file,
|
||||
cert_file=miot_cert.cert_file,
|
||||
key_file=miot_cert.key_file,
|
||||
port=central_port,
|
||||
home_name='mips local test')
|
||||
mips_local.enable_logger(logger=_LOGGER)
|
||||
mips_local.enable_mqtt_logger(logger=_LOGGER)
|
||||
|
||||
async def on_mips_state_changed_async(key: str, state: bool):
|
||||
_LOGGER.info('on mips state changed, %s, %s', key, state)
|
||||
|
||||
async def on_dev_list_changed_async(
|
||||
mips: MipsLocalClient, did_list: list[str]
|
||||
):
|
||||
_LOGGER.info('dev list changed, %s', did_list)
|
||||
|
||||
def on_prop_changed(payload: dict, ctx: Any):
|
||||
_LOGGER.info('prop changed, %s=%s', ctx, payload)
|
||||
|
||||
def on_event_occurred(payload: dict, ctx: Any):
|
||||
_LOGGER.info('event occurred, %s=%s', ctx, payload)
|
||||
|
||||
# Reg mips state
|
||||
mips_local.sub_mips_state(
|
||||
key='mips_local', handler=on_mips_state_changed_async)
|
||||
mips_local.on_dev_list_changed = on_dev_list_changed_async
|
||||
# Connect
|
||||
await mips_local.connect_async()
|
||||
await asyncio.sleep(0.5)
|
||||
# Get device list
|
||||
device_list = await mips_local.get_dev_list_async()
|
||||
assert isinstance(device_list, dict)
|
||||
_LOGGER.info(
|
||||
'get_dev_list, %d, %s', len(device_list), list(device_list.keys()))
|
||||
# Sub Prop
|
||||
mips_local.sub_prop(
|
||||
did=central_did, handler=on_prop_changed,
|
||||
handler_ctx=f'{central_did}.*')
|
||||
# Sub Event
|
||||
mips_local.sub_event(
|
||||
did=central_did, handler=on_event_occurred,
|
||||
handler_ctx=f'{central_did}.*')
|
||||
# Get/set prop
|
||||
test_siid = 3
|
||||
test_piid = 1
|
||||
# mips_local.sub_prop(
|
||||
# did=central_did, siid=test_siid, piid=test_piid,
|
||||
# handler=on_prop_changed,
|
||||
# handler_ctx=f'{central_did}.{test_siid}.{test_piid}')
|
||||
result1 = await mips_local.get_prop_async(
|
||||
did=central_did, siid=test_siid, piid=test_piid)
|
||||
assert isinstance(result1, bool)
|
||||
_LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result1)
|
||||
result2 = await mips_local.set_prop_async(
|
||||
did=central_did, siid=test_siid, piid=test_piid, value=not result1)
|
||||
_LOGGER.info(
|
||||
'set prop.%s.%s=%s, result=%s',
|
||||
test_siid, test_piid, not result1, result2)
|
||||
assert isinstance(result2, dict)
|
||||
result3 = await mips_local.get_prop_async(
|
||||
did=central_did, siid=test_siid, piid=test_piid)
|
||||
assert isinstance(result3, bool)
|
||||
_LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result3)
|
||||
# Action
|
||||
test_siid = 4
|
||||
test_aiid = 1
|
||||
in_list = [{'piid': 1, 'value': 'hello world.'}]
|
||||
result4 = await mips_local.action_async(
|
||||
did=central_did, siid=test_siid, aiid=test_aiid,
|
||||
in_list=in_list)
|
||||
assert isinstance(result4, dict)
|
||||
_LOGGER.info(
|
||||
'action.%s.%s=%s, result=%s', test_siid, test_piid, in_list, result4)
|
||||
# Disconnect
|
||||
await mips_local.disconnect_async()
|
||||
await mips_local.deinit_async()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mips_cloud_async(
|
||||
test_cache_path: str,
|
||||
test_name_uuid: str,
|
||||
test_cloud_server: str,
|
||||
test_domain_cloud_cache: str,
|
||||
test_name_oauth2_info: str,
|
||||
test_name_devices: str
|
||||
):
|
||||
"""
|
||||
NOTICE:
|
||||
- This test case requires running test_cloud.py first to obtain the
|
||||
central connection certificate.
|
||||
- This test case will control the indicator light switch of the central
|
||||
gateway.
|
||||
"""
|
||||
from miot.const import OAUTH2_CLIENT_ID
|
||||
from miot.miot_storage import MIoTStorage
|
||||
from miot.miot_mips import MipsCloudClient
|
||||
from miot.miot_cloud import MIoTHttpClient
|
||||
|
||||
miot_storage = MIoTStorage(test_cache_path)
|
||||
uuid = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
|
||||
assert isinstance(uuid, str)
|
||||
oauth_info = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
|
||||
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
|
||||
access_token = oauth_info['access_token']
|
||||
_LOGGER.info('connect info, %s, %s', uuid, access_token)
|
||||
mips_cloud = MipsCloudClient(
|
||||
uuid=uuid,
|
||||
cloud_server=test_cloud_server,
|
||||
app_id=OAUTH2_CLIENT_ID,
|
||||
token=access_token)
|
||||
mips_cloud.enable_logger(logger=_LOGGER)
|
||||
mips_cloud.enable_mqtt_logger(logger=_LOGGER)
|
||||
miot_http = MIoTHttpClient(
|
||||
cloud_server=test_cloud_server,
|
||||
client_id=OAUTH2_CLIENT_ID,
|
||||
access_token=access_token)
|
||||
|
||||
async def on_mips_state_changed_async(key: str, state: bool):
|
||||
_LOGGER.info('on mips state changed, %s, %s', key, state)
|
||||
|
||||
def on_prop_changed(payload: dict, ctx: Any):
|
||||
_LOGGER.info('prop changed, %s=%s', ctx, payload)
|
||||
|
||||
def on_event_occurred(payload: dict, ctx: Any):
|
||||
_LOGGER.info('event occurred, %s=%s', ctx, payload)
|
||||
|
||||
await mips_cloud.connect_async()
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
# Sub mips state
|
||||
mips_cloud.sub_mips_state(
|
||||
key='mips_cloud', handler=on_mips_state_changed_async)
|
||||
# Load devices
|
||||
local_devices = await miot_storage.load_async(
|
||||
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
|
||||
assert isinstance(local_devices, dict)
|
||||
central_did = ''
|
||||
for did, info in local_devices.items():
|
||||
if info['model'] != 'xiaomi.gateway.hub1':
|
||||
continue
|
||||
central_did = did
|
||||
break
|
||||
if central_did:
|
||||
# Sub Prop
|
||||
mips_cloud.sub_prop(
|
||||
did=central_did, handler=on_prop_changed,
|
||||
handler_ctx=f'{central_did}.*')
|
||||
# Sub Event
|
||||
mips_cloud.sub_event(
|
||||
did=central_did, handler=on_event_occurred,
|
||||
handler_ctx=f'{central_did}.*')
|
||||
# Get/set prop
|
||||
test_siid = 3
|
||||
test_piid = 1
|
||||
# mips_cloud.sub_prop(
|
||||
# did=central_did, siid=test_siid, piid=test_piid,
|
||||
# handler=on_prop_changed,
|
||||
# handler_ctx=f'{central_did}.{test_siid}.{test_piid}')
|
||||
result1 = await miot_http.get_prop_async(
|
||||
did=central_did, siid=test_siid, piid=test_piid)
|
||||
assert isinstance(result1, bool)
|
||||
_LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result1)
|
||||
result2 = await miot_http.set_prop_async(params=[{
|
||||
'did': central_did, 'siid': test_siid, 'piid': test_piid,
|
||||
'value': not result1}])
|
||||
_LOGGER.info(
|
||||
'set prop.%s.%s=%s, result=%s',
|
||||
test_siid, test_piid, not result1, result2)
|
||||
assert isinstance(result2, list)
|
||||
result3 = await miot_http.get_prop_async(
|
||||
did=central_did, siid=test_siid, piid=test_piid)
|
||||
assert isinstance(result3, bool)
|
||||
_LOGGER.info('get prop.%s.%s, value=%s', test_siid, test_piid, result3)
|
||||
# Action
|
||||
test_siid = 4
|
||||
test_aiid = 1
|
||||
in_list = [{'piid': 1, 'value': 'hello world.'}]
|
||||
result4 = await miot_http.action_async(
|
||||
did=central_did, siid=test_siid, aiid=test_aiid,
|
||||
in_list=in_list)
|
||||
assert isinstance(result4, dict)
|
||||
_LOGGER.info(
|
||||
'action.%s.%s=%s, result=%s',
|
||||
test_siid, test_piid, in_list, result4)
|
||||
await asyncio.sleep(1)
|
||||
# Disconnect
|
||||
await mips_cloud.disconnect_async()
|
||||
await mips_cloud.deinit_async()
|
||||
await miot_http.deinit_async()
|
@ -1,8 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_network.py."""
|
||||
import logging
|
||||
import pytest
|
||||
import asyncio
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-argument
|
||||
|
||||
|
||||
@ -12,16 +15,16 @@ async def test_network_monitor_loop_async():
|
||||
miot_net = MIoTNetwork()
|
||||
|
||||
async def on_network_status_changed(status: bool):
|
||||
print(f'on_network_status_changed, {status}')
|
||||
_LOGGER.info('on_network_status_changed, %s', status)
|
||||
miot_net.sub_network_status(key='test', handler=on_network_status_changed)
|
||||
|
||||
async def on_network_info_changed(
|
||||
status: InterfaceStatus, info: NetworkInfo):
|
||||
print(f'on_network_info_changed, {status}, {info}')
|
||||
_LOGGER.info('on_network_info_changed, %s, %s', status, info)
|
||||
miot_net.sub_network_info(key='test', handler=on_network_info_changed)
|
||||
|
||||
await miot_net.init_async(3)
|
||||
await miot_net.init_async()
|
||||
await asyncio.sleep(3)
|
||||
print(f'net status: {miot_net.network_status}')
|
||||
print(f'net info: {miot_net.network_info}')
|
||||
_LOGGER.info('net status: %s', miot_net.network_status)
|
||||
_LOGGER.info('net info: %s', miot_net.network_info)
|
||||
await miot_net.deinit_async()
|
||||
|
@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_spec.py."""
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
from urllib.request import Request, urlopen
|
||||
import pytest
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-argument
|
||||
|
||||
|
||||
@ -79,10 +82,10 @@ async def test_spec_random_parse_async(test_cache_path, test_lang):
|
||||
storage = MIoTStorage(test_cache_path)
|
||||
spec_parser = MIoTSpecParser(lang=test_lang, storage=storage)
|
||||
await spec_parser.init_async()
|
||||
start_ts: int = time.time()*1000
|
||||
start_ts = time.time()*1000
|
||||
for index in test_urn_index:
|
||||
urn: str = test_urns[int(index)]
|
||||
result = await spec_parser.parse(urn=urn, skip_cache=True)
|
||||
assert result is not None
|
||||
end_ts: int = time.time()*1000
|
||||
print(f'takes time, {test_count}, {end_ts-start_ts}')
|
||||
end_ts = time.time()*1000
|
||||
_LOGGER.info('takes time, %s, %s', test_count, end_ts-start_ts)
|
||||
|
@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Unit test for miot_storage.py."""
|
||||
import asyncio
|
||||
import logging
|
||||
from os import path
|
||||
import pytest
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=import-outside-toplevel, unused-argument
|
||||
|
||||
|
||||
@ -101,7 +104,7 @@ async def test_multi_task_load_async(test_cache_path):
|
||||
for _ in range(task_count):
|
||||
task_list.append(asyncio.create_task(storage.load_async(
|
||||
domain=test_domain, name=name, type_=dict)))
|
||||
print(f'\ntask count, {len(task_list)}')
|
||||
_LOGGER.info('task count, %s', len(task_list))
|
||||
result: list = await asyncio.gather(*task_list)
|
||||
assert None not in result
|
||||
|
||||
@ -178,28 +181,28 @@ async def test_user_config_async(
|
||||
config=config_update, replace=True)
|
||||
assert (config_replace := await storage.load_user_config_async(
|
||||
uid=test_uid, cloud_server=test_cloud_server)) == config_update
|
||||
print('replace result, ', config_replace)
|
||||
_LOGGER.info('replace result, %s', config_replace)
|
||||
# Test query
|
||||
query_keys = list(config_base.keys())
|
||||
print('query keys, ', query_keys)
|
||||
_LOGGER.info('query keys, %s', query_keys)
|
||||
query_result = await storage.load_user_config_async(
|
||||
uid=test_uid, cloud_server=test_cloud_server, keys=query_keys)
|
||||
print('query result 1, ', query_result)
|
||||
_LOGGER.info('query result 1, %s', query_result)
|
||||
assert await storage.update_user_config_async(
|
||||
uid=test_uid, cloud_server=test_cloud_server,
|
||||
config=config_base, replace=True)
|
||||
query_result = await storage.load_user_config_async(
|
||||
uid=test_uid, cloud_server=test_cloud_server, keys=query_keys)
|
||||
print('query result 2, ', query_result)
|
||||
_LOGGER.info('query result 2, %s', query_result)
|
||||
query_result = await storage.load_user_config_async(
|
||||
uid=test_uid, cloud_server=test_cloud_server)
|
||||
print('query result all, ', query_result)
|
||||
_LOGGER.info('query result all, %s', query_result)
|
||||
# Remove config
|
||||
assert await storage.update_user_config_async(
|
||||
uid=test_uid, cloud_server=test_cloud_server, config=None)
|
||||
query_result = await storage.load_user_config_async(
|
||||
uid=test_uid, cloud_server=test_cloud_server)
|
||||
print('remove result, ', query_result)
|
||||
_LOGGER.info('remove result, %s', query_result)
|
||||
# Remove domain
|
||||
assert await storage.remove_domain_async(domain='miot_config')
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user