mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2025-09-18 04:03:21 +08:00
Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
0f65635342 | |||
947169f18d | |||
65a7a6d22a | |||
c29f7eecbd | |||
58c671483e | |||
7c97b85f02 | |||
3f2c2a648b | |||
f11b2f2f68 | |||
f3abbef94c | |||
dae63657d7 | |||
3cc66450bd | |||
8cbb451153 | |||
0fee02ae5c | |||
580ff87e7f | |||
94583a23d1 | |||
925cf3b90f | |||
836bd01ead | |||
4ad040d2ea | |||
981429670a | |||
a82fd86c60 | |||
df3faea257 | |||
e09676661c | |||
aebeaf0245 |
4
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -5,8 +5,8 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Describe the Bug / 描述问题
|
label: Describe the Bug / 描述问题
|
||||||
description: |
|
description: |
|
||||||
> A clear and concise description of what the bug is.
|
> A clear and concise description of what the bug is. Please include the device model information (Like xiaomi.gateway.hub1 which can be found in Device info page).
|
||||||
> 清晰且简明地描述问题。
|
> 清晰且简明地描述问题。请注明设备 model 信息(例如 xiaomi.gateway.hub1,可在设备详情页查询)。
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,4 +1,39 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
## v0.4.2
|
||||||
|
### Changed
|
||||||
|
- Set the battery service's start-charge action as the fallback action to support RETURN_HOME feature of the vacuum entity. [#1344](https://github.com/XiaoMi/ha_xiaomi_home/pull/1344)
|
||||||
|
### Fixed
|
||||||
|
- Correct the property value format after expression calculation. [#1366](https://github.com/XiaoMi/ha_xiaomi_home/pull/1366)
|
||||||
|
- Fix the MIoT-Spec-V2 of fix: xiaomi.fan.p70 and fix: xiaomi.fan.p76 fan level, xiaomi.airc.rr0r00 and xiaomi.airc.h43h00 humidity-range, and zhimi.humidifier.ca4 water level. [#1367](https://github.com/XiaoMi/ha_xiaomi_home/pull/1367)
|
||||||
|
- Ignore the unsupported model hmpace.motion.v6nfc.
|
||||||
|
- Delete all unsupported MIoT-Spec-V2 instances of narwa.vacuum.001 and narwa.vacuum.ax11. [#1355](https://github.com/XiaoMi/ha_xiaomi_home/pull/1355)
|
||||||
|
|
||||||
|
## v0.4.1
|
||||||
|
### Changed
|
||||||
|
- The setting option "Cover closed position" in CONFIGURE is changed to "Cover dead zone width". [#1301](https://github.com/XiaoMi/ha_xiaomi_home/pull/1301)
|
||||||
|
- Add an alongside switch entity for 090615.aircondition.ktf and juhl.aircondition.hvac. [#1303](https://github.com/XiaoMi/ha_xiaomi_home/pull/1303)
|
||||||
|
### Fixed
|
||||||
|
- Fix the vacuum status so that the vacuum activity will not always be idle. [#1299](https://github.com/XiaoMi/ha_xiaomi_home/pull/1299)
|
||||||
|
- Set the device on when the switch status is False or None. [#1303](https://github.com/XiaoMi/ha_xiaomi_home/pull/1303)
|
||||||
|
- Hide sensitive info in printing logs. [#1328](https://github.com/XiaoMi/ha_xiaomi_home/pull/1328)
|
||||||
|
- Fix the MIoT-Spec-V2 of cuco.plug.cp2d electric current, xiaomi.fan.p45 fan level, sanmei.valve.s1 power consumption, current and voltage, xiaomi.aircondition.c17, xiaomi.aircondition.m16 and xiaomi.airc.h40h00 humidity-range unit. [#1329](https://github.com/XiaoMi/ha_xiaomi_home/pull/1329)
|
||||||
|
|
||||||
|
## v0.4.0
|
||||||
|
### Added
|
||||||
|
- Add the watch as the device tracker entity. [#1189](https://github.com/XiaoMi/ha_xiaomi_home/pull/1189)
|
||||||
|
- Add the wifi speaker and the television as the media player entity. [#706](https://github.com/XiaoMi/ha_xiaomi_home/pull/706)
|
||||||
|
- Add an option in CONFIGURE to set the cover closed position. [#1242](https://github.com/XiaoMi/ha_xiaomi_home/pull/1242)
|
||||||
|
- Add notifications to show the status of the local connection to the central hub gateway. [#1280](https://github.com/XiaoMi/ha_xiaomi_home/pull/1280)
|
||||||
|
- Import the device from the third party cloud. [#1258](https://github.com/XiaoMi/ha_xiaomi_home/pull/1258)
|
||||||
|
### Changed
|
||||||
|
- Add an alongside switch entity for viomi.waterheater.m1. [#1255](https://github.com/XiaoMi/ha_xiaomi_home/pull/1255)
|
||||||
|
- Do not subscribe BLE device online/offline state message. [#1264](https://github.com/XiaoMi/ha_xiaomi_home/pull/1264)
|
||||||
|
### Fixed
|
||||||
|
- Keep the first element of the discovered ip address list as the recently added address when getting mdns result. [#1250](https://github.com/XiaoMi/ha_xiaomi_home/pull/1250)
|
||||||
|
- Subscribe local topics every time when connected to the central hub gateway. [#1266](https://github.com/XiaoMi/ha_xiaomi_home/pull/1266)
|
||||||
|
- Record the "closing" and "closed" status that occur frequently in the motor-controller, the window-opener and the curtain service. [#1262](https://github.com/XiaoMi/ha_xiaomi_home/pull/1262)
|
||||||
|
- Fix xiaomi.aircondition.c24 total power consumption unit, adp.motor.adswb4 motor switch, cgllc.airm.cgd1st environment temperature, and shhf.light.sflt11 fan switch status. [#1256](https://github.com/XiaoMi/ha_xiaomi_home/pull/1256)
|
||||||
|
|
||||||
## v0.3.4
|
## v0.3.4
|
||||||
### Added
|
### Added
|
||||||
- Exclude the unsupported device models. [#1205](https://github.com/XiaoMi/ha_xiaomi_home/pull/1205)
|
- Exclude the unsupported device models. [#1205](https://github.com/XiaoMi/ha_xiaomi_home/pull/1205)
|
||||||
|
@ -83,9 +83,9 @@ Xiaomi Home Integration and the affiliated cloud interface is provided by Xiaomi
|
|||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
- Does Xiaomi Home Integration support all Xiaomi Home devices?
|
- Does Xiaomi Home Integration support all Xiaomi smart devices?
|
||||||
|
|
||||||
Xiaomi Home Integration currently supports most categories of Home device. Only a few categories are not supported. They are Bluetooth device, infrared device and virtual device.
|
Xiaomi Home Integration currently supports most categories of the smart device. Only a few categories are not supported. They are Bluetooth device, infrared device and virtual device.
|
||||||
|
|
||||||
- Does Xiaomi Home Integration support multiple Xiaomi accounts?
|
- Does Xiaomi Home Integration support multiple Xiaomi accounts?
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ Xiaomi Home Integration and the affiliated cloud interface is provided by Xiaomi
|
|||||||
|
|
||||||
- Does Xiaomi Home Integration support local control?
|
- Does Xiaomi Home Integration support local control?
|
||||||
|
|
||||||
Local control is implemented by [Xiaomi Central Hub Gateway](https://www.mi.com/shop/buy/detail?product_id=15755&cfrom=search) (firmware version 3.4.0_0000 above) or Xiaomi home devices with built-in central hub gateway (software version 0.8.0 above) inside. If you do not have a Xiaomi central hub gateway or other devices having central hub gateway function, all control commands are sent through Xiaomi Cloud. The firmware for Xiaomi central hub gateway including the built-in central hub gateway supporting Home Assistant local control feature has not been released yet. Please refer to MIoT team's notification for upgrade plans.
|
Local control is implemented by [Xiaomi Central Hub Gateway](https://www.mi.com/shop/buy/detail?product_id=15755&cfrom=search) (firmware version 3.3.0_0023 and above) or Xiaomi smart devices with built-in central hub gateway (software version 0.8.9 and above) inside. If you do not have a Xiaomi central hub gateway or other devices having central hub gateway function, all control commands are sent through Xiaomi Cloud. The firmware for Xiaomi central hub gateway including the built-in central hub gateway supporting Home Assistant local control feature has not been released yet. Please refer to MIoT team's notification for upgrade plans.
|
||||||
|
|
||||||
Xiaomi central hub gateway is only available in mainland China. In other regions, it is not available.
|
Xiaomi central hub gateway is only available in mainland China. In other regions, it is not available.
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ The value of the event instance name indicates `_attr_device_class` of the Home
|
|||||||
|
|
||||||
`spec_filter.yaml` is used to filter out the MIoT-Spec-V2 instance that will not be converted to Home Assistant entity.
|
`spec_filter.yaml` is used to filter out the MIoT-Spec-V2 instance that will not be converted to Home Assistant entity.
|
||||||
|
|
||||||
The format of `spec_filter.json` is as follows.
|
The format of `spec_filter.yaml` is as follows.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
<MIoT-Spec-V2 device instance urn without the version field>:
|
<MIoT-Spec-V2 device instance urn without the version field>:
|
||||||
|
@ -546,7 +546,7 @@ class AirConditioner(FeatureOnOff, FeatureTargetTemperature,
|
|||||||
f'{self.entity_id}')
|
f'{self.entity_id}')
|
||||||
return
|
return
|
||||||
# set the device on
|
# set the device on
|
||||||
if self.get_prop_value(prop=self._prop_on) is False:
|
if self.get_prop_value(prop=self._prop_on) is not True:
|
||||||
await self.set_property_async(prop=self._prop_on,
|
await self.set_property_async(prop=self._prop_on,
|
||||||
value=True,
|
value=True,
|
||||||
write_ha_state=False)
|
write_ha_state=False)
|
||||||
|
@ -75,6 +75,9 @@ from .miot.const import (
|
|||||||
DEFAULT_CLOUD_SERVER,
|
DEFAULT_CLOUD_SERVER,
|
||||||
DEFAULT_CTRL_MODE,
|
DEFAULT_CTRL_MODE,
|
||||||
DEFAULT_INTEGRATION_LANGUAGE,
|
DEFAULT_INTEGRATION_LANGUAGE,
|
||||||
|
DEFAULT_COVER_DEAD_ZONE_WIDTH,
|
||||||
|
MIN_COVER_DEAD_ZONE_WIDTH,
|
||||||
|
MAX_COVER_DEAD_ZONE_WIDTH,
|
||||||
DEFAULT_NICK_NAME,
|
DEFAULT_NICK_NAME,
|
||||||
DEFAULT_OAUTH2_API_HOST,
|
DEFAULT_OAUTH2_API_HOST,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -129,6 +132,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
_cloud_server: str
|
_cloud_server: str
|
||||||
_integration_language: str
|
_integration_language: str
|
||||||
|
_cover_dz_width: int
|
||||||
_auth_info: dict
|
_auth_info: dict
|
||||||
_nick_name: str
|
_nick_name: str
|
||||||
_home_selected: dict
|
_home_selected: dict
|
||||||
@ -151,6 +155,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self._main_loop = asyncio.get_running_loop()
|
self._main_loop = asyncio.get_running_loop()
|
||||||
self._cloud_server = DEFAULT_CLOUD_SERVER
|
self._cloud_server = DEFAULT_CLOUD_SERVER
|
||||||
self._integration_language = DEFAULT_INTEGRATION_LANGUAGE
|
self._integration_language = DEFAULT_INTEGRATION_LANGUAGE
|
||||||
|
self._cover_dz_width = DEFAULT_COVER_DEAD_ZONE_WIDTH
|
||||||
self._storage_path = ''
|
self._storage_path = ''
|
||||||
self._virtual_did = ''
|
self._virtual_did = ''
|
||||||
self._uid = ''
|
self._uid = ''
|
||||||
@ -951,6 +956,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
'action_debug': self._action_debug,
|
'action_debug': self._action_debug,
|
||||||
'hide_non_standard_entities':
|
'hide_non_standard_entities':
|
||||||
self._hide_non_standard_entities,
|
self._hide_non_standard_entities,
|
||||||
|
'cover_dead_zone_width': self._cover_dz_width,
|
||||||
'display_binary_mode': self._display_binary_mode,
|
'display_binary_mode': self._display_binary_mode,
|
||||||
'display_devices_changed_notify':
|
'display_devices_changed_notify':
|
||||||
self._display_devices_changed_notify
|
self._display_devices_changed_notify
|
||||||
@ -995,6 +1001,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
_hide_non_standard_entities: bool
|
_hide_non_standard_entities: bool
|
||||||
_display_binary_mode: list[str]
|
_display_binary_mode: list[str]
|
||||||
_display_devs_notify: list[str]
|
_display_devs_notify: list[str]
|
||||||
|
_cover_dz_width: int
|
||||||
|
|
||||||
_oauth_redirect_url_full: str
|
_oauth_redirect_url_full: str
|
||||||
_auth_info: dict
|
_auth_info: dict
|
||||||
@ -1015,6 +1022,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
_opt_lan_ctrl_cfg: bool
|
_opt_lan_ctrl_cfg: bool
|
||||||
_opt_network_detect_cfg: bool
|
_opt_network_detect_cfg: bool
|
||||||
_opt_check_network_deps: bool
|
_opt_check_network_deps: bool
|
||||||
|
_cover_width_new: int
|
||||||
|
|
||||||
_trans_rules_count: int
|
_trans_rules_count: int
|
||||||
_trans_rules_count_success: int
|
_trans_rules_count_success: int
|
||||||
@ -1043,6 +1051,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self._ctrl_mode = self._entry_data.get('ctrl_mode', DEFAULT_CTRL_MODE)
|
self._ctrl_mode = self._entry_data.get('ctrl_mode', DEFAULT_CTRL_MODE)
|
||||||
self._integration_language = self._entry_data.get(
|
self._integration_language = self._entry_data.get(
|
||||||
'integration_language', DEFAULT_INTEGRATION_LANGUAGE)
|
'integration_language', DEFAULT_INTEGRATION_LANGUAGE)
|
||||||
|
self._cover_dz_width = self._entry_data.get(
|
||||||
|
'cover_dead_zone_width', DEFAULT_COVER_DEAD_ZONE_WIDTH)
|
||||||
self._nick_name = self._entry_data.get('nick_name', DEFAULT_NICK_NAME)
|
self._nick_name = self._entry_data.get('nick_name', DEFAULT_NICK_NAME)
|
||||||
self._action_debug = self._entry_data.get('action_debug', False)
|
self._action_debug = self._entry_data.get('action_debug', False)
|
||||||
self._hide_non_standard_entities = self._entry_data.get(
|
self._hide_non_standard_entities = self._entry_data.get(
|
||||||
@ -1068,6 +1078,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
self._action_debug_new = False
|
self._action_debug_new = False
|
||||||
self._hide_non_standard_entities_new = False
|
self._hide_non_standard_entities_new = False
|
||||||
self._display_binary_mode_new = []
|
self._display_binary_mode_new = []
|
||||||
|
self._cover_width_new = self._cover_dz_width
|
||||||
self._update_user_info = False
|
self._update_user_info = False
|
||||||
self._update_devices = False
|
self._update_devices = False
|
||||||
self._update_trans_rules = False
|
self._update_trans_rules = False
|
||||||
@ -1340,6 +1351,12 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
): cv.multi_select(
|
): cv.multi_select(
|
||||||
self._miot_i18n.translate(
|
self._miot_i18n.translate(
|
||||||
'config.binary_mode')), # type: ignore
|
'config.binary_mode')), # type: ignore
|
||||||
|
vol.Optional(
|
||||||
|
'cover_dead_zone_width',
|
||||||
|
default=self._cover_dz_width # type: ignore
|
||||||
|
): vol.All(vol.Coerce(int), vol.Range(
|
||||||
|
min=MIN_COVER_DEAD_ZONE_WIDTH,
|
||||||
|
max=MAX_COVER_DEAD_ZONE_WIDTH)),
|
||||||
vol.Required(
|
vol.Required(
|
||||||
'update_trans_rules',
|
'update_trans_rules',
|
||||||
default=self._update_trans_rules # type: ignore
|
default=self._update_trans_rules # type: ignore
|
||||||
@ -1378,6 +1395,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
'update_lan_ctrl_config', self._opt_lan_ctrl_cfg)
|
'update_lan_ctrl_config', self._opt_lan_ctrl_cfg)
|
||||||
self._opt_network_detect_cfg = user_input.get(
|
self._opt_network_detect_cfg = user_input.get(
|
||||||
'network_detect_config', self._opt_network_detect_cfg)
|
'network_detect_config', self._opt_network_detect_cfg)
|
||||||
|
self._cover_width_new = user_input.get(
|
||||||
|
'cover_dead_zone_width', self._cover_dz_width)
|
||||||
|
|
||||||
return await self.async_step_update_user_info()
|
return await self.async_step_update_user_info()
|
||||||
|
|
||||||
@ -1926,6 +1945,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
'nick_name': self._nick_name,
|
'nick_name': self._nick_name,
|
||||||
'lang_new': INTEGRATION_LANGUAGES[self._lang_new],
|
'lang_new': INTEGRATION_LANGUAGES[self._lang_new],
|
||||||
'nick_name_new': self._nick_name_new,
|
'nick_name_new': self._nick_name_new,
|
||||||
|
'cover_width_new': self._cover_width_new,
|
||||||
'devices_add': len(self._devices_add),
|
'devices_add': len(self._devices_add),
|
||||||
'devices_remove': len(self._devices_remove),
|
'devices_remove': len(self._devices_remove),
|
||||||
'trans_rules_count': self._trans_rules_count,
|
'trans_rules_count': self._trans_rules_count,
|
||||||
@ -1952,6 +1972,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
if self._lang_new != self._integration_language:
|
if self._lang_new != self._integration_language:
|
||||||
self._entry_data['integration_language'] = self._lang_new
|
self._entry_data['integration_language'] = self._lang_new
|
||||||
self._need_reload = True
|
self._need_reload = True
|
||||||
|
if self._cover_width_new != self._cover_dz_width:
|
||||||
|
self._entry_data['cover_dead_zone_width'] = self._cover_width_new
|
||||||
|
self._need_reload = True
|
||||||
if self._update_user_info:
|
if self._update_user_info:
|
||||||
self._entry_data['nick_name'] = self._nick_name_new
|
self._entry_data['nick_name'] = self._nick_name_new
|
||||||
if self._update_devices:
|
if self._update_devices:
|
||||||
|
@ -91,6 +91,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry,
|
|||||||
class Cover(MIoTServiceEntity, CoverEntity):
|
class Cover(MIoTServiceEntity, CoverEntity):
|
||||||
"""Cover entities for Xiaomi Home."""
|
"""Cover entities for Xiaomi Home."""
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
|
_cover_dead_zone_width: int
|
||||||
_prop_motor_control: Optional[MIoTSpecProperty]
|
_prop_motor_control: Optional[MIoTSpecProperty]
|
||||||
_prop_motor_value_open: Optional[int]
|
_prop_motor_value_open: Optional[int]
|
||||||
_prop_motor_value_close: Optional[int]
|
_prop_motor_value_close: Optional[int]
|
||||||
@ -115,6 +116,9 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
|||||||
self._attr_supported_color_modes = set()
|
self._attr_supported_color_modes = set()
|
||||||
self._attr_supported_features = CoverEntityFeature(0)
|
self._attr_supported_features = CoverEntityFeature(0)
|
||||||
|
|
||||||
|
self._cover_dead_zone_width = (
|
||||||
|
miot_device.miot_client.cover_dead_zone_width)
|
||||||
|
|
||||||
self._prop_motor_control = None
|
self._prop_motor_control = None
|
||||||
self._prop_motor_value_open = None
|
self._prop_motor_value_open = None
|
||||||
self._prop_motor_value_close = None
|
self._prop_motor_value_close = None
|
||||||
@ -166,12 +170,13 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
|||||||
self._prop_status_opening.append(item.value)
|
self._prop_status_opening.append(item.value)
|
||||||
elif item_name in {
|
elif item_name in {
|
||||||
'closing', 'close', 'down', 'dowm', 'falling',
|
'closing', 'close', 'down', 'dowm', 'falling',
|
||||||
'dropping', 'downing', 'lower'
|
'fallin', 'dropping', 'downing', 'lower'
|
||||||
}:
|
}:
|
||||||
self._prop_status_closing.append(item.value)
|
self._prop_status_closing.append(item.value)
|
||||||
elif item_name in {
|
elif item_name in {
|
||||||
'stopatlowest', 'stoplowerlimit', 'lowerlimitstop',
|
'closed', 'closeover', 'stopatlowest',
|
||||||
'floor', 'lowerlimit'
|
'stoplowerlimit', 'lowerlimitstop', 'floor',
|
||||||
|
'lowerlimit'
|
||||||
}:
|
}:
|
||||||
self._prop_status_closed.append(item.value)
|
self._prop_status_closed.append(item.value)
|
||||||
self._prop_status = prop
|
self._prop_status = prop
|
||||||
@ -270,8 +275,14 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
|||||||
self._prop_pos_closing = False
|
self._prop_pos_closing = False
|
||||||
return self.get_prop_value(prop=self._prop_target_position)
|
return self.get_prop_value(prop=self._prop_target_position)
|
||||||
pos = self.get_prop_value(prop=self._prop_current_position)
|
pos = self.get_prop_value(prop=self._prop_current_position)
|
||||||
return None if pos is None else round(pos * 100 /
|
if pos is None:
|
||||||
self._prop_position_value_range)
|
return None
|
||||||
|
pos = round(pos*100/self._prop_position_value_range)
|
||||||
|
if pos <= self._cover_dead_zone_width:
|
||||||
|
pos = 0
|
||||||
|
elif pos >= (100 - self._cover_dead_zone_width):
|
||||||
|
pos = 100
|
||||||
|
return pos
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_opening(self) -> Optional[bool]:
|
def is_opening(self) -> Optional[bool]:
|
||||||
|
126
custom_components/xiaomi_home/device_tracker.py
Normal file
126
custom_components/xiaomi_home/device_tracker.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# -*- 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.
|
||||||
|
|
||||||
|
Device tracker entities for Xiaomi Home.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.components.device_tracker import TrackerEntity
|
||||||
|
|
||||||
|
from .miot.const import DOMAIN
|
||||||
|
from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData
|
||||||
|
from .miot.miot_spec import MIoTSpecProperty
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
device_list: list[MIoTDevice] = hass.data[DOMAIN]['devices'][
|
||||||
|
config_entry.entry_id]
|
||||||
|
new_entities = []
|
||||||
|
for miot_device in device_list:
|
||||||
|
for data in miot_device.entity_list.get('device_tracker', []):
|
||||||
|
new_entities.append(
|
||||||
|
DeviceTracker(miot_device=miot_device, entity_data=data))
|
||||||
|
if new_entities:
|
||||||
|
async_add_entities(new_entities)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceTracker(MIoTServiceEntity, TrackerEntity):
|
||||||
|
"""Tracker entities for Xiaomi Home."""
|
||||||
|
_prop_battery_level: Optional[MIoTSpecProperty]
|
||||||
|
_prop_latitude: Optional[MIoTSpecProperty]
|
||||||
|
_prop_longitude: Optional[MIoTSpecProperty]
|
||||||
|
_prop_area_id: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
self._prop_battery_level = None
|
||||||
|
self._prop_latitude = None
|
||||||
|
self._prop_longitude = None
|
||||||
|
self._prop_area_id = None
|
||||||
|
|
||||||
|
# properties
|
||||||
|
for prop in entity_data.props:
|
||||||
|
if prop.name == 'battery-level':
|
||||||
|
self._prop_battery_level = prop
|
||||||
|
elif prop.name == 'latitude':
|
||||||
|
self._prop_latitude = prop
|
||||||
|
elif prop.name == 'longitude':
|
||||||
|
self._prop_longitude = prop
|
||||||
|
elif prop.name == 'area-id':
|
||||||
|
self._prop_area_id = prop
|
||||||
|
|
||||||
|
@property
|
||||||
|
def battery_level(self) -> Optional[int]:
|
||||||
|
"""The battery level of the device."""
|
||||||
|
return None if (self._prop_battery_level
|
||||||
|
is None) else self.get_prop_value(
|
||||||
|
prop=self._prop_battery_level)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latitude(self) -> Optional[float]:
|
||||||
|
"""The latitude coordinate of the device."""
|
||||||
|
return None if self._prop_latitude is None else self.get_prop_value(
|
||||||
|
prop=self._prop_latitude)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def longitude(self) -> Optional[float]:
|
||||||
|
"""The longitude coordinate of the device."""
|
||||||
|
return None if self._prop_longitude is None else self.get_prop_value(
|
||||||
|
prop=self._prop_longitude)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def location_name(self) -> Optional[str]:
|
||||||
|
"""The location name of the device."""
|
||||||
|
return None if self._prop_area_id is None else self.get_prop_value(
|
||||||
|
prop=self._prop_area_id)
|
@ -221,7 +221,7 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
# preset_mode
|
# preset_mode
|
||||||
if preset_mode:
|
if preset_mode:
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
self._prop_mode,
|
prop=self._prop_mode,
|
||||||
value=self.get_map_key(
|
value=self.get_map_key(
|
||||||
map_=self._mode_map, value=preset_mode))
|
map_=self._mode_map, value=preset_mode))
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||||
"""Set the preset mode."""
|
"""Set the preset mode."""
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
self._prop_mode,
|
prop=self._prop_mode,
|
||||||
value=self.get_map_key(
|
value=self.get_map_key(
|
||||||
map_=self._mode_map, value=preset_mode))
|
map_=self._mode_map, value=preset_mode))
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"cryptography",
|
"cryptography",
|
||||||
"psutil"
|
"psutil"
|
||||||
],
|
],
|
||||||
"version": "v0.3.4",
|
"version": "v0.4.2",
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
"_miot-central._tcp.local."
|
"_miot-central._tcp.local."
|
||||||
]
|
]
|
||||||
|
470
custom_components/xiaomi_home/media_player.py
Normal file
470
custom_components/xiaomi_home/media_player.py
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
# -*- 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.
|
||||||
|
|
||||||
|
Media player entities for Xiaomi Home.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.components.media_player import (MediaPlayerEntity,
|
||||||
|
MediaPlayerEntityFeature,
|
||||||
|
MediaPlayerDeviceClass,
|
||||||
|
MediaPlayerState, MediaType)
|
||||||
|
|
||||||
|
from .miot.const import DOMAIN
|
||||||
|
from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData
|
||||||
|
from .miot.miot_spec import MIoTSpecProperty, MIoTSpecAction
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
new_entities = []
|
||||||
|
for miot_device in device_list:
|
||||||
|
for data in miot_device.entity_list.get('wifi-speaker', []):
|
||||||
|
new_entities.append(
|
||||||
|
WifiSpeaker(miot_device=miot_device, entity_data=data))
|
||||||
|
for data in miot_device.entity_list.get('television', []):
|
||||||
|
new_entities.append(
|
||||||
|
Television(miot_device=miot_device, entity_data=data))
|
||||||
|
|
||||||
|
if new_entities:
|
||||||
|
async_add_entities(new_entities)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureVolumeMute(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""VOLUME_MUTE feature of the media player entity."""
|
||||||
|
_prop_mute: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._prop_mute = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# properties
|
||||||
|
for prop in entity_data.props:
|
||||||
|
if prop.name == 'mute':
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.VOLUME_MUTE)
|
||||||
|
self._prop_mute = prop
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_volume_muted(self) -> Optional[bool]:
|
||||||
|
"""True if volume is currently muted."""
|
||||||
|
return self.get_prop_value(
|
||||||
|
prop=self._prop_mute) if self._prop_mute else None
|
||||||
|
|
||||||
|
async def async_mute_volume(self, mute: bool) -> None:
|
||||||
|
"""Mute the volume."""
|
||||||
|
await self.set_property_async(prop=self._prop_mute, value=mute)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureVolumeSet(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""VOLUME_SET feature of the media player entity."""
|
||||||
|
_prop_volume: Optional[MIoTSpecProperty]
|
||||||
|
_volume_value_min: Optional[float]
|
||||||
|
_volume_value_max: Optional[float]
|
||||||
|
_volume_value_range: Optional[float]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._prop_volume = None
|
||||||
|
self._volume_value_min = None
|
||||||
|
self._volume_value_max = None
|
||||||
|
self._volume_value_range = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# properties
|
||||||
|
for prop in entity_data.props:
|
||||||
|
if prop.name == 'volume':
|
||||||
|
if not prop.value_range:
|
||||||
|
_LOGGER.error('invalid volume value_range format, %s',
|
||||||
|
self.entity_id)
|
||||||
|
continue
|
||||||
|
self._volume_value_min = prop.value_range.min_
|
||||||
|
self._volume_value_max = prop.value_range.max_
|
||||||
|
self._volume_value_range = (prop.value_range.max_ -
|
||||||
|
prop.value_range.min_)
|
||||||
|
self._attr_volume_step = (prop.value_range.step /
|
||||||
|
self._volume_value_range)
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.VOLUME_SET |
|
||||||
|
MediaPlayerEntityFeature.VOLUME_STEP)
|
||||||
|
self._prop_volume = prop
|
||||||
|
|
||||||
|
async def async_set_volume_level(self, volume: float) -> None:
|
||||||
|
"""Set volume level."""
|
||||||
|
value = volume * self._volume_value_range + self._volume_value_min
|
||||||
|
if value > self._volume_value_max:
|
||||||
|
value = self._volume_value_max
|
||||||
|
elif value < self._volume_value_min:
|
||||||
|
value = self._volume_value_min
|
||||||
|
await self.set_property_async(prop=self._prop_volume, value=value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def volume_level(self) -> Optional[float]:
|
||||||
|
"""The current volume level, range [0, 1]."""
|
||||||
|
value = self.get_prop_value(
|
||||||
|
prop=self._prop_volume) if self._prop_volume else None
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return (value - self._volume_value_min) / self._volume_value_range
|
||||||
|
|
||||||
|
|
||||||
|
class FeaturePlay(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""PLAY feature of the media player entity."""
|
||||||
|
_action_play: Optional[MIoTSpecAction]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._action_play = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# actions
|
||||||
|
for act in entity_data.actions:
|
||||||
|
if act.name == 'play':
|
||||||
|
self._attr_supported_features |= (MediaPlayerEntityFeature.PLAY)
|
||||||
|
self._action_play = act
|
||||||
|
|
||||||
|
async def async_media_play(self) -> None:
|
||||||
|
"""Send play command."""
|
||||||
|
await self.action_async(action=self._action_play)
|
||||||
|
|
||||||
|
|
||||||
|
class FeaturePause(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""PAUSE feature of the media player entity."""
|
||||||
|
_action_pause: Optional[MIoTSpecAction]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._action_pause = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# actions
|
||||||
|
for act in entity_data.actions:
|
||||||
|
if act.name == 'pause':
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.PAUSE)
|
||||||
|
self._action_pause = act
|
||||||
|
|
||||||
|
async def async_media_pause(self) -> None:
|
||||||
|
"""Send pause command."""
|
||||||
|
await self.action_async(action=self._action_pause)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureStop(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""STOP feature of the media player entity."""
|
||||||
|
_action_stop: Optional[MIoTSpecAction]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._action_stop = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# actions
|
||||||
|
for act in entity_data.actions:
|
||||||
|
if act.name == 'stop':
|
||||||
|
self._attr_supported_features |= (MediaPlayerEntityFeature.STOP)
|
||||||
|
self._action_stop = act
|
||||||
|
|
||||||
|
async def async_media_stop(self) -> None:
|
||||||
|
"""Send stop command."""
|
||||||
|
await self.action_async(action=self._action_stop)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureNextTrack(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""NEXT_TRACK feature of the media player entity."""
|
||||||
|
_action_next: Optional[MIoTSpecAction]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._action_next = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# actions
|
||||||
|
for act in entity_data.actions:
|
||||||
|
if act.name == 'next':
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.NEXT_TRACK)
|
||||||
|
self._action_next = act
|
||||||
|
|
||||||
|
async def async_media_next_track(self) -> None:
|
||||||
|
"""Send next track command."""
|
||||||
|
await self.action_async(action=self._action_next)
|
||||||
|
|
||||||
|
|
||||||
|
class FeaturePreviousTrack(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""PREVIOUS_TRACK feature of the media player entity."""
|
||||||
|
_action_previous: Optional[MIoTSpecAction]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._action_previous = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# actions
|
||||||
|
for act in entity_data.actions:
|
||||||
|
if act.name == 'previous':
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.PREVIOUS_TRACK)
|
||||||
|
self._action_previous = act
|
||||||
|
|
||||||
|
async def async_media_previous_track(self) -> None:
|
||||||
|
"""Send previous track command."""
|
||||||
|
await self.action_async(action=self._action_previous)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureSoundMode(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""SELECT_SOUND_MODE feature of the media player entity."""
|
||||||
|
_prop_play_loop_mode: Optional[MIoTSpecProperty]
|
||||||
|
_sound_mode_map: Optional[dict[int, str]]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._prop_play_loop_mode = None
|
||||||
|
self._sound_mode_map = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# properties
|
||||||
|
for prop in entity_data.props:
|
||||||
|
if prop.name == 'play-loop-mode':
|
||||||
|
if not prop.value_list:
|
||||||
|
_LOGGER.error('invalid play-loop-mode value_list, %s',
|
||||||
|
self.entity_id)
|
||||||
|
continue
|
||||||
|
self._sound_mode_map = prop.value_list.to_map()
|
||||||
|
self._attr_sound_mode_list = list(self._sound_mode_map.values())
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.SELECT_SOUND_MODE)
|
||||||
|
self._prop_play_loop_mode = prop
|
||||||
|
|
||||||
|
async def async_select_sound_mode(self, sound_mode: str):
|
||||||
|
"""Switch the sound mode of the entity."""
|
||||||
|
await self.set_property_async(prop=self._prop_play_loop_mode,
|
||||||
|
value=self.get_map_key(
|
||||||
|
map_=self._sound_mode_map,
|
||||||
|
value=sound_mode))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sound_mode(self) -> Optional[str]:
|
||||||
|
"""The current sound mode."""
|
||||||
|
return (self.get_map_value(map_=self._sound_mode_map,
|
||||||
|
key=self.get_prop_value(
|
||||||
|
prop=self._prop_play_loop_mode))
|
||||||
|
if self._prop_play_loop_mode else None)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureTurnOn(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""TURN_ON feature of the media player entity."""
|
||||||
|
_action_turn_on: Optional[MIoTSpecAction]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._action_turn_on = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# actions
|
||||||
|
for act in entity_data.actions:
|
||||||
|
if act.name == 'turn-on':
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.TURN_ON)
|
||||||
|
self._action_turn_on = act
|
||||||
|
|
||||||
|
async def async_turn_on(self) -> None:
|
||||||
|
"""Turn the media player on."""
|
||||||
|
await self.action_async(action=self._action_turn_on)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureTurnOff(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""TURN_OFF feature of the media player entity."""
|
||||||
|
_action_turn_off: Optional[MIoTSpecAction]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._action_turn_off = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# actions
|
||||||
|
for act in entity_data.actions:
|
||||||
|
if act.name == 'turn-off':
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.TURN_OFF)
|
||||||
|
self._action_turn_off = act
|
||||||
|
|
||||||
|
async def async_turn_off(self) -> None:
|
||||||
|
"""Turn the media player off."""
|
||||||
|
await self.action_async(action=self._action_turn_off)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureSource(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""SELECT_SOURCE feature of the media player entity."""
|
||||||
|
_prop_input_control: Optional[MIoTSpecProperty]
|
||||||
|
_input_source_map: Optional[dict[int, str]]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._prop_input_control = None
|
||||||
|
self._input_source_map = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# properties
|
||||||
|
for prop in entity_data.props:
|
||||||
|
if prop.name == 'input-control':
|
||||||
|
if not prop.value_list:
|
||||||
|
_LOGGER.error('invalid input-control value_list, %s',
|
||||||
|
self.entity_id)
|
||||||
|
continue
|
||||||
|
self._input_source_map = prop.value_list.to_map()
|
||||||
|
self._attr_source_list = list(self._input_source_map.values())
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
MediaPlayerEntityFeature.SELECT_SOURCE)
|
||||||
|
self._prop_input_control = prop
|
||||||
|
|
||||||
|
async def async_select_source(self, source: str) -> None:
|
||||||
|
"""Select input source."""
|
||||||
|
await self.set_property_async(prop=self._prop_input_control,
|
||||||
|
value=self.get_map_key(
|
||||||
|
map_=self._input_source_map,
|
||||||
|
value=source))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source(self) -> Optional[str]:
|
||||||
|
"""The current input source."""
|
||||||
|
return (self.get_map_value(map_=self._input_source_map,
|
||||||
|
key=self.get_prop_value(
|
||||||
|
prop=self._prop_input_control))
|
||||||
|
if self._prop_input_control else None)
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureState(MIoTServiceEntity, MediaPlayerEntity):
|
||||||
|
"""States feature of the media player entity."""
|
||||||
|
_prop_playing_state: Optional[MIoTSpecProperty]
|
||||||
|
_playing_state_map: Optional[dict[int, str]]
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the feature class."""
|
||||||
|
self._prop_playing_state = None
|
||||||
|
self._playing_state_map = None
|
||||||
|
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
# properties
|
||||||
|
for prop in entity_data.props:
|
||||||
|
if prop.name == 'playing-state':
|
||||||
|
if not prop.value_list:
|
||||||
|
_LOGGER.error('invalid mode value_list, %s', self.entity_id)
|
||||||
|
continue
|
||||||
|
self._playing_state_map = {}
|
||||||
|
for item in prop.value_list.items:
|
||||||
|
if item.name in {'off'}:
|
||||||
|
self._playing_state_map[
|
||||||
|
item.value] = MediaPlayerState.OFF
|
||||||
|
elif item.name in {'idle', 'stop', 'stopped'}:
|
||||||
|
self._playing_state_map[
|
||||||
|
item.value] = MediaPlayerState.IDLE
|
||||||
|
elif item.name in {'playing'}:
|
||||||
|
self._playing_state_map[
|
||||||
|
item.value] = MediaPlayerState.PLAYING
|
||||||
|
elif item.name in {'pause', 'paused'}:
|
||||||
|
self._playing_state_map[
|
||||||
|
item.value] = MediaPlayerState.PAUSED
|
||||||
|
self._prop_playing_state = prop
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self) -> Optional[MediaPlayerState]:
|
||||||
|
"""The current state."""
|
||||||
|
return (self.get_map_value(map_=self._playing_state_map,
|
||||||
|
key=self.get_prop_value(
|
||||||
|
prop=self._prop_playing_state))
|
||||||
|
if self._prop_playing_state else MediaPlayerState.ON)
|
||||||
|
|
||||||
|
|
||||||
|
class WifiSpeaker(FeatureVolumeSet, FeatureVolumeMute, FeaturePlay,
|
||||||
|
FeaturePause, FeatureStop, FeatureNextTrack,
|
||||||
|
FeaturePreviousTrack, FeatureSoundMode, FeatureState):
|
||||||
|
"""WiFi speaker, aka XiaoAI sound speaker."""
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
|
||||||
|
self._attr_device_class = MediaPlayerDeviceClass.SPEAKER
|
||||||
|
self._attr_media_content_type = MediaType.MUSIC
|
||||||
|
|
||||||
|
|
||||||
|
class Television(FeatureVolumeSet, FeatureVolumeMute, FeaturePlay, FeaturePause,
|
||||||
|
FeatureStop, FeatureNextTrack, FeaturePreviousTrack,
|
||||||
|
FeatureSoundMode, FeatureState, FeatureSource, FeatureTurnOn,
|
||||||
|
FeatureTurnOff):
|
||||||
|
"""Television"""
|
||||||
|
|
||||||
|
def __init__(self, miot_device: MIoTDevice,
|
||||||
|
entity_data: MIoTEntityData) -> None:
|
||||||
|
"""Initialize the device."""
|
||||||
|
super().__init__(miot_device=miot_device, entity_data=entity_data)
|
||||||
|
|
||||||
|
self._attr_device_class = MediaPlayerDeviceClass.TV
|
||||||
|
self._attr_media_content_type = MediaType.VIDEO
|
@ -71,10 +71,12 @@ SUPPORTED_PLATFORMS: list = [
|
|||||||
'button',
|
'button',
|
||||||
'climate',
|
'climate',
|
||||||
'cover',
|
'cover',
|
||||||
|
'device_tracker',
|
||||||
'event',
|
'event',
|
||||||
'fan',
|
'fan',
|
||||||
'humidifier',
|
'humidifier',
|
||||||
'light',
|
'light',
|
||||||
|
'media_player',
|
||||||
'notify',
|
'notify',
|
||||||
'number',
|
'number',
|
||||||
'select',
|
'select',
|
||||||
@ -87,6 +89,7 @@ SUPPORTED_PLATFORMS: list = [
|
|||||||
|
|
||||||
UNSUPPORTED_MODELS: list = [
|
UNSUPPORTED_MODELS: list = [
|
||||||
'chuangmi.ir.v2',
|
'chuangmi.ir.v2',
|
||||||
|
'hmpace.motion.v6nfc',
|
||||||
'xiaomi.router.rd03'
|
'xiaomi.router.rd03'
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -118,6 +121,10 @@ INTEGRATION_LANGUAGES = {
|
|||||||
'zh-Hant': '繁體中文'
|
'zh-Hant': '繁體中文'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFAULT_COVER_DEAD_ZONE_WIDTH: int = 0
|
||||||
|
MIN_COVER_DEAD_ZONE_WIDTH: int = 0
|
||||||
|
MAX_COVER_DEAD_ZONE_WIDTH: int = 5
|
||||||
|
|
||||||
DEFAULT_CTRL_MODE: str = 'auto'
|
DEFAULT_CTRL_MODE: str = 'auto'
|
||||||
|
|
||||||
# Registered in Xiaomi OAuth 2.0 Service
|
# Registered in Xiaomi OAuth 2.0 Service
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} Geräte offline:** \n{message}",
|
"device_list_offline": "\n**{count} Geräte offline:** \n{message}",
|
||||||
"network_status_online": "Online",
|
"network_status_online": "Online",
|
||||||
"network_status_offline": "Offline",
|
"network_status_offline": "Offline",
|
||||||
|
"central_state_changed_title": "Verbindungsstatus des Zentral-Gateways",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** Lokale Verbindungsstrecke des Zentral-Gateways: {conn_status}",
|
||||||
|
"central_state_connected": "verbunden",
|
||||||
|
"central_state_disconnected": "getrennt",
|
||||||
"device_exec_error": "Fehler bei der Ausführung"
|
"device_exec_error": "Fehler bei der Ausführung"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} devices offline:** \n{message}",
|
"device_list_offline": "\n**{count} devices offline:** \n{message}",
|
||||||
"network_status_online": "Online",
|
"network_status_online": "Online",
|
||||||
"network_status_offline": "Offline",
|
"network_status_offline": "Offline",
|
||||||
|
"central_state_changed_title": "Central Hub Gateway Connection Status",
|
||||||
|
"central_state_changed":"**{nick_name}({uid}, {cloud_server})** local connection to Xiaomi central hub gateway: {conn_status}",
|
||||||
|
"central_state_connected": "Connected",
|
||||||
|
"central_state_disconnected": "Disconnected",
|
||||||
"device_exec_error": "Execution error"
|
"device_exec_error": "Execution error"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} dispositivos sin conexión:** \n{message}",
|
"device_list_offline": "\n**{count} dispositivos sin conexión:** \n{message}",
|
||||||
"network_status_online": "En línea",
|
"network_status_online": "En línea",
|
||||||
"network_status_offline": "Desconectado",
|
"network_status_offline": "Desconectado",
|
||||||
|
"central_state_changed_title": "Estado de conexión de la puerta de enlace central",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** enlace de conexión local de la puerta de enlace central: {conn_status}",
|
||||||
|
"central_state_connected": "conectado",
|
||||||
|
"central_state_disconnected": "desconectado",
|
||||||
"device_exec_error": "Error de ejecución"
|
"device_exec_error": "Error de ejecución"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} appareils hors ligne :** \n{message}",
|
"device_list_offline": "\n**{count} appareils hors ligne :** \n{message}",
|
||||||
"network_status_online": "En ligne",
|
"network_status_online": "En ligne",
|
||||||
"network_status_offline": "Hors ligne",
|
"network_status_offline": "Hors ligne",
|
||||||
|
"central_state_changed_title": "État de connexion de la passerelle centrale",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** liaison de connexion locale de la passerelle centrale : {conn_status}",
|
||||||
|
"central_state_connected": "connecté",
|
||||||
|
"central_state_disconnected": "déconnecté",
|
||||||
"device_exec_error": "Erreur d'exécution"
|
"device_exec_error": "Erreur d'exécution"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} dispositivi offline:** \n{message}",
|
"device_list_offline": "\n**{count} dispositivi offline:** \n{message}",
|
||||||
"network_status_online": "Online",
|
"network_status_online": "Online",
|
||||||
"network_status_offline": "Offline",
|
"network_status_offline": "Offline",
|
||||||
|
"central_state_changed_title": "Stato di connessione del gateway centrale",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** collegamento locale del gateway centrale: {conn_status}",
|
||||||
|
"central_state_connected": "connesso",
|
||||||
|
"central_state_disconnected": "disconnesso",
|
||||||
"device_exec_error": "Errore di esecuzione"
|
"device_exec_error": "Errore di esecuzione"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} デバイスがオフライン:** \n{message}",
|
"device_list_offline": "\n**{count} デバイスがオフライン:** \n{message}",
|
||||||
"network_status_online": "オンライン",
|
"network_status_online": "オンライン",
|
||||||
"network_status_offline": "オフライン",
|
"network_status_offline": "オフライン",
|
||||||
|
"central_state_changed_title": "中枢ゲートウェイ接続ステータス",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** 中枢ゲートウェイのローカル接続リンク: {conn_status}",
|
||||||
|
"central_state_connected": "接続済み",
|
||||||
|
"central_state_disconnected": "切断されました",
|
||||||
"device_exec_error": "実行エラー"
|
"device_exec_error": "実行エラー"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} apparaten offline:** \n{message}",
|
"device_list_offline": "\n**{count} apparaten offline:** \n{message}",
|
||||||
"network_status_online": "Online",
|
"network_status_online": "Online",
|
||||||
"network_status_offline": "Offline",
|
"network_status_offline": "Offline",
|
||||||
|
"central_state_changed_title": "Verbindingsstatus van centrale gateway",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** Lokale verbinding van centrale gateway: {conn_status}",
|
||||||
|
"central_state_connected": "verbonden",
|
||||||
|
"central_state_disconnected": "verbinding verbroken",
|
||||||
"device_exec_error": "Uitvoeringsfout"
|
"device_exec_error": "Uitvoeringsfout"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
|
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
|
||||||
"network_status_online": "online",
|
"network_status_online": "online",
|
||||||
"network_status_offline": "offline",
|
"network_status_offline": "offline",
|
||||||
|
"central_state_changed_title": "Status de conexão do gateway central",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** conexão local do gateway central: {conn_status}",
|
||||||
|
"central_state_connected": "conectado",
|
||||||
|
"central_state_disconnected": "desconectado",
|
||||||
"device_exec_error": "Erro na execução"
|
"device_exec_error": "Erro na execução"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
|
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
|
||||||
"network_status_online": "Online",
|
"network_status_online": "Online",
|
||||||
"network_status_offline": "Offline",
|
"network_status_offline": "Offline",
|
||||||
|
"central_state_changed_title": "Estado da ligação do gateway central",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** ligação local do gateway central: {conn_status}",
|
||||||
|
"central_state_connected": "ligado",
|
||||||
|
"central_state_disconnected": "desligado",
|
||||||
"device_exec_error": "Erro de execução"
|
"device_exec_error": "Erro de execução"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} устройств недоступно:** \n{message}",
|
"device_list_offline": "\n**{count} устройств недоступно:** \n{message}",
|
||||||
"network_status_online": "В сети",
|
"network_status_online": "В сети",
|
||||||
"network_status_offline": "Не в сети",
|
"network_status_offline": "Не в сети",
|
||||||
|
"central_state_changed_title": "Статус подключения центрального шлюза",
|
||||||
|
"central_state_changed": "**{nick_name}({uid}, {cloud_server})** локальное подключение центрального шлюза: {conn_status}",
|
||||||
|
"central_state_connected": "подключено",
|
||||||
|
"central_state_disconnected": "разъединено",
|
||||||
"device_exec_error": "Ошибка выполнения"
|
"device_exec_error": "Ошибка выполнения"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} 个设备离线**: \n{message}",
|
"device_list_offline": "\n**{count} 个设备离线**: \n{message}",
|
||||||
"network_status_online": "在线",
|
"network_status_online": "在线",
|
||||||
"network_status_offline": "离线",
|
"network_status_offline": "离线",
|
||||||
|
"central_state_changed_title": "中枢网关连接状态",
|
||||||
|
"central_state_changed":"**{nick_name}({uid}, {cloud_server})** 中枢网关本地连接链路: {conn_status}",
|
||||||
|
"central_state_connected": "已连接",
|
||||||
|
"central_state_disconnected": "断连",
|
||||||
"device_exec_error": "执行错误"
|
"device_exec_error": "执行错误"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
"device_list_offline": "\n**{count} 個設備離線:** \n{message}",
|
"device_list_offline": "\n**{count} 個設備離線:** \n{message}",
|
||||||
"network_status_online": "在線",
|
"network_status_online": "在線",
|
||||||
"network_status_offline": "離線",
|
"network_status_offline": "離線",
|
||||||
|
"central_state_changed_title": "中枢網關連接狀態",
|
||||||
|
"central_state_changed":"**{nick_name}({uid}, {cloud_server})** 中枢網關本地連接鏈路: {conn_status}",
|
||||||
|
"central_state_connected": "已連接",
|
||||||
|
"central_state_disconnected": "断連",
|
||||||
"device_exec_error": "執行錯誤"
|
"device_exec_error": "執行錯誤"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -63,7 +63,8 @@ from .common import MIoTMatcher, slugify_did
|
|||||||
from .const import (
|
from .const import (
|
||||||
DEFAULT_CTRL_MODE, DEFAULT_INTEGRATION_LANGUAGE, DEFAULT_NICK_NAME, DOMAIN,
|
DEFAULT_CTRL_MODE, DEFAULT_INTEGRATION_LANGUAGE, DEFAULT_NICK_NAME, DOMAIN,
|
||||||
MIHOME_CERT_EXPIRE_MARGIN, NETWORK_REFRESH_INTERVAL,
|
MIHOME_CERT_EXPIRE_MARGIN, NETWORK_REFRESH_INTERVAL,
|
||||||
OAUTH2_CLIENT_ID, SUPPORT_CENTRAL_GATEWAY_CTRL)
|
OAUTH2_CLIENT_ID, SUPPORT_CENTRAL_GATEWAY_CTRL,
|
||||||
|
DEFAULT_COVER_DEAD_ZONE_WIDTH)
|
||||||
from .miot_cloud import MIoTHttpClient, MIoTOauthClient
|
from .miot_cloud import MIoTHttpClient, MIoTOauthClient
|
||||||
from .miot_error import MIoTClientError, MIoTErrorCode
|
from .miot_error import MIoTClientError, MIoTErrorCode
|
||||||
from .miot_mips import (
|
from .miot_mips import (
|
||||||
@ -252,7 +253,18 @@ class MIoTClient:
|
|||||||
if not self._user_config:
|
if not self._user_config:
|
||||||
# Integration need to be add again
|
# Integration need to be add again
|
||||||
raise MIoTClientError('load_user_config_async error')
|
raise MIoTClientError('load_user_config_async error')
|
||||||
_LOGGER.debug('user config, %s', json.dumps(self._user_config))
|
# Hide sensitive info in printing
|
||||||
|
p_user_config: dict = deepcopy(self._user_config)
|
||||||
|
p_access_token: str = p_user_config['auth_info']['access_token']
|
||||||
|
p_refresh_token: str = p_user_config['auth_info']['refresh_token']
|
||||||
|
p_mac_key: str = p_user_config['auth_info']['mac_key']
|
||||||
|
p_user_config['auth_info'][
|
||||||
|
'access_token'] = f"{p_access_token[:5]}***{p_access_token[-5:]}"
|
||||||
|
p_user_config['auth_info'][
|
||||||
|
'refresh_token'] = f"{p_refresh_token[:5]}***{p_refresh_token[-5:]}"
|
||||||
|
p_user_config['auth_info'][
|
||||||
|
'mac_key'] = f"{p_mac_key[:5]}***{p_mac_key[-5:]}"
|
||||||
|
_LOGGER.debug('user config, %s', json.dumps(p_user_config))
|
||||||
# MIoT i18n client
|
# MIoT i18n client
|
||||||
self._i18n = MIoTI18n(
|
self._i18n = MIoTI18n(
|
||||||
lang=self._entry_data.get(
|
lang=self._entry_data.get(
|
||||||
@ -486,6 +498,11 @@ class MIoTClient:
|
|||||||
def display_binary_bool(self) -> bool:
|
def display_binary_bool(self) -> bool:
|
||||||
return self._display_binary_bool
|
return self._display_binary_bool
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cover_dead_zone_width(self) -> int:
|
||||||
|
return self._entry_data.get('cover_dead_zone_width',
|
||||||
|
DEFAULT_COVER_DEAD_ZONE_WIDTH)
|
||||||
|
|
||||||
@display_devices_changed_notify.setter
|
@display_devices_changed_notify.setter
|
||||||
def display_devices_changed_notify(self, value: list[str]) -> None:
|
def display_devices_changed_notify(self, value: list[str]) -> None:
|
||||||
if set(value) == set(self._display_devs_notify):
|
if set(value) == set(self._display_devs_notify):
|
||||||
@ -990,7 +1007,7 @@ class MIoTClient:
|
|||||||
and self._device_list_cloud[did].get('online', False)
|
and self._device_list_cloud[did].get('online', False)
|
||||||
):
|
):
|
||||||
from_new = 'cloud'
|
from_new = 'cloud'
|
||||||
if from_new == from_old:
|
if (from_new == from_old) and (from_new=='cloud' or from_new=='lan'):
|
||||||
# No need to update
|
# No need to update
|
||||||
return
|
return
|
||||||
# Unsub old
|
# Unsub old
|
||||||
@ -1109,8 +1126,10 @@ class MIoTClient:
|
|||||||
_LOGGER.info('local mips state changed, %s, %s', group_id, state)
|
_LOGGER.info('local mips state changed, %s, %s', group_id, state)
|
||||||
mips = self._mips_local.get(group_id, None)
|
mips = self._mips_local.get(group_id, None)
|
||||||
if not mips:
|
if not mips:
|
||||||
_LOGGER.error(
|
_LOGGER.info(
|
||||||
'local mips state changed, mips not exist, %s', group_id)
|
'local mips state changed, mips not exist, %s', group_id)
|
||||||
|
# The connection to the central hub gateway is definitely broken.
|
||||||
|
self.__show_central_state_changed_notify(False)
|
||||||
return
|
return
|
||||||
if state:
|
if state:
|
||||||
# Connected
|
# Connected
|
||||||
@ -1144,6 +1163,7 @@ class MIoTClient:
|
|||||||
if sub and sub.handler:
|
if sub and sub.handler:
|
||||||
sub.handler(did, MIoTDeviceState.OFFLINE, sub.handler_ctx)
|
sub.handler(did, MIoTDeviceState.OFFLINE, sub.handler_ctx)
|
||||||
self.__request_show_devices_changed_notify()
|
self.__request_show_devices_changed_notify()
|
||||||
|
self.__show_central_state_changed_notify(state)
|
||||||
|
|
||||||
@final
|
@final
|
||||||
async def __on_miot_lan_state_change(self, state: bool) -> None:
|
async def __on_miot_lan_state_change(self, state: bool) -> None:
|
||||||
@ -1486,8 +1506,6 @@ class MIoTClient:
|
|||||||
if did not in filter_dids:
|
if did not in filter_dids:
|
||||||
continue
|
continue
|
||||||
device_old = 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
|
gw_state_new: bool = False
|
||||||
device_new = gw_list.pop(did, None)
|
device_new = gw_list.pop(did, None)
|
||||||
if device_new:
|
if device_new:
|
||||||
@ -1501,7 +1519,7 @@ class MIoTClient:
|
|||||||
device_old['online'] = False
|
device_old['online'] = False
|
||||||
# Update cache group_id
|
# Update cache group_id
|
||||||
info['group_id'] = group_id
|
info['group_id'] = group_id
|
||||||
if gw_state_old == gw_state_new:
|
if not gw_state_new:
|
||||||
continue
|
continue
|
||||||
self.__update_device_msg_sub(did=did)
|
self.__update_device_msg_sub(did=did)
|
||||||
state_old: Optional[bool] = info.get('online', None)
|
state_old: Optional[bool] = info.get('online', None)
|
||||||
@ -1911,6 +1929,23 @@ class MIoTClient:
|
|||||||
self._show_devices_changed_notify_timer = self._main_loop.call_later(
|
self._show_devices_changed_notify_timer = self._main_loop.call_later(
|
||||||
delay_sec, self.__show_devices_changed_notify)
|
delay_sec, self.__show_devices_changed_notify)
|
||||||
|
|
||||||
|
@final
|
||||||
|
def __show_central_state_changed_notify(self, connected: bool) -> None:
|
||||||
|
conn_status: str = (
|
||||||
|
self._i18n.translate('miot.client.central_state_connected')
|
||||||
|
if connected else
|
||||||
|
self._i18n.translate('miot.client.central_state_disconnected'))
|
||||||
|
self._persistence_notify(
|
||||||
|
self.__gen_notify_key('central_state_changed'),
|
||||||
|
self._i18n.translate('miot.client.central_state_changed_title'),
|
||||||
|
self._i18n.translate(key='miot.client.central_state_changed',
|
||||||
|
replace={
|
||||||
|
'nick_name': self._entry_data.get(
|
||||||
|
'nick_name', DEFAULT_NICK_NAME),
|
||||||
|
'uid': self._uid,
|
||||||
|
'cloud_server': self._cloud_server,
|
||||||
|
'conn_status': conn_status
|
||||||
|
}))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_miot_instance_async(
|
async def get_miot_instance_async(
|
||||||
|
@ -543,6 +543,7 @@ class MIoTHttpClient:
|
|||||||
req_data: dict = {
|
req_data: dict = {
|
||||||
'limit': 200,
|
'limit': 200,
|
||||||
'get_split_device': True,
|
'get_split_device': True,
|
||||||
|
'get_third_device': True,
|
||||||
'dids': dids
|
'dids': dids
|
||||||
}
|
}
|
||||||
if start_did:
|
if start_did:
|
||||||
|
@ -1108,6 +1108,8 @@ class MIoTServiceEntity(Entity):
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
value: Any = prop.value_format(params['value'])
|
value: Any = prop.value_format(params['value'])
|
||||||
|
value = prop.eval_expr(value)
|
||||||
|
value = prop.value_format(value)
|
||||||
self._prop_value_map[prop] = value
|
self._prop_value_map[prop] = value
|
||||||
if prop in self._prop_changed_subs:
|
if prop in self._prop_changed_subs:
|
||||||
self._prop_changed_subs[prop](prop, value)
|
self._prop_changed_subs[prop](prop, value)
|
||||||
@ -1279,8 +1281,9 @@ class MIoTPropertyEntity(Entity):
|
|||||||
|
|
||||||
def __on_value_changed(self, params: dict, ctx: Any) -> None:
|
def __on_value_changed(self, params: dict, ctx: Any) -> None:
|
||||||
_LOGGER.debug('property changed, %s', params)
|
_LOGGER.debug('property changed, %s', params)
|
||||||
self._value = self.spec.value_format(params['value'])
|
value: Any = self.spec.value_format(params['value'])
|
||||||
self._value = self.spec.eval_expr(self._value)
|
value = self.spec.eval_expr(value)
|
||||||
|
self._value = self.spec.value_format(value)
|
||||||
if not self._pending_write_ha_state_timer:
|
if not self._pending_write_ha_state_timer:
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@ -110,7 +110,6 @@ class MipsServiceData:
|
|||||||
version=IPVersion.V4Only)
|
version=IPVersion.V4Only)
|
||||||
if not self.addresses:
|
if not self.addresses:
|
||||||
raise MipsServiceError('invalid addresses')
|
raise MipsServiceError('invalid addresses')
|
||||||
self.addresses.sort()
|
|
||||||
if not service_info.port:
|
if not service_info.port:
|
||||||
raise MipsServiceError('invalid port')
|
raise MipsServiceError('invalid port')
|
||||||
self.port = service_info.port
|
self.port = service_info.port
|
||||||
@ -226,7 +225,7 @@ class MipsService:
|
|||||||
state_change: ServiceStateChange
|
state_change: ServiceStateChange
|
||||||
) -> None:
|
) -> None:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
'mips service state changed, %s, %s, %s',
|
'mdns discovery changed, %s, %s, %s',
|
||||||
state_change, name, service_type)
|
state_change, name, service_type)
|
||||||
|
|
||||||
if state_change is ServiceStateChange.Removed:
|
if state_change is ServiceStateChange.Removed:
|
||||||
|
@ -513,6 +513,7 @@ class _MipsClient(ABC):
|
|||||||
"""
|
"""
|
||||||
self.__thread_check()
|
self.__thread_check()
|
||||||
if not self._mqtt or not self._mqtt.is_connected():
|
if not self._mqtt or not self._mqtt.is_connected():
|
||||||
|
self.log_error(f'mips sub when not connected, {topic}')
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
if topic not in self._mips_sub_pending_map:
|
if topic not in self._mips_sub_pending_map:
|
||||||
@ -531,6 +532,7 @@ class _MipsClient(ABC):
|
|||||||
"""
|
"""
|
||||||
self.__thread_check()
|
self.__thread_check()
|
||||||
if not self._mqtt or not self._mqtt.is_connected():
|
if not self._mqtt or not self._mqtt.is_connected():
|
||||||
|
self.log_debug(f'mips unsub when not connected, {topic}')
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
result, mid = self._mqtt.unsubscribe(topic=topic)
|
result, mid = self._mqtt.unsubscribe(topic=topic)
|
||||||
@ -639,6 +641,7 @@ class _MipsClient(ABC):
|
|||||||
_LOGGER.error('__on_connect, but mqtt is None')
|
_LOGGER.error('__on_connect, but mqtt is None')
|
||||||
return
|
return
|
||||||
if not self._mqtt.is_connected():
|
if not self._mqtt.is_connected():
|
||||||
|
_LOGGER.error('__on_connect, but mqtt is disconnected')
|
||||||
return
|
return
|
||||||
self.log_info(f'mips connect, {flags}, {rc}, {props}')
|
self.log_info(f'mips connect, {flags}, {rc}, {props}')
|
||||||
self.__reset_reconnect_time()
|
self.__reset_reconnect_time()
|
||||||
@ -994,6 +997,11 @@ class MipsCloudClient(_MipsClient):
|
|||||||
handler(
|
handler(
|
||||||
did, MIoTDeviceState.ONLINE if msg['event'] == 'online'
|
did, MIoTDeviceState.ONLINE if msg['event'] == 'online'
|
||||||
else MIoTDeviceState.OFFLINE, ctx)
|
else MIoTDeviceState.OFFLINE, ctx)
|
||||||
|
|
||||||
|
if did.startswith('blt.'):
|
||||||
|
# MIoT cloud may not publish BLE device online/offline state message.
|
||||||
|
# Do not subscribe BLE device online/offline state.
|
||||||
|
return True
|
||||||
return self.__reg_broadcast_external(
|
return self.__reg_broadcast_external(
|
||||||
topic=topic, handler=on_state_msg, handler_ctx=handler_ctx)
|
topic=topic, handler=on_state_msg, handler_ctx=handler_ctx)
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -48,3 +48,9 @@ urn:miot-spec-v2:device:thermostat:0000A031:tofan-wk01:
|
|||||||
services:
|
services:
|
||||||
- '2'
|
- '2'
|
||||||
- '4'
|
- '4'
|
||||||
|
urn:miot-spec-v2:device:vacuum:0000A006:narwa-001:
|
||||||
|
services:
|
||||||
|
- '*'
|
||||||
|
urn:miot-spec-v2:device:vacuum:0000A006:narwa-ax11:
|
||||||
|
services:
|
||||||
|
- '*'
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
urn:miot-spec-v2:device:air-condition-outlet:0000A045:lumi-mcn04:1:
|
urn:miot-spec-v2:device:air-condition-outlet:0000A045:lumi-mcn04:1:
|
||||||
prop.3.4:
|
prop.3.4:
|
||||||
format: uint8
|
format: uint8
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:1:
|
||||||
|
prop.10.6:
|
||||||
|
unit: none
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:1
|
||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1:
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1:
|
||||||
prop.10.6:
|
prop.10.6:
|
||||||
unit: none
|
unit: none
|
||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1
|
||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:1:
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:1:
|
||||||
|
prop.8.6:
|
||||||
|
unit: kWh
|
||||||
prop.10.6:
|
prop.10.6:
|
||||||
unit: none
|
unit: none
|
||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:1
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c24:1
|
||||||
@ -13,6 +19,19 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1:
|
|||||||
prop.10.6:
|
prop.10.6:
|
||||||
unit: none
|
unit: none
|
||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h40h00:1:
|
||||||
|
prop.10.6:
|
||||||
|
unit: none
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:1:
|
||||||
|
prop.10.6:
|
||||||
|
unit: none
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:1
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1:
|
||||||
|
prop.10.6:
|
||||||
|
unit: none
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1
|
||||||
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: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: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:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
|
||||||
@ -26,6 +45,18 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1:
|
|||||||
prop.10.6:
|
prop.10.6:
|
||||||
unit: none
|
unit: none
|
||||||
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1:
|
||||||
|
prop.10.6:
|
||||||
|
unit: none
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1
|
||||||
|
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1
|
||||||
|
urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgd1st:1:
|
||||||
|
prop.3.7:
|
||||||
|
value-range:
|
||||||
|
- -30
|
||||||
|
- 100
|
||||||
|
- 0.1
|
||||||
urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-s1:1:
|
urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-s1:1:
|
||||||
prop.2.5:
|
prop.2.5:
|
||||||
name: voc-density
|
name: voc-density
|
||||||
@ -78,6 +109,13 @@ urn:miot-spec-v2:device:bath-heater:0000A028:xiaomi-s1:1:
|
|||||||
urn:miot-spec-v2:device:curtain:0000A00C:bjkcz-kczble:1:0000D031:
|
urn:miot-spec-v2:device:curtain:0000A00C:bjkcz-kczble:1:0000D031:
|
||||||
prop.2.2:
|
prop.2.2:
|
||||||
name: status-a
|
name: status-a
|
||||||
|
urn:miot-spec-v2:device:electronic-valve:0000A0A7:sanmei-s1:1:
|
||||||
|
prop.3.1:
|
||||||
|
expr: round(src_value/100, 2)
|
||||||
|
prop.3.2:
|
||||||
|
expr: round(src_value/100, 2)
|
||||||
|
prop.3.3:
|
||||||
|
expr: round(src_value/10, 1)
|
||||||
urn:miot-spec-v2:device:fan:0000A005:dmaker-p33:1:
|
urn:miot-spec-v2:device:fan:0000A005:dmaker-p33:1:
|
||||||
prop.2.2:
|
prop.2.2:
|
||||||
name: fan-level-a
|
name: fan-level-a
|
||||||
@ -93,12 +131,21 @@ urn:miot-spec-v2:device:fan:0000A005:dmaker-p5:1:
|
|||||||
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p43:1:
|
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p43:1:
|
||||||
prop.2.2:
|
prop.2.2:
|
||||||
name: fan-level-a
|
name: fan-level-a
|
||||||
|
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p45:1:0000D062:
|
||||||
|
prop.2.4:
|
||||||
|
name: fan-level-a
|
||||||
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1:
|
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1:
|
||||||
prop.2.2:
|
prop.2.2:
|
||||||
name: fan-level-a
|
name: fan-level-a
|
||||||
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p69:1:0000D062:
|
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p69:1:0000D062:
|
||||||
prop.2.4:
|
prop.2.4:
|
||||||
name: fan-level-a
|
name: fan-level-a
|
||||||
|
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p70:1:0000D062:
|
||||||
|
prop.2.4:
|
||||||
|
name: fan-level-a
|
||||||
|
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p76:1:0000D062:
|
||||||
|
prop.2.4:
|
||||||
|
name: fan-level-a
|
||||||
urn:miot-spec-v2:device:fan:0000A005:zhimi-sa1:3:
|
urn:miot-spec-v2:device:fan:0000A005:zhimi-sa1:3:
|
||||||
prop.2.2:
|
prop.2.2:
|
||||||
name: fan-level-a
|
name: fan-level-a
|
||||||
@ -144,6 +191,10 @@ urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:2: urn:miot-spec-v2:device:hood
|
|||||||
urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:3:
|
urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:3:
|
||||||
prop.3.1:
|
prop.3.1:
|
||||||
name: on-ventilation
|
name: on-ventilation
|
||||||
|
urn:miot-spec-v2:device:humidifier:0000A00E:zhimi-ca4:2:
|
||||||
|
prop.2.7:
|
||||||
|
unit: percentage
|
||||||
|
expr: round(src_value*0.83)
|
||||||
urn:miot-spec-v2:device:kettle:0000A009:yunmi-r3:1:
|
urn:miot-spec-v2:device:kettle:0000A009:yunmi-r3:1:
|
||||||
prop.3.1:
|
prop.3.1:
|
||||||
unit: ppm
|
unit: ppm
|
||||||
@ -153,6 +204,9 @@ urn:miot-spec-v2:device:light:0000A001:shhf-sfla10:1:
|
|||||||
urn:miot-spec-v2:device:light:0000A001:shhf-sfla12:1:
|
urn:miot-spec-v2:device:light:0000A001:shhf-sfla12:1:
|
||||||
prop.8.11:
|
prop.8.11:
|
||||||
name: on-a
|
name: on-a
|
||||||
|
urn:miot-spec-v2:device:light:0000A001:shhf-sflt11:1:0000C802:
|
||||||
|
prop.11.14:
|
||||||
|
name: on-power
|
||||||
urn:miot-spec-v2:device:magnet-sensor:0000A016:linp-m1:1:
|
urn:miot-spec-v2:device:magnet-sensor:0000A016:linp-m1:1:
|
||||||
prop.2.1004:
|
prop.2.1004:
|
||||||
name: contact-state
|
name: contact-state
|
||||||
@ -161,13 +215,16 @@ urn:miot-spec-v2:device:magnet-sensor:0000A016:linp-m1:1:
|
|||||||
description: open
|
description: open
|
||||||
- value: 1
|
- value: 1
|
||||||
description: closed
|
description: closed
|
||||||
expr: src_value!=1
|
expr: (src_value!=1)
|
||||||
urn:miot-spec-v2:device:motion-sensor:0000A014:lumi-acn001:1:
|
urn:miot-spec-v2:device:motion-sensor:0000A014:lumi-acn001:1:
|
||||||
prop.3.2:
|
prop.3.2:
|
||||||
access:
|
access:
|
||||||
- read
|
- read
|
||||||
- notify
|
- notify
|
||||||
unit: mV
|
unit: mV
|
||||||
|
urn:miot-spec-v2:device:motor-controller:0000A01D:adp-adswb4:1:0000C837:
|
||||||
|
prop.2.1:
|
||||||
|
name: motor-switch
|
||||||
urn:miot-spec-v2:device:occupancy-sensor:0000A0BF:ainice-3b:1: urn:miot-spec-v2:device:occupancy-sensor:0000A0BF:ainice-3b:2
|
urn:miot-spec-v2:device:occupancy-sensor:0000A0BF:ainice-3b:1: urn:miot-spec-v2:device:occupancy-sensor:0000A0BF:ainice-3b:2
|
||||||
urn:miot-spec-v2:device:occupancy-sensor:0000A0BF:ainice-3b:2:
|
urn:miot-spec-v2:device:occupancy-sensor:0000A0BF:ainice-3b:2:
|
||||||
prop.2.8:
|
prop.2.8:
|
||||||
@ -199,6 +256,9 @@ urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2:
|
|||||||
unit: mA
|
unit: mA
|
||||||
prop.3.2:
|
prop.3.2:
|
||||||
expr: round(src_value/10, 1)
|
expr: round(src_value/10, 1)
|
||||||
|
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2d:1:
|
||||||
|
prop.3.2:
|
||||||
|
expr: round(src_value/1000, 2)
|
||||||
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1:
|
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1:
|
||||||
prop.11.1:
|
prop.11.1:
|
||||||
name: power-consumption
|
name: power-consumption
|
||||||
@ -243,7 +303,7 @@ urn:miot-spec-v2:device:router:0000A036:xiaomi-rd08:1:
|
|||||||
urn:miot-spec-v2:device:safe-box:0000A042:loock-v1:1:
|
urn:miot-spec-v2:device:safe-box:0000A042:loock-v1:1:
|
||||||
prop.5.1:
|
prop.5.1:
|
||||||
name: contact-state
|
name: contact-state
|
||||||
expr: src_value!=1
|
expr: (src_value!=1)
|
||||||
urn:miot-spec-v2:device:switch:0000A003:090615-x1tpm:1:0000D042:
|
urn:miot-spec-v2:device:switch:0000A003:090615-x1tpm:1:0000D042:
|
||||||
prop.27.3:
|
prop.27.3:
|
||||||
name: light-on
|
name: light-on
|
||||||
|
@ -50,18 +50,11 @@ from homeassistant.components.sensor import SensorStateClass
|
|||||||
from homeassistant.components.event import EventDeviceClass
|
from homeassistant.components.event import EventDeviceClass
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
EntityCategory, LIGHT_LUX, UnitOfEnergy,
|
||||||
EntityCategory,
|
UnitOfPower, UnitOfElectricCurrent,
|
||||||
LIGHT_LUX,
|
UnitOfElectricPotential, UnitOfTemperature,
|
||||||
UnitOfEnergy,
|
UnitOfPressure, PERCENTAGE)
|
||||||
UnitOfPower,
|
|
||||||
UnitOfElectricCurrent,
|
|
||||||
UnitOfElectricPotential,
|
|
||||||
UnitOfTemperature,
|
|
||||||
UnitOfPressure,
|
|
||||||
PERCENTAGE
|
|
||||||
)
|
|
||||||
|
|
||||||
# pylint: disable=pointless-string-statement
|
# pylint: disable=pointless-string-statement
|
||||||
"""SPEC_DEVICE_TRANS_MAP
|
"""SPEC_DEVICE_TRANS_MAP
|
||||||
@ -107,7 +100,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'humidifier': {
|
'humidifier': {
|
||||||
'required': {
|
'required': {
|
||||||
'humidifier': {
|
'humidifier': {
|
||||||
'required': {
|
'required': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'on': {'read', 'write'}
|
'on': {'read', 'write'}
|
||||||
}
|
}
|
||||||
@ -119,7 +112,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
'environment': {
|
'environment': {
|
||||||
'required': {
|
'required': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'relative-humidity': {'read'}
|
'relative-humidity': {'read'}
|
||||||
}
|
}
|
||||||
@ -131,7 +124,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'dehumidifier': {
|
'dehumidifier': {
|
||||||
'required': {
|
'required': {
|
||||||
'dehumidifier': {
|
'dehumidifier': {
|
||||||
'required': {
|
'required': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'on': {'read', 'write'}
|
'on': {'read', 'write'}
|
||||||
}
|
}
|
||||||
@ -143,7 +136,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
'environment': {
|
'environment': {
|
||||||
'required': {
|
'required': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'relative-humidity': {'read'}
|
'relative-humidity': {'read'}
|
||||||
}
|
}
|
||||||
@ -155,15 +148,13 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'vacuum': {
|
'vacuum': {
|
||||||
'required': {
|
'required': {
|
||||||
'vacuum': {
|
'vacuum': {
|
||||||
'required': {
|
'required': {
|
||||||
'actions': {'start-sweep', 'stop-sweeping'},
|
'actions': {'start-sweep', 'stop-sweeping'},
|
||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
'properties': {'status', 'fan-level'},
|
'properties': {'status', 'fan-level'},
|
||||||
'actions': {
|
'actions': {
|
||||||
'pause-sweeping',
|
'pause-sweeping', 'continue-sweep', 'stop-and-gocharge'
|
||||||
'continue-sweep',
|
|
||||||
'stop-and-gocharge'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,7 +162,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'optional': {
|
'optional': {
|
||||||
'identify': {
|
'identify': {
|
||||||
'required': {
|
'required': {
|
||||||
'actions': {'identify'}
|
'actions': {'identify'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'battery': {
|
'battery': {
|
||||||
@ -179,6 +170,11 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'properties': {
|
'properties': {
|
||||||
'battery-level': {'read'}
|
'battery-level': {'read'}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'actions': {
|
||||||
|
'start-charge'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -204,10 +200,9 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'required': {},
|
'required': {},
|
||||||
'optional': {
|
'optional': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'on',
|
'on', 'fan-level', 'horizontal-swing', 'vertical-swing'
|
||||||
'fan-level',
|
}
|
||||||
'horizontal-swing',
|
}
|
||||||
'vertical-swing'}}
|
|
||||||
},
|
},
|
||||||
'environment': {
|
'environment': {
|
||||||
'required': {},
|
'required': {},
|
||||||
@ -235,8 +230,8 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'target-temperature', 'mode', 'fan-level',
|
'target-temperature', 'mode', 'fan-level', 'temperature'
|
||||||
'temperature'}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -278,7 +273,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'ptc-bath-heater': {
|
'ptc-bath-heater': {
|
||||||
'required': {
|
'required': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'mode':{'read', 'write'}
|
'mode': {'read', 'write'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
@ -321,6 +316,89 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
|||||||
'optional': {},
|
'optional': {},
|
||||||
'entity': 'electric-blanket'
|
'entity': 'electric-blanket'
|
||||||
},
|
},
|
||||||
|
'speaker': {
|
||||||
|
'required': {
|
||||||
|
'speaker': {
|
||||||
|
'required': {
|
||||||
|
'properties': {
|
||||||
|
'volume': {'read', 'write'}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'properties': {'mute'}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'play-control': {
|
||||||
|
'required': {
|
||||||
|
'actions': {'play'}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'properties': {'playing-state'},
|
||||||
|
'actions': {'pause', 'stop', 'next', 'previous'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'optional': {},
|
||||||
|
'entity': 'wifi-speaker'
|
||||||
|
},
|
||||||
|
'television': {
|
||||||
|
'required': {
|
||||||
|
'speaker': {
|
||||||
|
'required': {
|
||||||
|
'properties': {
|
||||||
|
'volume': {'read', 'write'}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'properties': {'mute'}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'television': {
|
||||||
|
'required': {
|
||||||
|
'actions': {'turn-off'}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'properties': {'input-control'},
|
||||||
|
'actions': {'turn-on'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'play-control': {
|
||||||
|
'required': {},
|
||||||
|
'optional': {
|
||||||
|
'properties': {'playing-state'},
|
||||||
|
'actions': {'play', 'pause', 'stop', 'next', 'previous'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'entity': 'television'
|
||||||
|
},
|
||||||
|
'watch': {
|
||||||
|
'required': {
|
||||||
|
'watch': {
|
||||||
|
'required': {
|
||||||
|
'properties': {
|
||||||
|
'longitude': {'read'},
|
||||||
|
'latitude': {'read'}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'properties': {'area-id'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'optional': {
|
||||||
|
'battery': {
|
||||||
|
'required': {
|
||||||
|
'properties': {
|
||||||
|
'battery-level': {'read'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'entity': 'device_tracker'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"""SPEC_SERVICE_TRANS_MAP
|
"""SPEC_SERVICE_TRANS_MAP
|
||||||
@ -351,9 +429,7 @@ SPEC_SERVICE_TRANS_MAP: dict = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
'properties': {
|
'properties': {'mode', 'brightness', 'color', 'color-temperature'}
|
||||||
'mode', 'brightness', 'color', 'color-temperature'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'entity': 'light'
|
'entity': 'light'
|
||||||
},
|
},
|
||||||
@ -368,7 +444,8 @@ SPEC_SERVICE_TRANS_MAP: dict = {
|
|||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'mode', 'brightness',
|
'mode',
|
||||||
|
'brightness',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'entity': 'light',
|
'entity': 'light',
|
||||||
@ -401,16 +478,14 @@ SPEC_SERVICE_TRANS_MAP: dict = {
|
|||||||
},
|
},
|
||||||
'entity': 'water_heater'
|
'entity': 'water_heater'
|
||||||
},
|
},
|
||||||
'curtain': {
|
'curtain': {
|
||||||
'required': {
|
'required': {
|
||||||
'properties': {
|
'properties': {
|
||||||
'motor-control': {'write'}
|
'motor-control': {'write'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'optional': {
|
'optional': {
|
||||||
'properties': {
|
'properties': {'status', 'current-position', 'target-position'}
|
||||||
'status', 'current-position', 'target-position'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'entity': 'cover'
|
'entity': 'cover'
|
||||||
},
|
},
|
||||||
@ -600,6 +675,4 @@ SPEC_EVENT_TRANS_MAP: dict[str, str] = {
|
|||||||
'doorbell-ring': EventDeviceClass.DOORBELL
|
'doorbell-ring': EventDeviceClass.DOORBELL
|
||||||
}
|
}
|
||||||
|
|
||||||
SPEC_ACTION_TRANS_MAP = {
|
SPEC_ACTION_TRANS_MAP = {}
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"step": {
|
"step": {
|
||||||
"eula": {
|
"eula": {
|
||||||
"title": "Risikohinweis",
|
"title": "Risikohinweis",
|
||||||
"description": "1. Ihre **Xiaomi-Benutzerinformationen und Geräteinformationen** werden in Ihrem Home Assistant-System gespeichert. **Xiaomi kann die Sicherheit des Home Assistant-Speichermechanismus nicht garantieren**. Sie sind dafür verantwortlich, Ihre Informationen vor Diebstahl zu schützen.\r\n2. Diese Integration wird von der Open-Source-Community unterstützt und gewartet. Es können jedoch Stabilitätsprobleme oder andere Probleme auftreten. Wenn Sie auf ein Problem stoßen, das mit dieser Integration zusammenhängt, sollten Sie **die Open-Source-Community um Hilfe bitten, anstatt sich an den Xiaomi Home Kundendienst zu wenden**.\r\n3. Sie benötigen bestimmte technische Fähigkeiten, um Ihre lokale Laufzeitumgebung zu warten. Diese Integration ist für Anfänger nicht geeignet. \r\n4. Bevor Sie diese Integration verwenden, lesen Sie bitte die **README-Datei sorgfältig durch**.\r\n5. Um eine stabile Nutzung der Integration zu gewährleisten und Missbrauch der Schnittstelle zu verhindern, **darf diese Integration nur in Home Assistant verwendet werden. Weitere Informationen finden Sie in der LICENSE**.",
|
"description": "1. Ihre **Xiaomi-Benutzerinformationen und Geräteinformationen** werden in Ihrem Home Assistant-System gespeichert. **Xiaomi kann die Sicherheit des Home Assistant-Speichermechanismus nicht garantieren**. Sie sind dafür verantwortlich, Ihre Informationen vor Diebstahl zu schützen.\r\n2. Diese Integration wird von der Open-Source-Community unterstützt und gewartet. Es können jedoch Stabilitätsprobleme oder andere Probleme auftreten. Wenn Sie auf ein Problem stoßen, das mit dieser Integration zusammenhängt, sollten Sie **die Open-Source-Community um Hilfe bitten, anstatt sich an den Xiaomi Home Kundendienst zu wenden**.\r\n3. Sie benötigen bestimmte technische Fähigkeiten, um Ihre lokale Laufzeitumgebung zu warten. Diese Integration ist für Anfänger nicht geeignet.\r\n4. Bevor Sie diese Integration verwenden, lesen Sie bitte die **README-Datei sorgfältig durch**.\r\n5. Um eine stabile Nutzung der Integration zu gewährleisten und Missbrauch der Schnittstelle zu verhindern, **darf diese Integration nur in Home Assistant verwendet werden. Weitere Informationen finden Sie in der LICENSE**.",
|
||||||
"data": {
|
"data": {
|
||||||
"eula": "Ich habe das oben genannte Risiko zur Kenntnis genommen und übernehme freiwillig die damit verbundenen Risiken durch die Verwendung der Integration."
|
"eula": "Ich habe das oben genannte Risiko zur Kenntnis genommen und übernehme freiwillig die damit verbundenen Risiken durch die Verwendung der Integration."
|
||||||
}
|
}
|
||||||
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Gerätestatusänderungen anzeigen",
|
"display_devices_changed_notify": "Gerätestatusänderungen anzeigen",
|
||||||
"update_trans_rules": "Entitätskonvertierungsregeln aktualisieren",
|
"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"
|
"network_detect_config": "Integrierte Netzwerkkonfiguration",
|
||||||
|
"cover_dead_zone_width": "Die Breite des toten Winkels des Vorhangs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Bestätigen Sie die Konfiguration",
|
"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",
|
"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\nDie Breite des toten Winkels des Vorhangs:\t{cover_width_new}\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",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Änderungen bestätigen"
|
"confirm": "Änderungen bestätigen"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Display device status change notifications",
|
"display_devices_changed_notify": "Display device status change notifications",
|
||||||
"update_trans_rules": "Update entity conversion rules",
|
"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"
|
"network_detect_config": "Integrated network configuration",
|
||||||
|
"cover_dead_zone_width": "Cover dead zone width"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Confirm Configuration",
|
"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",
|
"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\nCover dead zone width:\t{cover_width_new}\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",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Confirm the change"
|
"confirm": "Confirm the change"
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"step": {
|
"step": {
|
||||||
"eula": {
|
"eula": {
|
||||||
"title": "Aviso de riesgo",
|
"title": "Aviso de riesgo",
|
||||||
"description": "1. Su **información de usuario de Xiaomi e información del dispositivo** se almacenará en su sistema Home Assistant. **Xiaomi no puede garantizar la seguridad del mecanismo de almacenamiento de Home Assistant**. Usted es responsable de evitar que su información sea robada.\r\n2. Esta integración es mantenida por la comunidad de código abierto y puede haber problemas de estabilidad u otros problemas. Cuando tenga problemas relacionados con el uso de esta integración, **busque ayuda en la comunidad de código abierto en lugar de contactar al servicio al cliente de Xiaomi**.\r\n3. Es necesario tener ciertas habilidades técnicas para mantener su entorno de ejecución local, esta integración no es amigable para los usuarios novatos.\r\n4. Antes de utilizar esta integración, por favor **lea detenidamente el archivo README**. \r\n5. Para garantizar el uso estable de la integración y prevenir el abuso de la interfaz, **esta integración solo está permitida en Home Assistant. Para más detalles, consulte la LICENSE**.",
|
"description": "1. Su **información de usuario de Xiaomi e información del dispositivo** se almacenará en su sistema Home Assistant. **Xiaomi no puede garantizar la seguridad del mecanismo de almacenamiento de Home Assistant**. Usted es responsable de evitar que su información sea robada.\r\n2. Esta integración es mantenida por la comunidad de código abierto y puede haber problemas de estabilidad u otros problemas. Cuando tenga problemas relacionados con el uso de esta integración, **busque ayuda en la comunidad de código abierto en lugar de contactar al servicio al cliente de Xiaomi**.\r\n3. Es necesario tener ciertas habilidades técnicas para mantener su entorno de ejecución local, esta integración no es amigable para los usuarios novatos.\r\n4. Antes de utilizar esta integración, por favor **lea detenidamente el archivo README**.\r\n5. Para garantizar el uso estable de la integración y prevenir el abuso de la interfaz, **esta integración solo está permitida en Home Assistant. Para más detalles, consulte la LICENSE**.",
|
||||||
"data": {
|
"data": {
|
||||||
"eula": "He leído y entiendo los riesgos anteriores, y estoy dispuesto a asumir cualquier riesgo relacionado con el uso de esta integración."
|
"eula": "He leído y entiendo los riesgos anteriores, y estoy dispuesto a asumir cualquier riesgo relacionado con el uso de esta integración."
|
||||||
}
|
}
|
||||||
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Mostrar notificaciones de cambio de estado del dispositivo",
|
"display_devices_changed_notify": "Mostrar notificaciones de cambio de estado del dispositivo",
|
||||||
"update_trans_rules": "Actualizar reglas de conversión de entidad",
|
"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"
|
"network_detect_config": "Configuración de Red Integrada",
|
||||||
|
"cover_dead_zone_width": "Anchura del punto ciego de la cortina"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Confirmar configuración",
|
"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",
|
"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\nAnchura del punto ciego de la cortina:\t{cover_width_new}\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",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Confirmar modificación"
|
"confirm": "Confirmar modificación"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Afficher les notifications de changement d'état de l'appareil",
|
"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_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"
|
"network_detect_config": "Configuration Réseau Intégrée",
|
||||||
|
"cover_dead_zone_width": "Largeur de la zone aveugle du rideau"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Confirmer la configuration",
|
"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",
|
"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\nLargeur de la zone aveugle du rideau:\t{cover_width_new}\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",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Confirmer la modification"
|
"confirm": "Confirmer la modification"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Mostra notifiche di cambio stato del dispositivo",
|
"display_devices_changed_notify": "Mostra notifiche di cambio stato del dispositivo",
|
||||||
"update_trans_rules": "Aggiorna le regole di conversione delle entità",
|
"update_trans_rules": "Aggiorna le regole di conversione delle entità",
|
||||||
"update_lan_ctrl_config": "Aggiorna configurazione del controllo LAN",
|
"update_lan_ctrl_config": "Aggiorna configurazione del controllo LAN",
|
||||||
"network_detect_config": "Configurazione di Rete Integrata"
|
"network_detect_config": "Configurazione di Rete Integrata",
|
||||||
|
"cover_dead_zone_width": "Larghezza dell’angolo cieco della tenda"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Conferma Configurazione",
|
"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",
|
"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\nLarghezza dell’angolo cieco della tenda:\t{cover_width_new}\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": {
|
"data": {
|
||||||
"confirm": "Conferma la modifica"
|
"confirm": "Conferma la modifica"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "デバイスの状態変化通知を表示",
|
"display_devices_changed_notify": "デバイスの状態変化通知を表示",
|
||||||
"update_trans_rules": "エンティティ変換ルールを更新する",
|
"update_trans_rules": "エンティティ変換ルールを更新する",
|
||||||
"update_lan_ctrl_config": "LAN制御構成を更新する",
|
"update_lan_ctrl_config": "LAN制御構成を更新する",
|
||||||
"network_detect_config": "統合ネットワーク構成"
|
"network_detect_config": "統合ネットワーク構成",
|
||||||
|
"cover_dead_zone_width": "カーテンの死角幅"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "構成を確認する",
|
"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}** 個の規則",
|
"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{cover_width_new}\r\nデバイスの状態変化通知を表示:\t{display_devices_changed_notify}\r\nデバイス変更:\t追加 **{devices_add}** 個のデバイス、削除 **{devices_remove}** 個のデバイス\r\n変換ルール変更:\t合計 **{trans_rules_count}** 個の規則、更新 **{trans_rules_count_success}** 個の規則",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "変更を確認する"
|
"confirm": "変更を確認する"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Apparaatstatuswijzigingen weergeven",
|
"display_devices_changed_notify": "Apparaatstatuswijzigingen weergeven",
|
||||||
"update_trans_rules": "Werk entiteitsconversieregels bij",
|
"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"
|
"network_detect_config": "Geïntegreerde Netwerkconfiguratie",
|
||||||
|
"cover_dead_zone_width": "Breedte van de dode hoek van het gordijn"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Bevestig Configuratie",
|
"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",
|
"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\nBreedte van de dode hoek van het gordijn:\t{cover_width_new}\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",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Bevestig de wijziging"
|
"confirm": "Bevestig de wijziging"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
|
"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_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"
|
"network_detect_config": "Configuração de Rede Integrada",
|
||||||
|
"cover_dead_zone_width": "Largura da área cega da cortina"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Confirmar Configuração",
|
"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",
|
"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\nLargura da área cega da cortina:\t{cover_width_new}\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",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Confirmar a mudança"
|
"confirm": "Confirmar a mudança"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
|
"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_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"
|
"network_detect_config": "Configuração de Rede Integrada",
|
||||||
|
"cover_dead_zone_width": "Largura da zona cega da cortina"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Confirmar Configuração",
|
"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",
|
"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\nLargura da zona cega da cortina:\t{cover_width_new}\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",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Confirmar a alteração"
|
"confirm": "Confirmar a alteração"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "Отображать уведомления о изменении состояния устройства",
|
"display_devices_changed_notify": "Отображать уведомления о изменении состояния устройства",
|
||||||
"update_trans_rules": "Обновить правила преобразования сущностей",
|
"update_trans_rules": "Обновить правила преобразования сущностей",
|
||||||
"update_lan_ctrl_config": "Обновить конфигурацию управления LAN",
|
"update_lan_ctrl_config": "Обновить конфигурацию управления LAN",
|
||||||
"network_detect_config": "Интегрированная Сетевая Конфигурация"
|
"network_detect_config": "Интегрированная Сетевая Конфигурация",
|
||||||
|
"cover_dead_zone_width": "Ширина «мертвой зоны» шторы"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "Подтверждение настройки",
|
"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}** правил",
|
"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{cover_width_new}\r\nОтображать уведомления о изменении состояния устройства:\t{display_devices_changed_notify}\r\nИзменение устройства:\tДобавлено **{devices_add}** устройство, удалено **{devices_remove}** устройства\r\nИзменение правил преобразования:\tВсего **{trans_rules_count}** правил, обновлено **{trans_rules_count_success}** правил",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "Подтвердить изменения"
|
"confirm": "Подтвердить изменения"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "显示设备状态变化通知",
|
"display_devices_changed_notify": "显示设备状态变化通知",
|
||||||
"update_trans_rules": "更新实体转换规则",
|
"update_trans_rules": "更新实体转换规则",
|
||||||
"update_lan_ctrl_config": "更新局域网控制配置",
|
"update_lan_ctrl_config": "更新局域网控制配置",
|
||||||
"network_detect_config": "集成网络配置"
|
"network_detect_config": "集成网络配置",
|
||||||
|
"cover_dead_zone_width": "窗帘盲区宽度"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "确认配置",
|
"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}** 条规则",
|
"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{cover_width_new}\r\n显示设备状态变化通知:\t{display_devices_changed_notify}\r\n设备变化:\t新增 **{devices_add}** 个设备,移除 **{devices_remove}** 个设备\r\n转换规则变化:\t共条 **{trans_rules_count}** 规则,更新 **{trans_rules_count_success}** 条规则",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "确认修改"
|
"confirm": "确认修改"
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,8 @@
|
|||||||
"display_devices_changed_notify": "顯示設備狀態變化通知",
|
"display_devices_changed_notify": "顯示設備狀態變化通知",
|
||||||
"update_trans_rules": "更新實體轉換規則",
|
"update_trans_rules": "更新實體轉換規則",
|
||||||
"update_lan_ctrl_config": "更新局域網控制配置",
|
"update_lan_ctrl_config": "更新局域網控制配置",
|
||||||
"network_detect_config": "集成網絡配置"
|
"network_detect_config": "集成網絡配置",
|
||||||
|
"cover_dead_zone_width": "窗簾盲區寬度"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update_user_info": {
|
"update_user_info": {
|
||||||
@ -183,7 +184,7 @@
|
|||||||
},
|
},
|
||||||
"config_confirm": {
|
"config_confirm": {
|
||||||
"title": "確認配置",
|
"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}** 條規則",
|
"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{cover_width_new}\r\n顯示設備狀態變化通知:\t{display_devices_changed_notify}\r\n設備變化:\t新增 **{devices_add}** 個設備,移除 **{devices_remove}** 個設備\r\n轉換規則變化:\t共條 **{trans_rules_count}** 規則,更新 **{trans_rules_count_success}** 條規則",
|
||||||
"data": {
|
"data": {
|
||||||
"confirm": "確認修改"
|
"confirm": "確認修改"
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,12 @@ from .miot.const import DOMAIN
|
|||||||
from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData
|
from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData
|
||||||
from .miot.miot_spec import (MIoTSpecAction, MIoTSpecProperty)
|
from .miot.miot_spec import (MIoTSpecAction, MIoTSpecProperty)
|
||||||
|
|
||||||
|
try: # VacuumActivity is introduced in HA core 2025.1.0
|
||||||
|
from homeassistant.components.vacuum import VacuumActivity
|
||||||
|
HA_CORE_HAS_ACTIVITY = True
|
||||||
|
except ImportError:
|
||||||
|
HA_CORE_HAS_ACTIVITY = False
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -85,6 +91,11 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
_prop_status: Optional[MIoTSpecProperty]
|
_prop_status: Optional[MIoTSpecProperty]
|
||||||
_prop_fan_level: Optional[MIoTSpecProperty]
|
_prop_fan_level: Optional[MIoTSpecProperty]
|
||||||
_prop_battery_level: Optional[MIoTSpecProperty]
|
_prop_battery_level: Optional[MIoTSpecProperty]
|
||||||
|
_prop_status_cleaning: Optional[list[int]]
|
||||||
|
_prop_status_docked: Optional[list[int]]
|
||||||
|
_prop_status_paused: Optional[list[int]]
|
||||||
|
_prop_status_returning: Optional[list[int]]
|
||||||
|
_prop_status_error: Optional[list[int]]
|
||||||
|
|
||||||
_action_start_sweep: Optional[MIoTSpecAction]
|
_action_start_sweep: Optional[MIoTSpecAction]
|
||||||
_action_stop_sweeping: Optional[MIoTSpecAction]
|
_action_stop_sweeping: Optional[MIoTSpecAction]
|
||||||
@ -107,6 +118,11 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
self._prop_status = None
|
self._prop_status = None
|
||||||
self._prop_fan_level = None
|
self._prop_fan_level = None
|
||||||
self._prop_battery_level = None
|
self._prop_battery_level = None
|
||||||
|
self._prop_status_cleaning = []
|
||||||
|
self._prop_status_docked = []
|
||||||
|
self._prop_status_paused = []
|
||||||
|
self._prop_status_returning = []
|
||||||
|
self._prop_status_error = []
|
||||||
self._action_start_sweep = None
|
self._action_start_sweep = None
|
||||||
self._action_stop_sweeping = None
|
self._action_stop_sweeping = None
|
||||||
self._action_pause_sweeping = None
|
self._action_pause_sweeping = None
|
||||||
@ -126,6 +142,35 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
self._status_map = prop.value_list.to_map()
|
self._status_map = prop.value_list.to_map()
|
||||||
self._attr_supported_features |= VacuumEntityFeature.STATE
|
self._attr_supported_features |= VacuumEntityFeature.STATE
|
||||||
self._prop_status = prop
|
self._prop_status = prop
|
||||||
|
for item in prop.value_list.items:
|
||||||
|
item_str: str = item.name
|
||||||
|
item_name: str = re.sub(r'[^a-z]', '', item_str)
|
||||||
|
if item_name in {
|
||||||
|
'charging', 'charged', 'chargingcompleted',
|
||||||
|
'fullcharge', 'fullpower', 'findchargerpause',
|
||||||
|
'drying', 'washing', 'wash', 'inthewash',
|
||||||
|
'inthedry', 'stationworking', 'dustcollecting',
|
||||||
|
'upgrade', 'upgrading', 'updating'
|
||||||
|
}:
|
||||||
|
self._prop_status_docked.append(item.value)
|
||||||
|
elif item_name in {'paused', 'pause'}:
|
||||||
|
self._prop_status_paused.append(item.value)
|
||||||
|
elif item_name in {
|
||||||
|
'gocharging', 'cleancompletegocharging',
|
||||||
|
'findchargewash', 'backtowashmop', 'gowash',
|
||||||
|
'gowashing', 'summon'
|
||||||
|
}:
|
||||||
|
self._prop_status_returning.append(item.value)
|
||||||
|
elif item_name in {
|
||||||
|
'error', 'breakcharging', 'gochargebreak'
|
||||||
|
}:
|
||||||
|
self._prop_status_error.append(item.value)
|
||||||
|
elif (item_name.find('sweeping') != -1) or (
|
||||||
|
item_name.find('mopping') != -1) or (item_name in {
|
||||||
|
'cleaning', 'remoteclean', 'continuesweep',
|
||||||
|
'busy', 'building', 'buildingmap', 'mapping'
|
||||||
|
}):
|
||||||
|
self._prop_status_cleaning.append(item.value)
|
||||||
elif prop.name == 'fan-level':
|
elif prop.name == 'fan-level':
|
||||||
if not prop.value_list:
|
if not prop.value_list:
|
||||||
_LOGGER.error('invalid fan-level value_list, %s',
|
_LOGGER.error('invalid fan-level value_list, %s',
|
||||||
@ -158,18 +203,22 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
self._attr_supported_features |= VacuumEntityFeature.LOCATE
|
self._attr_supported_features |= VacuumEntityFeature.LOCATE
|
||||||
self._action_identify = action
|
self._action_identify = action
|
||||||
|
|
||||||
|
# Use start-charge from battery service as fallback
|
||||||
|
# if stop-and-gocharge is not available
|
||||||
|
if self._action_stop_and_gocharge is None:
|
||||||
|
for action in entity_data.actions:
|
||||||
|
if action.name == 'start-charge':
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
VacuumEntityFeature.RETURN_HOME)
|
||||||
|
self._action_stop_and_gocharge = action
|
||||||
|
break
|
||||||
|
|
||||||
async def async_start(self) -> None:
|
async def async_start(self) -> None:
|
||||||
"""Start or resume the cleaning task."""
|
"""Start or resume the cleaning task."""
|
||||||
try: # VacuumActivity is introduced in HA core 2025.1.0
|
if self._prop_status is not None:
|
||||||
# pylint: disable=import-outside-toplevel
|
status = self.get_prop_value(prop=self._prop_status)
|
||||||
from homeassistant.components.vacuum import VacuumActivity
|
if (status in self._prop_status_paused
|
||||||
if (self.activity
|
) and self._action_continue_sweep:
|
||||||
== VacuumActivity.PAUSED) and self._action_continue_sweep:
|
|
||||||
await self.action_async(action=self._action_continue_sweep)
|
|
||||||
return
|
|
||||||
except ImportError:
|
|
||||||
if self.state and (self.state in {'paused', 'pause'
|
|
||||||
}) and self._action_continue_sweep:
|
|
||||||
await self.action_async(action=self._action_continue_sweep)
|
await self.action_async(action=self._action_continue_sweep)
|
||||||
return
|
return
|
||||||
await self.action_async(action=self._action_start_sweep)
|
await self.action_async(action=self._action_start_sweep)
|
||||||
@ -203,9 +252,22 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
return self._device_name
|
return self._device_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self) -> Optional[str]:
|
def battery_level(self) -> Optional[int]:
|
||||||
"""Return the current state of the vacuum cleaner.
|
"""The current battery level of the vacuum cleaner."""
|
||||||
|
return self.get_prop_value(prop=self._prop_battery_level)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fan_speed(self) -> Optional[str]:
|
||||||
|
"""The current fan speed of the vacuum cleaner."""
|
||||||
|
return self.get_map_value(
|
||||||
|
map_=self._fan_level_map,
|
||||||
|
key=self.get_prop_value(prop=self._prop_fan_level))
|
||||||
|
|
||||||
|
if HA_CORE_HAS_ACTIVITY:
|
||||||
|
|
||||||
|
@property
|
||||||
|
def activity(self) -> Optional[str]:
|
||||||
|
"""The current vacuum activity.
|
||||||
To fix the HA warning below:
|
To fix the HA warning below:
|
||||||
Detected that custom integration 'xiaomi_home' is setting state
|
Detected that custom integration 'xiaomi_home' is setting state
|
||||||
directly.Entity XXX(<class 'custom_components.xiaomi_home.vacuum
|
directly.Entity XXX(<class 'custom_components.xiaomi_home.vacuum
|
||||||
@ -220,58 +282,27 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
|||||||
more constants, try get matching VacuumActivity enum first, return state
|
more constants, try get matching VacuumActivity enum first, return state
|
||||||
string as before if there is no match. In Home Assistant 2026.1, every
|
string as before if there is no match. In Home Assistant 2026.1, every
|
||||||
state should map to a VacuumActivity enum.
|
state should map to a VacuumActivity enum.
|
||||||
"""
|
"""
|
||||||
return self.activity
|
status = self.get_prop_value(prop=self._prop_status)
|
||||||
|
if status is None:
|
||||||
@property
|
return None
|
||||||
def activity(self) -> Optional[str]:
|
if status in self._prop_status_cleaning:
|
||||||
"""The current vacuum activity."""
|
|
||||||
status = self.get_prop_value(prop=self._prop_status)
|
|
||||||
if status is None:
|
|
||||||
return None
|
|
||||||
status_value = self.get_map_value(map_=self._status_map, key=status)
|
|
||||||
if status_value is None:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
from homeassistant.components.vacuum import VacuumActivity
|
|
||||||
status_value = status_value.lower()
|
|
||||||
status_str = re.sub(r'[^a-z]', '', status_value)
|
|
||||||
if status_str in {
|
|
||||||
'charging', 'charged', 'chargingcompleted', 'fullcharge',
|
|
||||||
'fullpower', 'findchargerpause', 'drying', 'washing',
|
|
||||||
'wash', 'inthewash', 'inthedry', 'stationworking',
|
|
||||||
'dustcollecting', 'upgrade', 'upgrading', 'updating'
|
|
||||||
}:
|
|
||||||
return VacuumActivity.DOCKED
|
|
||||||
if status_str in {'paused', 'pause'}:
|
|
||||||
return VacuumActivity.PAUSED
|
|
||||||
if status_str in {
|
|
||||||
'gocharging', 'cleancompletegocharging', 'findchargewash',
|
|
||||||
'backtowashmop', 'gowash', 'gowashing', 'summon'
|
|
||||||
}:
|
|
||||||
return VacuumActivity.RETURNING
|
|
||||||
if (status_str.find('sweeping')
|
|
||||||
!= -1) or (status_str.find('mopping')
|
|
||||||
!= -1) or (status_str in {
|
|
||||||
'cleaning', 'remoteclean', 'continuesweep',
|
|
||||||
'busy', 'building', 'buildingmap', 'mapping'
|
|
||||||
}):
|
|
||||||
return VacuumActivity.CLEANING
|
return VacuumActivity.CLEANING
|
||||||
if status_str in {'error', 'breakcharging', 'gochargebreak'}:
|
if status in self._prop_status_docked:
|
||||||
|
return VacuumActivity.DOCKED
|
||||||
|
if status in self._prop_status_paused:
|
||||||
|
return VacuumActivity.PAUSED
|
||||||
|
if status in self._prop_status_returning:
|
||||||
|
return VacuumActivity.RETURNING
|
||||||
|
if status in self._prop_status_error:
|
||||||
return VacuumActivity.ERROR
|
return VacuumActivity.ERROR
|
||||||
return VacuumActivity.IDLE
|
return VacuumActivity.IDLE
|
||||||
except ImportError:
|
|
||||||
return status_value
|
|
||||||
|
|
||||||
@property
|
else:
|
||||||
def battery_level(self) -> Optional[int]:
|
|
||||||
"""The current battery level of the vacuum cleaner."""
|
|
||||||
return self.get_prop_value(prop=self._prop_battery_level)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fan_speed(self) -> Optional[str]:
|
def state(self) -> Optional[str]:
|
||||||
"""The current fan speed of the vacuum cleaner."""
|
"""The current state of the vacuum."""
|
||||||
return self.get_map_value(
|
status = self.get_prop_value(prop=self._prop_status)
|
||||||
map_=self._fan_level_map,
|
return None if (status is None) else self.get_map_value(
|
||||||
key=self.get_prop_value(prop=self._prop_fan_level))
|
map_=self._status_map, key=status)
|
||||||
|
@ -168,7 +168,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
if operation_mode == STATE_ON:
|
if operation_mode == STATE_ON:
|
||||||
await self.set_property_async(prop=self._prop_on, value=True)
|
await self.set_property_async(prop=self._prop_on, value=True)
|
||||||
return
|
return
|
||||||
if self.get_prop_value(prop=self._prop_on) is False:
|
if self.get_prop_value(prop=self._prop_on) is not True:
|
||||||
await self.set_property_async(prop=self._prop_on,
|
await self.set_property_async(prop=self._prop_on,
|
||||||
value=True,
|
value=True,
|
||||||
write_ha_state=False)
|
write_ha_state=False)
|
||||||
|
@ -93,7 +93,7 @@ git checkout v1.0.0
|
|||||||
|
|
||||||
- 米家集成是否支持本地化控制?
|
- 米家集成是否支持本地化控制?
|
||||||
|
|
||||||
米家集成支持通过[小米中枢网关](https://www.mi.com/shop/buy/detail?product_id=15755&cfrom=search)(固件版本 3.4.0_000 以上)或内置中枢网关(软件版本 0.8.0 以上)的米家设备实现本地化控制。如果没有小米中枢网关或其他带中枢网关功能的设备,那么所有控制指令都会通过小米云发送。支持 Home Assistant 本地化控制的小米中枢网关(含内置中枢网关)的固件尚未发布,固件升级计划请参阅 MIoT 团队的通知。
|
米家集成支持通过[小米中枢网关](https://www.mi.com/shop/buy/detail?product_id=15755&cfrom=search)(固件版本 3.3.0_0023 及以上)或内置中枢网关(软件版本 0.8.9 及以上)的米家设备实现本地化控制。如果没有小米中枢网关或其他带中枢网关功能的设备,那么所有控制指令都会通过小米云发送。支持 Home Assistant 本地化控制的小米中枢网关(含内置中枢网关)的固件尚未发布,固件升级计划请参阅 MIoT 团队的通知。
|
||||||
|
|
||||||
小米中枢网关仅在中国大陆可用,在其他地区不可用。
|
小米中枢网关仅在中国大陆可用,在其他地区不可用。
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user