mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2025-09-17 19:53:15 +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:
|
||||
label: Describe the Bug / 描述问题
|
||||
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:
|
||||
required: true
|
||||
|
||||
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,4 +1,39 @@
|
||||
# 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
|
||||
### Added
|
||||
- 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
|
||||
|
||||
- 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?
|
||||
|
||||
@ -93,7 +93,7 @@ Xiaomi Home Integration and the affiliated cloud interface is provided by Xiaomi
|
||||
|
||||
- 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.
|
||||
|
||||
@ -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.
|
||||
|
||||
The format of `spec_filter.json` is as follows.
|
||||
The format of `spec_filter.yaml` is as follows.
|
||||
|
||||
```yaml
|
||||
<MIoT-Spec-V2 device instance urn without the version field>:
|
||||
|
@ -546,7 +546,7 @@ class AirConditioner(FeatureOnOff, FeatureTargetTemperature,
|
||||
f'{self.entity_id}')
|
||||
return
|
||||
# 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,
|
||||
value=True,
|
||||
write_ha_state=False)
|
||||
|
@ -75,6 +75,9 @@ from .miot.const import (
|
||||
DEFAULT_CLOUD_SERVER,
|
||||
DEFAULT_CTRL_MODE,
|
||||
DEFAULT_INTEGRATION_LANGUAGE,
|
||||
DEFAULT_COVER_DEAD_ZONE_WIDTH,
|
||||
MIN_COVER_DEAD_ZONE_WIDTH,
|
||||
MAX_COVER_DEAD_ZONE_WIDTH,
|
||||
DEFAULT_NICK_NAME,
|
||||
DEFAULT_OAUTH2_API_HOST,
|
||||
DOMAIN,
|
||||
@ -129,6 +132,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
_cloud_server: str
|
||||
_integration_language: str
|
||||
_cover_dz_width: int
|
||||
_auth_info: dict
|
||||
_nick_name: str
|
||||
_home_selected: dict
|
||||
@ -151,6 +155,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
self._main_loop = asyncio.get_running_loop()
|
||||
self._cloud_server = DEFAULT_CLOUD_SERVER
|
||||
self._integration_language = DEFAULT_INTEGRATION_LANGUAGE
|
||||
self._cover_dz_width = DEFAULT_COVER_DEAD_ZONE_WIDTH
|
||||
self._storage_path = ''
|
||||
self._virtual_did = ''
|
||||
self._uid = ''
|
||||
@ -951,6 +956,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
'action_debug': self._action_debug,
|
||||
'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_devices_changed_notify':
|
||||
self._display_devices_changed_notify
|
||||
@ -995,6 +1001,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
_hide_non_standard_entities: bool
|
||||
_display_binary_mode: list[str]
|
||||
_display_devs_notify: list[str]
|
||||
_cover_dz_width: int
|
||||
|
||||
_oauth_redirect_url_full: str
|
||||
_auth_info: dict
|
||||
@ -1015,6 +1022,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
_opt_lan_ctrl_cfg: bool
|
||||
_opt_network_detect_cfg: bool
|
||||
_opt_check_network_deps: bool
|
||||
_cover_width_new: int
|
||||
|
||||
_trans_rules_count: 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._integration_language = self._entry_data.get(
|
||||
'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._action_debug = self._entry_data.get('action_debug', False)
|
||||
self._hide_non_standard_entities = self._entry_data.get(
|
||||
@ -1068,6 +1078,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
self._action_debug_new = False
|
||||
self._hide_non_standard_entities_new = False
|
||||
self._display_binary_mode_new = []
|
||||
self._cover_width_new = self._cover_dz_width
|
||||
self._update_user_info = False
|
||||
self._update_devices = False
|
||||
self._update_trans_rules = False
|
||||
@ -1340,6 +1351,12 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
): cv.multi_select(
|
||||
self._miot_i18n.translate(
|
||||
'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(
|
||||
'update_trans_rules',
|
||||
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)
|
||||
self._opt_network_detect_cfg = user_input.get(
|
||||
'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()
|
||||
|
||||
@ -1926,6 +1945,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
'nick_name': self._nick_name,
|
||||
'lang_new': INTEGRATION_LANGUAGES[self._lang_new],
|
||||
'nick_name_new': self._nick_name_new,
|
||||
'cover_width_new': self._cover_width_new,
|
||||
'devices_add': len(self._devices_add),
|
||||
'devices_remove': len(self._devices_remove),
|
||||
'trans_rules_count': self._trans_rules_count,
|
||||
@ -1952,6 +1972,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
if self._lang_new != self._integration_language:
|
||||
self._entry_data['integration_language'] = self._lang_new
|
||||
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:
|
||||
self._entry_data['nick_name'] = self._nick_name_new
|
||||
if self._update_devices:
|
||||
|
@ -91,6 +91,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry,
|
||||
class Cover(MIoTServiceEntity, CoverEntity):
|
||||
"""Cover entities for Xiaomi Home."""
|
||||
# pylint: disable=unused-argument
|
||||
_cover_dead_zone_width: int
|
||||
_prop_motor_control: Optional[MIoTSpecProperty]
|
||||
_prop_motor_value_open: 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_features = CoverEntityFeature(0)
|
||||
|
||||
self._cover_dead_zone_width = (
|
||||
miot_device.miot_client.cover_dead_zone_width)
|
||||
|
||||
self._prop_motor_control = None
|
||||
self._prop_motor_value_open = None
|
||||
self._prop_motor_value_close = None
|
||||
@ -166,12 +170,13 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
||||
self._prop_status_opening.append(item.value)
|
||||
elif item_name in {
|
||||
'closing', 'close', 'down', 'dowm', 'falling',
|
||||
'dropping', 'downing', 'lower'
|
||||
'fallin', 'dropping', 'downing', 'lower'
|
||||
}:
|
||||
self._prop_status_closing.append(item.value)
|
||||
elif item_name in {
|
||||
'stopatlowest', 'stoplowerlimit', 'lowerlimitstop',
|
||||
'floor', 'lowerlimit'
|
||||
'closed', 'closeover', 'stopatlowest',
|
||||
'stoplowerlimit', 'lowerlimitstop', 'floor',
|
||||
'lowerlimit'
|
||||
}:
|
||||
self._prop_status_closed.append(item.value)
|
||||
self._prop_status = prop
|
||||
@ -270,8 +275,14 @@ class Cover(MIoTServiceEntity, CoverEntity):
|
||||
self._prop_pos_closing = False
|
||||
return self.get_prop_value(prop=self._prop_target_position)
|
||||
pos = self.get_prop_value(prop=self._prop_current_position)
|
||||
return None if pos is None else round(pos * 100 /
|
||||
self._prop_position_value_range)
|
||||
if pos is None:
|
||||
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
|
||||
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
|
||||
if preset_mode:
|
||||
await self.set_property_async(
|
||||
self._prop_mode,
|
||||
prop=self._prop_mode,
|
||||
value=self.get_map_key(
|
||||
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:
|
||||
"""Set the preset mode."""
|
||||
await self.set_property_async(
|
||||
self._prop_mode,
|
||||
prop=self._prop_mode,
|
||||
value=self.get_map_key(
|
||||
map_=self._mode_map, value=preset_mode))
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
"cryptography",
|
||||
"psutil"
|
||||
],
|
||||
"version": "v0.3.4",
|
||||
"version": "v0.4.2",
|
||||
"zeroconf": [
|
||||
"_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',
|
||||
'climate',
|
||||
'cover',
|
||||
'device_tracker',
|
||||
'event',
|
||||
'fan',
|
||||
'humidifier',
|
||||
'light',
|
||||
'media_player',
|
||||
'notify',
|
||||
'number',
|
||||
'select',
|
||||
@ -87,6 +89,7 @@ SUPPORTED_PLATFORMS: list = [
|
||||
|
||||
UNSUPPORTED_MODELS: list = [
|
||||
'chuangmi.ir.v2',
|
||||
'hmpace.motion.v6nfc',
|
||||
'xiaomi.router.rd03'
|
||||
]
|
||||
|
||||
@ -118,6 +121,10 @@ INTEGRATION_LANGUAGES = {
|
||||
'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'
|
||||
|
||||
# Registered in Xiaomi OAuth 2.0 Service
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} Geräte offline:** \n{message}",
|
||||
"network_status_online": "Online",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} devices offline:** \n{message}",
|
||||
"network_status_online": "Online",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} dispositivos sin conexión:** \n{message}",
|
||||
"network_status_online": "En línea",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} appareils hors ligne :** \n{message}",
|
||||
"network_status_online": "En 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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} dispositivi offline:** \n{message}",
|
||||
"network_status_online": "Online",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} デバイスがオフライン:** \n{message}",
|
||||
"network_status_online": "オンライン",
|
||||
"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": "実行エラー"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} apparaten offline:** \n{message}",
|
||||
"network_status_online": "Online",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
|
||||
"network_status_online": "online",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} dispositivos offline**: \n{message}",
|
||||
"network_status_online": "Online",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} устройств недоступно:** \n{message}",
|
||||
"network_status_online": "В сети",
|
||||
"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": "Ошибка выполнения"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} 个设备离线**: \n{message}",
|
||||
"network_status_online": "在线",
|
||||
"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": "执行错误"
|
||||
}
|
||||
},
|
||||
|
@ -99,6 +99,10 @@
|
||||
"device_list_offline": "\n**{count} 個設備離線:** \n{message}",
|
||||
"network_status_online": "在線",
|
||||
"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": "執行錯誤"
|
||||
}
|
||||
},
|
||||
|
@ -63,7 +63,8 @@ from .common import MIoTMatcher, slugify_did
|
||||
from .const import (
|
||||
DEFAULT_CTRL_MODE, DEFAULT_INTEGRATION_LANGUAGE, DEFAULT_NICK_NAME, DOMAIN,
|
||||
MIHOME_CERT_EXPIRE_MARGIN, NETWORK_REFRESH_INTERVAL,
|
||||
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_error import MIoTClientError, MIoTErrorCode
|
||||
from .miot_mips import (
|
||||
@ -252,7 +253,18 @@ class MIoTClient:
|
||||
if not self._user_config:
|
||||
# Integration need to be add again
|
||||
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
|
||||
self._i18n = MIoTI18n(
|
||||
lang=self._entry_data.get(
|
||||
@ -486,6 +498,11 @@ class MIoTClient:
|
||||
def display_binary_bool(self) -> 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
|
||||
def display_devices_changed_notify(self, value: list[str]) -> None:
|
||||
if set(value) == set(self._display_devs_notify):
|
||||
@ -990,7 +1007,7 @@ class MIoTClient:
|
||||
and self._device_list_cloud[did].get('online', False)
|
||||
):
|
||||
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
|
||||
return
|
||||
# Unsub old
|
||||
@ -1109,8 +1126,10 @@ class MIoTClient:
|
||||
_LOGGER.info('local mips state changed, %s, %s', group_id, state)
|
||||
mips = self._mips_local.get(group_id, None)
|
||||
if not mips:
|
||||
_LOGGER.error(
|
||||
_LOGGER.info(
|
||||
'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
|
||||
if state:
|
||||
# Connected
|
||||
@ -1144,6 +1163,7 @@ class MIoTClient:
|
||||
if sub and sub.handler:
|
||||
sub.handler(did, MIoTDeviceState.OFFLINE, sub.handler_ctx)
|
||||
self.__request_show_devices_changed_notify()
|
||||
self.__show_central_state_changed_notify(state)
|
||||
|
||||
@final
|
||||
async def __on_miot_lan_state_change(self, state: bool) -> None:
|
||||
@ -1486,8 +1506,6 @@ class MIoTClient:
|
||||
if did not in filter_dids:
|
||||
continue
|
||||
device_old = self._device_list_gateway.get(did, None)
|
||||
gw_state_old = device_old.get(
|
||||
'online', False) if device_old else False
|
||||
gw_state_new: bool = False
|
||||
device_new = gw_list.pop(did, None)
|
||||
if device_new:
|
||||
@ -1501,7 +1519,7 @@ class MIoTClient:
|
||||
device_old['online'] = False
|
||||
# Update cache group_id
|
||||
info['group_id'] = group_id
|
||||
if gw_state_old == gw_state_new:
|
||||
if not gw_state_new:
|
||||
continue
|
||||
self.__update_device_msg_sub(did=did)
|
||||
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(
|
||||
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
|
||||
async def get_miot_instance_async(
|
||||
|
@ -543,6 +543,7 @@ class MIoTHttpClient:
|
||||
req_data: dict = {
|
||||
'limit': 200,
|
||||
'get_split_device': True,
|
||||
'get_third_device': True,
|
||||
'dids': dids
|
||||
}
|
||||
if start_did:
|
||||
|
@ -1108,6 +1108,8 @@ class MIoTServiceEntity(Entity):
|
||||
):
|
||||
continue
|
||||
value: Any = prop.value_format(params['value'])
|
||||
value = prop.eval_expr(value)
|
||||
value = prop.value_format(value)
|
||||
self._prop_value_map[prop] = value
|
||||
if prop in self._prop_changed_subs:
|
||||
self._prop_changed_subs[prop](prop, value)
|
||||
@ -1279,8 +1281,9 @@ class MIoTPropertyEntity(Entity):
|
||||
|
||||
def __on_value_changed(self, params: dict, ctx: Any) -> None:
|
||||
_LOGGER.debug('property changed, %s', params)
|
||||
self._value = self.spec.value_format(params['value'])
|
||||
self._value = self.spec.eval_expr(self._value)
|
||||
value: Any = self.spec.value_format(params['value'])
|
||||
value = self.spec.eval_expr(value)
|
||||
self._value = self.spec.value_format(value)
|
||||
if not self._pending_write_ha_state_timer:
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
@ -110,7 +110,6 @@ class MipsServiceData:
|
||||
version=IPVersion.V4Only)
|
||||
if not self.addresses:
|
||||
raise MipsServiceError('invalid addresses')
|
||||
self.addresses.sort()
|
||||
if not service_info.port:
|
||||
raise MipsServiceError('invalid port')
|
||||
self.port = service_info.port
|
||||
@ -226,7 +225,7 @@ class MipsService:
|
||||
state_change: ServiceStateChange
|
||||
) -> None:
|
||||
_LOGGER.debug(
|
||||
'mips service state changed, %s, %s, %s',
|
||||
'mdns discovery changed, %s, %s, %s',
|
||||
state_change, name, service_type)
|
||||
|
||||
if state_change is ServiceStateChange.Removed:
|
||||
|
@ -513,6 +513,7 @@ class _MipsClient(ABC):
|
||||
"""
|
||||
self.__thread_check()
|
||||
if not self._mqtt or not self._mqtt.is_connected():
|
||||
self.log_error(f'mips sub when not connected, {topic}')
|
||||
return
|
||||
try:
|
||||
if topic not in self._mips_sub_pending_map:
|
||||
@ -531,6 +532,7 @@ class _MipsClient(ABC):
|
||||
"""
|
||||
self.__thread_check()
|
||||
if not self._mqtt or not self._mqtt.is_connected():
|
||||
self.log_debug(f'mips unsub when not connected, {topic}')
|
||||
return
|
||||
try:
|
||||
result, mid = self._mqtt.unsubscribe(topic=topic)
|
||||
@ -639,6 +641,7 @@ class _MipsClient(ABC):
|
||||
_LOGGER.error('__on_connect, but mqtt is None')
|
||||
return
|
||||
if not self._mqtt.is_connected():
|
||||
_LOGGER.error('__on_connect, but mqtt is disconnected')
|
||||
return
|
||||
self.log_info(f'mips connect, {flags}, {rc}, {props}')
|
||||
self.__reset_reconnect_time()
|
||||
@ -994,6 +997,11 @@ class MipsCloudClient(_MipsClient):
|
||||
handler(
|
||||
did, MIoTDeviceState.ONLINE if msg['event'] == 'online'
|
||||
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(
|
||||
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:
|
||||
- '2'
|
||||
- '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:
|
||||
prop.3.4:
|
||||
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:
|
||||
prop.10.6:
|
||||
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-c24:1:
|
||||
prop.8.6:
|
||||
unit: kWh
|
||||
prop.10.6:
|
||||
unit: none
|
||||
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:
|
||||
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-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: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
|
||||
@ -26,6 +45,18 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1:
|
||||
prop.10.6:
|
||||
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-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:
|
||||
prop.2.5:
|
||||
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:
|
||||
prop.2.2:
|
||||
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:
|
||||
prop.2.2:
|
||||
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:
|
||||
prop.2.2:
|
||||
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:
|
||||
prop.2.2:
|
||||
name: fan-level-a
|
||||
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p69:1:0000D062:
|
||||
prop.2.4:
|
||||
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:
|
||||
prop.2.2:
|
||||
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:
|
||||
prop.3.1:
|
||||
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:
|
||||
prop.3.1:
|
||||
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:
|
||||
prop.8.11:
|
||||
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:
|
||||
prop.2.1004:
|
||||
name: contact-state
|
||||
@ -161,13 +215,16 @@ urn:miot-spec-v2:device:magnet-sensor:0000A016:linp-m1:1:
|
||||
description: open
|
||||
- value: 1
|
||||
description: closed
|
||||
expr: src_value!=1
|
||||
expr: (src_value!=1)
|
||||
urn:miot-spec-v2:device:motion-sensor:0000A014:lumi-acn001:1:
|
||||
prop.3.2:
|
||||
access:
|
||||
- read
|
||||
- notify
|
||||
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:2:
|
||||
prop.2.8:
|
||||
@ -199,6 +256,9 @@ urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2:
|
||||
unit: mA
|
||||
prop.3.2:
|
||||
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:
|
||||
prop.11.1:
|
||||
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:
|
||||
prop.5.1:
|
||||
name: contact-state
|
||||
expr: src_value!=1
|
||||
expr: (src_value!=1)
|
||||
urn:miot-spec-v2:device:switch:0000A003:090615-x1tpm:1:0000D042:
|
||||
prop.27.3:
|
||||
name: light-on
|
||||
|
@ -50,18 +50,11 @@ from homeassistant.components.sensor import SensorStateClass
|
||||
from homeassistant.components.event import EventDeviceClass
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
EntityCategory,
|
||||
LIGHT_LUX,
|
||||
UnitOfEnergy,
|
||||
UnitOfPower,
|
||||
UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfTemperature,
|
||||
UnitOfPressure,
|
||||
PERCENTAGE
|
||||
)
|
||||
from homeassistant.const import (CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
EntityCategory, LIGHT_LUX, UnitOfEnergy,
|
||||
UnitOfPower, UnitOfElectricCurrent,
|
||||
UnitOfElectricPotential, UnitOfTemperature,
|
||||
UnitOfPressure, PERCENTAGE)
|
||||
|
||||
# pylint: disable=pointless-string-statement
|
||||
"""SPEC_DEVICE_TRANS_MAP
|
||||
@ -107,7 +100,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'humidifier': {
|
||||
'required': {
|
||||
'humidifier': {
|
||||
'required': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'on': {'read', 'write'}
|
||||
}
|
||||
@ -119,7 +112,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
},
|
||||
'optional': {
|
||||
'environment': {
|
||||
'required': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'relative-humidity': {'read'}
|
||||
}
|
||||
@ -131,7 +124,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'dehumidifier': {
|
||||
'required': {
|
||||
'dehumidifier': {
|
||||
'required': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'on': {'read', 'write'}
|
||||
}
|
||||
@ -143,7 +136,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
},
|
||||
'optional': {
|
||||
'environment': {
|
||||
'required': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'relative-humidity': {'read'}
|
||||
}
|
||||
@ -155,15 +148,13 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'vacuum': {
|
||||
'required': {
|
||||
'vacuum': {
|
||||
'required': {
|
||||
'required': {
|
||||
'actions': {'start-sweep', 'stop-sweeping'},
|
||||
},
|
||||
'optional': {
|
||||
'properties': {'status', 'fan-level'},
|
||||
'actions': {
|
||||
'pause-sweeping',
|
||||
'continue-sweep',
|
||||
'stop-and-gocharge'
|
||||
'pause-sweeping', 'continue-sweep', 'stop-and-gocharge'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,7 +162,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'optional': {
|
||||
'identify': {
|
||||
'required': {
|
||||
'actions': {'identify'}
|
||||
'actions': {'identify'}
|
||||
}
|
||||
},
|
||||
'battery': {
|
||||
@ -179,6 +170,11 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'properties': {
|
||||
'battery-level': {'read'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'actions': {
|
||||
'start-charge'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -204,10 +200,9 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'required': {},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'on',
|
||||
'fan-level',
|
||||
'horizontal-swing',
|
||||
'vertical-swing'}}
|
||||
'on', 'fan-level', 'horizontal-swing', 'vertical-swing'
|
||||
}
|
||||
}
|
||||
},
|
||||
'environment': {
|
||||
'required': {},
|
||||
@ -235,8 +230,8 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'target-temperature', 'mode', 'fan-level',
|
||||
'temperature'}
|
||||
'target-temperature', 'mode', 'fan-level', 'temperature'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -278,7 +273,7 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'ptc-bath-heater': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'mode':{'read', 'write'}
|
||||
'mode': {'read', 'write'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
@ -321,6 +316,89 @@ SPEC_DEVICE_TRANS_MAP: dict = {
|
||||
'optional': {},
|
||||
'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
|
||||
@ -351,9 +429,7 @@ SPEC_SERVICE_TRANS_MAP: dict = {
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'mode', 'brightness', 'color', 'color-temperature'
|
||||
}
|
||||
'properties': {'mode', 'brightness', 'color', 'color-temperature'}
|
||||
},
|
||||
'entity': 'light'
|
||||
},
|
||||
@ -368,7 +444,8 @@ SPEC_SERVICE_TRANS_MAP: dict = {
|
||||
},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'mode', 'brightness',
|
||||
'mode',
|
||||
'brightness',
|
||||
}
|
||||
},
|
||||
'entity': 'light',
|
||||
@ -401,16 +478,14 @@ SPEC_SERVICE_TRANS_MAP: dict = {
|
||||
},
|
||||
'entity': 'water_heater'
|
||||
},
|
||||
'curtain': {
|
||||
'curtain': {
|
||||
'required': {
|
||||
'properties': {
|
||||
'motor-control': {'write'}
|
||||
}
|
||||
},
|
||||
'optional': {
|
||||
'properties': {
|
||||
'status', 'current-position', 'target-position'
|
||||
}
|
||||
'properties': {'status', 'current-position', 'target-position'}
|
||||
},
|
||||
'entity': 'cover'
|
||||
},
|
||||
@ -600,6 +675,4 @@ SPEC_EVENT_TRANS_MAP: dict[str, str] = {
|
||||
'doorbell-ring': EventDeviceClass.DOORBELL
|
||||
}
|
||||
|
||||
SPEC_ACTION_TRANS_MAP = {
|
||||
|
||||
}
|
||||
SPEC_ACTION_TRANS_MAP = {}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"step": {
|
||||
"eula": {
|
||||
"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": {
|
||||
"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",
|
||||
"update_trans_rules": "Entitätskonvertierungsregeln 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": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Bestätigen Sie die Konfiguration",
|
||||
"description": "**{nick_name}**, bitte bestätigen Sie die neuesten Konfigurationsinformationen und klicken Sie dann auf \"Senden\". Die Integration wird mit den aktualisierten Konfigurationen erneut geladen.\r\n\r\nIntegrationsprache:\t{lang_new}\r\nBenutzername:\t{nick_name_new}\r\nAction-Debug-Modus:\t{action_debug}\r\nVerstecke Nicht-Standard-Entitäten:\t{hide_non_standard_entities}\r\nGerätestatusänderungen anzeigen:\t{display_devices_changed_notify}\r\nGeräteänderungen:\t{devices_add} neue Geräte hinzufügen, {devices_remove} Geräte entfernen\r\nKonvertierungsregeländerungen:\tInsgesamt {trans_rules_count} Regeln, aktualisiert {trans_rules_count_success} Regeln",
|
||||
"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": {
|
||||
"confirm": "Änderungen bestätigen"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "Display device status change notifications",
|
||||
"update_trans_rules": "Update entity conversion rules",
|
||||
"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": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirm Configuration",
|
||||
"description": "Hello **{nick_name}**, please confirm the latest configuration information and then Click SUBMIT.\r\nThe integration will reload using the updated configuration.\r\n\r\nIntegration Language: \t{lang_new}\r\nNickname: \t{nick_name_new}\r\nDebug mode for action: \t{action_debug}\r\nHide non-standard created entities: \t{hide_non_standard_entities}\r\nDisplay device status change notifications:\t{display_devices_changed_notify}\r\nDevice Changes: \tAdd **{devices_add}** devices, Remove **{devices_remove}** devices\r\nTransformation rules change: \tThere are a total of **{trans_rules_count}** rules, and updated **{trans_rules_count_success}** rules",
|
||||
"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": {
|
||||
"confirm": "Confirm the change"
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"step": {
|
||||
"eula": {
|
||||
"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": {
|
||||
"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",
|
||||
"update_trans_rules": "Actualizar reglas de conversión de entidad",
|
||||
"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": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmar configuración",
|
||||
"description": "¡Hola, **{nick_name}**! Por favor, confirme la última información de configuración y haga clic en \"Enviar\" para finalizar la configuración.\r\nLa integración se volverá a cargar con la nueva configuración.\r\n\r\nIdioma de la integración:\t{lang_new}\r\nApodo de usuario:\t{nick_name_new}\r\nModo de depuración de Action:\t{action_debug}\r\nOcultar entidades generadas no estándar:\t{hide_non_standard_entities}\r\nMostrar notificaciones de cambio de estado del dispositivo:\t{display_devices_changed_notify}\r\nCambios de dispositivos:\t{devices_add} dispositivos agregados, {devices_remove} dispositivos eliminados\r\nCambios en las reglas de conversión:\t{trans_rules_count} reglas en total, {trans_rules_count_success} reglas actualizadas",
|
||||
"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": {
|
||||
"confirm": "Confirmar modificación"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "Afficher les notifications de changement d'état de l'appareil",
|
||||
"update_trans_rules": "Mettre à jour les règles de conversion d'entités",
|
||||
"update_lan_ctrl_config": "Mettre à jour la configuration de contrôle LAN",
|
||||
"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": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmer la configuration",
|
||||
"description": "**{nick_name}** Bonjour ! Veuillez confirmer les dernières informations de configuration et cliquer sur \"Soumettre\".\r\nL'intégration rechargera avec la nouvelle configuration.\r\n\r\nLangue d'intégration : {lang_new}\r\nPseudo utilisateur : {nick_name_new}\r\nMode de débogage d'action : {action_debug}\r\nMasquer les entités générées non standard : {hide_non_standard_entities}\r\nAfficher les notifications de changement d'état de l'appareil:\t{display_devices_changed_notify}\r\nModifications des appareils : Ajouter **{devices_add}** appareils, supprimer **{devices_remove}** appareils\r\nModifications des règles de conversion : **{trans_rules_count}** règles au total, mise à jour de **{trans_rules_count_success}** règles",
|
||||
"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": {
|
||||
"confirm": "Confirmer la modification"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "Mostra notifiche di cambio stato del dispositivo",
|
||||
"update_trans_rules": "Aggiorna le regole di conversione delle entità",
|
||||
"update_lan_ctrl_config": "Aggiorna configurazione del controllo LAN",
|
||||
"network_detect_config": "Configurazione di Rete Integrata"
|
||||
"network_detect_config": "Configurazione di Rete Integrata",
|
||||
"cover_dead_zone_width": "Larghezza dell’angolo cieco della tenda"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Conferma Configurazione",
|
||||
"description": "Ciao **{nick_name}**, si prega di confermare le informazioni di configurazione più recenti e poi fare clic su INVIA.\r\nL'integrazione verrà ricaricata utilizzando la configurazione aggiornata.\r\n\r\nLingua dell'Integrazione: \t{lang_new}\r\nSoprannome: \t{nick_name_new}\r\nModalità di debug per azione: \t{action_debug}\r\nNascondi entità create non standard: \t{hide_non_standard_entities}\r\nMostra notifiche di cambio stato del dispositivo:\t{display_devices_changed_notify}\r\nCambiamenti del Dispositivo: \tAggiungi **{devices_add}** dispositivi, Rimuovi **{devices_remove}** dispositivi\r\nCambiamenti delle regole di trasformazione: \tCi sono un totale di **{trans_rules_count}** regole, e aggiornate **{trans_rules_count_success}** regole",
|
||||
"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": {
|
||||
"confirm": "Conferma la modifica"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "デバイスの状態変化通知を表示",
|
||||
"update_trans_rules": "エンティティ変換ルールを更新する",
|
||||
"update_lan_ctrl_config": "LAN制御構成を更新する",
|
||||
"network_detect_config": "統合ネットワーク構成"
|
||||
"network_detect_config": "統合ネットワーク構成",
|
||||
"cover_dead_zone_width": "カーテンの死角幅"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "構成を確認する",
|
||||
"description": "**{nick_name}** さん、こんにちは! 最新の構成情報を確認してください。[送信] をクリックして、更新された構成を使用して再度読み込みます。\r\n\r\n統合言語:\t{lang_new}\r\nユーザー名:\t{nick_name_new}\r\nAction デバッグモード:\t{action_debug}\r\n非標準生成エンティティを非表示にする:\t{hide_non_standard_entities}\r\nデバイスの状態変化通知を表示:\t{display_devices_changed_notify}\r\nデバイス変更:\t追加 **{devices_add}** 個のデバイス、削除 **{devices_remove}** 個のデバイス\r\n変換ルール変更:\t合計 **{trans_rules_count}** 個の規則、更新 **{trans_rules_count_success}** 個の規則",
|
||||
"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": {
|
||||
"confirm": "変更を確認する"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "Apparaatstatuswijzigingen weergeven",
|
||||
"update_trans_rules": "Werk entiteitsconversieregels 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": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Bevestig Configuratie",
|
||||
"description": "Hallo **{nick_name}**, bevestig alstublieft de nieuwste configuratie-informatie en klik vervolgens op INDENKEN.\r\nDe integratie zal opnieuw laden met de bijgewerkte configuratie.\r\n\r\nIntegratietaal: \t{lang_new}\r\nBijnaam: \t{nick_name_new}\r\nDebugmodus voor actie: \t{action_debug}\r\nVerberg niet-standaard gemaakte entiteiten: \t{hide_non_standard_entities}\r\nApparaatstatuswijzigingen weergeven:\t{display_devices_changed_notify}\r\nWijzigingen in apparaten: \tVoeg **{devices_add}** apparaten toe, Verwijder **{devices_remove}** apparaten\r\nWijzigingen in transformateregels: \tEr zijn in totaal **{trans_rules_count}** regels, en **{trans_rules_count_success}** regels zijn bijgewerkt",
|
||||
"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": {
|
||||
"confirm": "Bevestig de wijziging"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
|
||||
"update_trans_rules": "Atualizar regras de conversão de entidades",
|
||||
"update_lan_ctrl_config": "Atualizar configuração de controle LAN",
|
||||
"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": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmar Configuração",
|
||||
"description": "Olá **{nick_name}**, confirme as informações da configuração mais recente e depois clique em ENVIAR.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nApelido:\t{nick_name_new}\r\nModo de depuração para ação:\t{action_debug}\r\nOcultar entidades não padrão criadas:\t{hide_non_standard_entities}\r\nExibir notificações de mudança de status do dispositivo:\t{display_devices_changed_notify}\r\nAlterações de Dispositivos:\tAdicionar **{devices_add}** dispositivos, Remover **{devices_remove}** dispositivos\r\nAlteração nas Regras de Transformação:\tUm total de **{trans_rules_count}** regras, e **{trans_rules_count_success}** regras atualizadas",
|
||||
"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": {
|
||||
"confirm": "Confirmar a mudança"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "Exibir notificações de mudança de status do dispositivo",
|
||||
"update_trans_rules": "Atualizar regras de conversão de entidades",
|
||||
"update_lan_ctrl_config": "Atualizar configuração de controlo LAN",
|
||||
"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": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Confirmar Configuração",
|
||||
"description": "Olá **{nick_name}**, confirme a informação da configuração mais recente e depois clique em SUBMETER.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nAlcunha:\t{nick_name_new}\r\nModo de depuração de ação:\t{action_debug}\r\nOcultar entidades não padrão:\t{hide_non_standard_entities}\r\nExibir notificações de mudança de status do dispositivo:\t{display_devices_changed_notify}\r\nAlterações aos Dispositivos:\tAdicionar **{devices_add}** dispositivos, Remover **{devices_remove}** dispositivos\r\nAlteração das Regras de Transformação:\tExistem **{trans_rules_count}** regras no total, com **{trans_rules_count_success}** regras atualizadas",
|
||||
"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": {
|
||||
"confirm": "Confirmar a alteração"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "Отображать уведомления о изменении состояния устройства",
|
||||
"update_trans_rules": "Обновить правила преобразования сущностей",
|
||||
"update_lan_ctrl_config": "Обновить конфигурацию управления LAN",
|
||||
"network_detect_config": "Интегрированная Сетевая Конфигурация"
|
||||
"network_detect_config": "Интегрированная Сетевая Конфигурация",
|
||||
"cover_dead_zone_width": "Ширина «мертвой зоны» шторы"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "Подтверждение настройки",
|
||||
"description": "**{nick_name}** Здравствуйте! Подтвердите последнюю информацию о настройке и нажмите «Отправить». Интеграция будет перезагружена с использованием обновленных настроек.\r\n\r\nЯзык интеграции:\t{lang_new}\r\nИмя пользователя:\t{nick_name_new}\r\nРежим отладки Action:\t{action_debug}\r\nСкрыть непроизводственные сущности:\t{hide_non_standard_entities}\r\nОтображать уведомления о изменении состояния устройства:\t{display_devices_changed_notify}\r\nИзменение устройства:\tДобавлено **{devices_add}** устройство, удалено **{devices_remove}** устройства\r\nИзменение правил преобразования:\tВсего **{trans_rules_count}** правил, обновлено **{trans_rules_count_success}** правил",
|
||||
"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": {
|
||||
"confirm": "Подтвердить изменения"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "显示设备状态变化通知",
|
||||
"update_trans_rules": "更新实体转换规则",
|
||||
"update_lan_ctrl_config": "更新局域网控制配置",
|
||||
"network_detect_config": "集成网络配置"
|
||||
"network_detect_config": "集成网络配置",
|
||||
"cover_dead_zone_width": "窗帘盲区宽度"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "确认配置",
|
||||
"description": "**{nick_name}** 您好!请确认最新的配置信息,然后点击“提交”。\r\n集成将会使用更新后的配置重新载入。\r\n\r\n集成语言:\t{lang_new}\r\n用户昵称:\t{nick_name_new}\r\nAction 调试模式:\t{action_debug}\r\n隐藏非标准生成实体:\t{hide_non_standard_entities}\r\n显示设备状态变化通知:\t{display_devices_changed_notify}\r\n设备变化:\t新增 **{devices_add}** 个设备,移除 **{devices_remove}** 个设备\r\n转换规则变化:\t共条 **{trans_rules_count}** 规则,更新 **{trans_rules_count_success}** 条规则",
|
||||
"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": {
|
||||
"confirm": "确认修改"
|
||||
}
|
||||
|
@ -124,7 +124,8 @@
|
||||
"display_devices_changed_notify": "顯示設備狀態變化通知",
|
||||
"update_trans_rules": "更新實體轉換規則",
|
||||
"update_lan_ctrl_config": "更新局域網控制配置",
|
||||
"network_detect_config": "集成網絡配置"
|
||||
"network_detect_config": "集成網絡配置",
|
||||
"cover_dead_zone_width": "窗簾盲區寬度"
|
||||
}
|
||||
},
|
||||
"update_user_info": {
|
||||
@ -183,7 +184,7 @@
|
||||
},
|
||||
"config_confirm": {
|
||||
"title": "確認配置",
|
||||
"description": "**{nick_name}** 您好!請確認最新的配置信息,然後點擊“提交”。\r\n集成將會使用更新後的配置重新載入。\r\n\r\n集成語言:\t{lang_new}\r\n用戶暱稱:\t{nick_name_new}\r\nAction 調試模式:\t{action_debug}\r\n隱藏非標準生成實體:\t{hide_non_standard_entities}\r\n顯示設備狀態變化通知:\t{display_devices_changed_notify}\r\n設備變化:\t新增 **{devices_add}** 個設備,移除 **{devices_remove}** 個設備\r\n轉換規則變化:\t共條 **{trans_rules_count}** 規則,更新 **{trans_rules_count_success}** 條規則",
|
||||
"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": {
|
||||
"confirm": "確認修改"
|
||||
}
|
||||
|
@ -60,6 +60,12 @@ from .miot.const import DOMAIN
|
||||
from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData
|
||||
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__)
|
||||
|
||||
|
||||
@ -85,6 +91,11 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
_prop_status: Optional[MIoTSpecProperty]
|
||||
_prop_fan_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_stop_sweeping: Optional[MIoTSpecAction]
|
||||
@ -107,6 +118,11 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
self._prop_status = None
|
||||
self._prop_fan_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_stop_sweeping = None
|
||||
self._action_pause_sweeping = None
|
||||
@ -126,6 +142,35 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
self._status_map = prop.value_list.to_map()
|
||||
self._attr_supported_features |= VacuumEntityFeature.STATE
|
||||
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':
|
||||
if not prop.value_list:
|
||||
_LOGGER.error('invalid fan-level value_list, %s',
|
||||
@ -158,18 +203,22 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
self._attr_supported_features |= VacuumEntityFeature.LOCATE
|
||||
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:
|
||||
"""Start or resume the cleaning task."""
|
||||
try: # VacuumActivity is introduced in HA core 2025.1.0
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from homeassistant.components.vacuum import VacuumActivity
|
||||
if (self.activity
|
||||
== 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:
|
||||
if self._prop_status is not None:
|
||||
status = self.get_prop_value(prop=self._prop_status)
|
||||
if (status in self._prop_status_paused
|
||||
) and self._action_continue_sweep:
|
||||
await self.action_async(action=self._action_continue_sweep)
|
||||
return
|
||||
await self.action_async(action=self._action_start_sweep)
|
||||
@ -203,9 +252,22 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
|
||||
return self._device_name
|
||||
|
||||
@property
|
||||
def state(self) -> Optional[str]:
|
||||
"""Return the current state of the vacuum cleaner.
|
||||
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
|
||||
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:
|
||||
Detected that custom integration 'xiaomi_home' is setting state
|
||||
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
|
||||
string as before if there is no match. In Home Assistant 2026.1, every
|
||||
state should map to a VacuumActivity enum.
|
||||
"""
|
||||
return self.activity
|
||||
|
||||
@property
|
||||
def activity(self) -> Optional[str]:
|
||||
"""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'
|
||||
}):
|
||||
"""
|
||||
status = self.get_prop_value(prop=self._prop_status)
|
||||
if status is None:
|
||||
return None
|
||||
if status in self._prop_status_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.IDLE
|
||||
except ImportError:
|
||||
return status_value
|
||||
|
||||
@property
|
||||
def battery_level(self) -> Optional[int]:
|
||||
"""The current battery level of the vacuum cleaner."""
|
||||
return self.get_prop_value(prop=self._prop_battery_level)
|
||||
else:
|
||||
|
||||
@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))
|
||||
@property
|
||||
def state(self) -> Optional[str]:
|
||||
"""The current state of the vacuum."""
|
||||
status = self.get_prop_value(prop=self._prop_status)
|
||||
return None if (status is None) else self.get_map_value(
|
||||
map_=self._status_map, key=status)
|
||||
|
@ -168,7 +168,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
||||
if operation_mode == STATE_ON:
|
||||
await self.set_property_async(prop=self._prop_on, value=True)
|
||||
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,
|
||||
value=True,
|
||||
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