14 Commits
v0.4.0 ... main

Author SHA1 Message Date
ec833b6539 feat: subscribe the proxy gateway child device up messages even though the device is offline (#1393)
Some checks failed
Tests / check-rule-format (push) Failing after 2s
Validate / validate-hassfest (push) Failing after 3s
Validate / validate-hacs (push) Failing after 10s
Validate / validate-lint (push) Failing after 2s
Validate / validate-setup (push) Failing after 5s
* feat: subscribe the proxy gateway child device up messages even though the device is offline (#1313)

* feat: do not subscribe proxy gateway child device online/offline state message
2025-09-02 17:22:40 +08:00
f2200ba003 fix: contact-state value format (#1387)
Some checks failed
Tests / check-rule-format (push) Failing after 4s
Validate / validate-hassfest (push) Failing after 3s
Validate / validate-hacs (push) Failing after 9s
Validate / validate-lint (push) Failing after 2s
Validate / validate-setup (push) Failing after 3s
2025-08-29 17:36:25 +08:00
073cdf2dcb fix: integer value step (#1388) 2025-08-29 17:35:46 +08:00
0f65635342 docs: update changelog and version to v0.4.2 (#1368)
Some checks failed
Tests / check-rule-format (push) Failing after 4s
Validate / validate-hassfest (push) Failing after 3s
Validate / validate-hacs (push) Failing after 10s
Validate / validate-lint (push) Failing after 2s
Validate / validate-setup (push) Failing after 4s
2025-08-20 09:15:31 +08:00
947169f18d Fix specs (#1367)
Some checks failed
Tests / check-rule-format (push) Failing after 5s
Validate / validate-hassfest (push) Failing after 3s
Validate / validate-hacs (push) Failing after 9s
Validate / validate-lint (push) Failing after 3s
Validate / validate-setup (push) Failing after 5s
* fix: xiaomi.fan.p70 fan level

* fix: xiaomi.fan.p76 fan level

* fix: ignore hmpace.motion.v6nfc

* fix: xiaomi.airc.rr0r00 humidity-range

* fix: xiaomi.airc.h43h00 humidity-range

* fix: zhimi.humidifier.ca4 water level
2025-08-19 17:49:27 +08:00
65a7a6d22a fix: correct the property value format after expression calculation (#1366) 2025-08-19 14:36:30 +08:00
c29f7eecbd fix: delete all unsupported MIoT-Spec-V2 instances of Narwa vacuum (#1355) 2025-08-19 14:12:35 +08:00
58c671483e fix: add RETURN_HOME functionality for new vacuum cleaner model (#1344)
Some checks failed
Tests / check-rule-format (push) Failing after 4s
Validate / validate-hassfest (push) Failing after 3s
Validate / validate-hacs (push) Failing after 9s
Validate / validate-lint (push) Failing after 2s
Validate / validate-setup (push) Failing after 4s
2025-08-19 10:15:01 +08:00
7c97b85f02 docs: update changelog and version to v0.4.1 (#1336)
Some checks failed
Tests / check-rule-format (push) Failing after 4s
Validate / validate-hassfest (push) Failing after 3s
Validate / validate-hacs (push) Failing after 10s
Validate / validate-lint (push) Failing after 3s
Validate / validate-setup (push) Failing after 4s
2025-08-04 09:44:12 +08:00
3f2c2a648b Fix specs (#1329)
Some checks failed
Tests / check-rule-format (push) Failing after 4s
Validate / validate-hassfest (push) Failing after 5s
Validate / validate-hacs (push) Failing after 11s
Validate / validate-lint (push) Failing after 4s
Validate / validate-setup (push) Failing after 5s
* fix: cuco.plug.cp2d electric current (#1279)

* fix: xiaomi.fan.p45 fan level (#1291)

* docs: add necessary notices

* fix: xiaomi.aircondition.c17 humidity-range unit (#1308)

* fix: xiaomi.airc.h40h00 humidity-range unit

* fix: sanmei.valve.s1 power consumption, current and voltage (#1327)

* fix: xiaomi.aircondition.m16 humidity-range unit
2025-08-01 14:56:42 +08:00
f11b2f2f68 fix: hide sensitive info in printing logs (#1328) 2025-08-01 14:54:33 +08:00
f3abbef94c fix: set the device on when the switch status is False or None (#1303)
Some checks failed
Tests / check-rule-format (push) Failing after 5s
Validate / validate-hassfest (push) Failing after 5s
Validate / validate-hacs (push) Failing after 11s
Validate / validate-lint (push) Failing after 3s
Validate / validate-setup (push) Failing after 5s
* fix: set the air conditioner on if its switch status property is False or None (#1277)

* feat: add a standalone switch for 090615.aircondition.ktf

* fix: add an alongside switch for juhl.aircondition.hvac (#1287)
2025-07-30 15:15:28 +08:00
dae63657d7 fix: vacuum status (#1299)
* fix: vacuum status (#1283)

* fix: vacuum continue to sweep

* refactor: core warnings about vacuum activity (#1288)
2025-07-30 15:14:20 +08:00
3cc66450bd feat: cover dead zone width (#1301) 2025-07-30 15:13:04 +08:00
31 changed files with 1735 additions and 139 deletions

View File

@ -5,8 +5,8 @@ body:
attributes: attributes:
label: Describe the Bug / 描述问题 label: Describe the Bug / 描述问题
description: | description: |
> A clear and concise description of what the bug is. > A clear and concise description of what the bug is. Please include the device model information (Like xiaomi.gateway.hub1 which can be found in Device info page).
> 清晰且简明地描述问题。 > 清晰且简明地描述问题。请注明设备 model 信息(例如 xiaomi.gateway.hub1可在设备详情页查询
validations: validations:
required: true required: true

View File

@ -1,4 +1,23 @@
# CHANGELOG # CHANGELOG
## v0.4.2
### Changed
- Set the battery service's start-charge action as the fallback action to support RETURN_HOME feature of the vacuum entity. [#1344](https://github.com/XiaoMi/ha_xiaomi_home/pull/1344)
### Fixed
- Correct the property value format after expression calculation. [#1366](https://github.com/XiaoMi/ha_xiaomi_home/pull/1366)
- Fix the MIoT-Spec-V2 of fix: xiaomi.fan.p70 and fix: xiaomi.fan.p76 fan level, xiaomi.airc.rr0r00 and xiaomi.airc.h43h00 humidity-range, and zhimi.humidifier.ca4 water level. [#1367](https://github.com/XiaoMi/ha_xiaomi_home/pull/1367)
- Ignore the unsupported model hmpace.motion.v6nfc.
- Delete all unsupported MIoT-Spec-V2 instances of narwa.vacuum.001 and narwa.vacuum.ax11. [#1355](https://github.com/XiaoMi/ha_xiaomi_home/pull/1355)
## v0.4.1
### Changed
- The setting option "Cover closed position" in CONFIGURE is changed to "Cover dead zone width". [#1301](https://github.com/XiaoMi/ha_xiaomi_home/pull/1301)
- Add an alongside switch entity for 090615.aircondition.ktf and juhl.aircondition.hvac. [#1303](https://github.com/XiaoMi/ha_xiaomi_home/pull/1303)
### Fixed
- Fix the vacuum status so that the vacuum activity will not always be idle. [#1299](https://github.com/XiaoMi/ha_xiaomi_home/pull/1299)
- Set the device on when the switch status is False or None. [#1303](https://github.com/XiaoMi/ha_xiaomi_home/pull/1303)
- Hide sensitive info in printing logs. [#1328](https://github.com/XiaoMi/ha_xiaomi_home/pull/1328)
- Fix the MIoT-Spec-V2 of cuco.plug.cp2d electric current, xiaomi.fan.p45 fan level, sanmei.valve.s1 power consumption, current and voltage, xiaomi.aircondition.c17, xiaomi.aircondition.m16 and xiaomi.airc.h40h00 humidity-range unit. [#1329](https://github.com/XiaoMi/ha_xiaomi_home/pull/1329)
## v0.4.0 ## v0.4.0
### Added ### Added
- Add the watch as the device tracker entity. [#1189](https://github.com/XiaoMi/ha_xiaomi_home/pull/1189) - Add the watch as the device tracker entity. [#1189](https://github.com/XiaoMi/ha_xiaomi_home/pull/1189)

View File

@ -70,8 +70,8 @@ async def async_setup_entry(
for miot_device in device_list: for miot_device in device_list:
if miot_device.miot_client.display_binary_bool: if miot_device.miot_client.display_binary_bool:
for prop in miot_device.prop_list.get('binary_sensor', []): for prop in miot_device.prop_list.get('binary_sensor', []):
new_entities.append(BinarySensor( new_entities.append(
miot_device=miot_device, spec=prop)) BinarySensor(miot_device=miot_device, spec=prop))
if new_entities: if new_entities:
async_add_entities(new_entities) async_add_entities(new_entities)
@ -90,7 +90,7 @@ class BinarySensor(MIoTPropertyEntity, BinarySensorEntity):
def is_on(self) -> bool: def is_on(self) -> bool:
"""On/Off state. True if the binary sensor is on, False otherwise.""" """On/Off state. True if the binary sensor is on, False otherwise."""
if self.spec.name == 'contact-state': if self.spec.name == 'contact-state':
return self._value is False return bool(self._value) is False
elif self.spec.name == 'occupancy-status': elif self.spec.name == 'occupancy-status':
return bool(self._value) return bool(self._value)
return self._value is True return self._value is True

View File

@ -546,7 +546,7 @@ class AirConditioner(FeatureOnOff, FeatureTargetTemperature,
f'{self.entity_id}') f'{self.entity_id}')
return return
# set the device on # set the device on
if self.get_prop_value(prop=self._prop_on) is False: if self.get_prop_value(prop=self._prop_on) is not True:
await self.set_property_async(prop=self._prop_on, await self.set_property_async(prop=self._prop_on,
value=True, value=True,
write_ha_state=False) write_ha_state=False)

View File

@ -75,9 +75,9 @@ from .miot.const import (
DEFAULT_CLOUD_SERVER, DEFAULT_CLOUD_SERVER,
DEFAULT_CTRL_MODE, DEFAULT_CTRL_MODE,
DEFAULT_INTEGRATION_LANGUAGE, DEFAULT_INTEGRATION_LANGUAGE,
DEFAULT_COVER_CLOSED_POSITION, DEFAULT_COVER_DEAD_ZONE_WIDTH,
MIN_COVER_CLOSED_POSITION, MIN_COVER_DEAD_ZONE_WIDTH,
MAX_COVER_CLOSED_POSITION, MAX_COVER_DEAD_ZONE_WIDTH,
DEFAULT_NICK_NAME, DEFAULT_NICK_NAME,
DEFAULT_OAUTH2_API_HOST, DEFAULT_OAUTH2_API_HOST,
DOMAIN, DOMAIN,
@ -132,7 +132,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
_cloud_server: str _cloud_server: str
_integration_language: str _integration_language: str
_cover_closed_position: int _cover_dz_width: int
_auth_info: dict _auth_info: dict
_nick_name: str _nick_name: str
_home_selected: dict _home_selected: dict
@ -155,7 +155,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._main_loop = asyncio.get_running_loop() self._main_loop = asyncio.get_running_loop()
self._cloud_server = DEFAULT_CLOUD_SERVER self._cloud_server = DEFAULT_CLOUD_SERVER
self._integration_language = DEFAULT_INTEGRATION_LANGUAGE self._integration_language = DEFAULT_INTEGRATION_LANGUAGE
self._cover_closed_position = DEFAULT_COVER_CLOSED_POSITION self._cover_dz_width = DEFAULT_COVER_DEAD_ZONE_WIDTH
self._storage_path = '' self._storage_path = ''
self._virtual_did = '' self._virtual_did = ''
self._uid = '' self._uid = ''
@ -956,7 +956,7 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
'action_debug': self._action_debug, 'action_debug': self._action_debug,
'hide_non_standard_entities': 'hide_non_standard_entities':
self._hide_non_standard_entities, self._hide_non_standard_entities,
'cover_closed_position': self._cover_closed_position, 'cover_dead_zone_width': self._cover_dz_width,
'display_binary_mode': self._display_binary_mode, 'display_binary_mode': self._display_binary_mode,
'display_devices_changed_notify': 'display_devices_changed_notify':
self._display_devices_changed_notify self._display_devices_changed_notify
@ -1001,7 +1001,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
_hide_non_standard_entities: bool _hide_non_standard_entities: bool
_display_binary_mode: list[str] _display_binary_mode: list[str]
_display_devs_notify: list[str] _display_devs_notify: list[str]
_cover_closed_position: int _cover_dz_width: int
_oauth_redirect_url_full: str _oauth_redirect_url_full: str
_auth_info: dict _auth_info: dict
@ -1022,7 +1022,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
_opt_lan_ctrl_cfg: bool _opt_lan_ctrl_cfg: bool
_opt_network_detect_cfg: bool _opt_network_detect_cfg: bool
_opt_check_network_deps: bool _opt_check_network_deps: bool
_cover_pos_new: int _cover_width_new: int
_trans_rules_count: int _trans_rules_count: int
_trans_rules_count_success: int _trans_rules_count_success: int
@ -1051,8 +1051,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
self._ctrl_mode = self._entry_data.get('ctrl_mode', DEFAULT_CTRL_MODE) self._ctrl_mode = self._entry_data.get('ctrl_mode', DEFAULT_CTRL_MODE)
self._integration_language = self._entry_data.get( self._integration_language = self._entry_data.get(
'integration_language', DEFAULT_INTEGRATION_LANGUAGE) 'integration_language', DEFAULT_INTEGRATION_LANGUAGE)
self._cover_closed_position = self._entry_data.get( self._cover_dz_width = self._entry_data.get(
'cover_closed_position', DEFAULT_COVER_CLOSED_POSITION) 'cover_dead_zone_width', DEFAULT_COVER_DEAD_ZONE_WIDTH)
self._nick_name = self._entry_data.get('nick_name', DEFAULT_NICK_NAME) self._nick_name = self._entry_data.get('nick_name', DEFAULT_NICK_NAME)
self._action_debug = self._entry_data.get('action_debug', False) self._action_debug = self._entry_data.get('action_debug', False)
self._hide_non_standard_entities = self._entry_data.get( self._hide_non_standard_entities = self._entry_data.get(
@ -1078,7 +1078,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
self._action_debug_new = False self._action_debug_new = False
self._hide_non_standard_entities_new = False self._hide_non_standard_entities_new = False
self._display_binary_mode_new = [] self._display_binary_mode_new = []
self._cover_pos_new = self._cover_closed_position self._cover_width_new = self._cover_dz_width
self._update_user_info = False self._update_user_info = False
self._update_devices = False self._update_devices = False
self._update_trans_rules = False self._update_trans_rules = False
@ -1352,11 +1352,11 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
self._miot_i18n.translate( self._miot_i18n.translate(
'config.binary_mode')), # type: ignore 'config.binary_mode')), # type: ignore
vol.Optional( vol.Optional(
'cover_closed_position', 'cover_dead_zone_width',
default=self._cover_closed_position # type: ignore default=self._cover_dz_width # type: ignore
): vol.All(vol.Coerce(int), vol.Range( ): vol.All(vol.Coerce(int), vol.Range(
min=MIN_COVER_CLOSED_POSITION, min=MIN_COVER_DEAD_ZONE_WIDTH,
max=MAX_COVER_CLOSED_POSITION)), max=MAX_COVER_DEAD_ZONE_WIDTH)),
vol.Required( vol.Required(
'update_trans_rules', 'update_trans_rules',
default=self._update_trans_rules # type: ignore default=self._update_trans_rules # type: ignore
@ -1395,8 +1395,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
'update_lan_ctrl_config', self._opt_lan_ctrl_cfg) 'update_lan_ctrl_config', self._opt_lan_ctrl_cfg)
self._opt_network_detect_cfg = user_input.get( self._opt_network_detect_cfg = user_input.get(
'network_detect_config', self._opt_network_detect_cfg) 'network_detect_config', self._opt_network_detect_cfg)
self._cover_pos_new = user_input.get( self._cover_width_new = user_input.get(
'cover_closed_position', self._cover_closed_position) 'cover_dead_zone_width', self._cover_dz_width)
return await self.async_step_update_user_info() return await self.async_step_update_user_info()
@ -1945,7 +1945,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
'nick_name': self._nick_name, 'nick_name': self._nick_name,
'lang_new': INTEGRATION_LANGUAGES[self._lang_new], 'lang_new': INTEGRATION_LANGUAGES[self._lang_new],
'nick_name_new': self._nick_name_new, 'nick_name_new': self._nick_name_new,
'cover_pos_new': self._cover_pos_new, 'cover_width_new': self._cover_width_new,
'devices_add': len(self._devices_add), 'devices_add': len(self._devices_add),
'devices_remove': len(self._devices_remove), 'devices_remove': len(self._devices_remove),
'trans_rules_count': self._trans_rules_count, 'trans_rules_count': self._trans_rules_count,
@ -1972,8 +1972,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
if self._lang_new != self._integration_language: if self._lang_new != self._integration_language:
self._entry_data['integration_language'] = self._lang_new self._entry_data['integration_language'] = self._lang_new
self._need_reload = True self._need_reload = True
if self._cover_pos_new != self._cover_closed_position: if self._cover_width_new != self._cover_dz_width:
self._entry_data['cover_closed_position'] = self._cover_pos_new self._entry_data['cover_dead_zone_width'] = self._cover_width_new
self._need_reload = True self._need_reload = True
if self._update_user_info: if self._update_user_info:
self._entry_data['nick_name'] = self._nick_name_new self._entry_data['nick_name'] = self._nick_name_new

View File

@ -91,7 +91,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry,
class Cover(MIoTServiceEntity, CoverEntity): class Cover(MIoTServiceEntity, CoverEntity):
"""Cover entities for Xiaomi Home.""" """Cover entities for Xiaomi Home."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
_cover_closed_position: int _cover_dead_zone_width: int
_prop_motor_control: Optional[MIoTSpecProperty] _prop_motor_control: Optional[MIoTSpecProperty]
_prop_motor_value_open: Optional[int] _prop_motor_value_open: Optional[int]
_prop_motor_value_close: Optional[int] _prop_motor_value_close: Optional[int]
@ -116,8 +116,8 @@ class Cover(MIoTServiceEntity, CoverEntity):
self._attr_supported_color_modes = set() self._attr_supported_color_modes = set()
self._attr_supported_features = CoverEntityFeature(0) self._attr_supported_features = CoverEntityFeature(0)
self._cover_closed_position = ( self._cover_dead_zone_width = (
miot_device.miot_client.cover_closed_position) miot_device.miot_client.cover_dead_zone_width)
self._prop_motor_control = None self._prop_motor_control = None
self._prop_motor_value_open = None self._prop_motor_value_open = None
@ -275,8 +275,14 @@ class Cover(MIoTServiceEntity, CoverEntity):
self._prop_pos_closing = False self._prop_pos_closing = False
return self.get_prop_value(prop=self._prop_target_position) return self.get_prop_value(prop=self._prop_target_position)
pos = self.get_prop_value(prop=self._prop_current_position) pos = self.get_prop_value(prop=self._prop_current_position)
return None if pos is None else round(pos * 100 / if pos is None:
self._prop_position_value_range) return None
pos = round(pos*100/self._prop_position_value_range)
if pos <= self._cover_dead_zone_width:
pos = 0
elif pos >= (100 - self._cover_dead_zone_width):
pos = 100
return pos
@property @property
def is_opening(self) -> Optional[bool]: def is_opening(self) -> Optional[bool]:
@ -302,7 +308,7 @@ class Cover(MIoTServiceEntity, CoverEntity):
def is_closed(self) -> Optional[bool]: def is_closed(self) -> Optional[bool]:
"""Return if the cover is closed.""" """Return if the cover is closed."""
if self.current_cover_position is not None: if self.current_cover_position is not None:
return self.current_cover_position <= self._cover_closed_position return self.current_cover_position == 0
# The current position is prior to the status when determining # The current position is prior to the status when determining
# whether the cover is closed. # whether the cover is closed.
if self._prop_status and self._prop_status_closed: if self._prop_status and self._prop_status_closed:

View File

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

View File

@ -89,6 +89,7 @@ SUPPORTED_PLATFORMS: list = [
UNSUPPORTED_MODELS: list = [ UNSUPPORTED_MODELS: list = [
'chuangmi.ir.v2', 'chuangmi.ir.v2',
'hmpace.motion.v6nfc',
'xiaomi.router.rd03' 'xiaomi.router.rd03'
] ]
@ -120,9 +121,9 @@ INTEGRATION_LANGUAGES = {
'zh-Hant': '繁體中文' 'zh-Hant': '繁體中文'
} }
DEFAULT_COVER_CLOSED_POSITION: int = 0 DEFAULT_COVER_DEAD_ZONE_WIDTH: int = 0
MIN_COVER_CLOSED_POSITION: int = 0 MIN_COVER_DEAD_ZONE_WIDTH: int = 0
MAX_COVER_CLOSED_POSITION: int = 5 MAX_COVER_DEAD_ZONE_WIDTH: int = 5
DEFAULT_CTRL_MODE: str = 'auto' DEFAULT_CTRL_MODE: str = 'auto'

View File

@ -64,7 +64,7 @@ from .const import (
DEFAULT_CTRL_MODE, DEFAULT_INTEGRATION_LANGUAGE, DEFAULT_NICK_NAME, DOMAIN, DEFAULT_CTRL_MODE, DEFAULT_INTEGRATION_LANGUAGE, DEFAULT_NICK_NAME, DOMAIN,
MIHOME_CERT_EXPIRE_MARGIN, NETWORK_REFRESH_INTERVAL, MIHOME_CERT_EXPIRE_MARGIN, NETWORK_REFRESH_INTERVAL,
OAUTH2_CLIENT_ID, SUPPORT_CENTRAL_GATEWAY_CTRL, OAUTH2_CLIENT_ID, SUPPORT_CENTRAL_GATEWAY_CTRL,
DEFAULT_COVER_CLOSED_POSITION) DEFAULT_COVER_DEAD_ZONE_WIDTH)
from .miot_cloud import MIoTHttpClient, MIoTOauthClient from .miot_cloud import MIoTHttpClient, MIoTOauthClient
from .miot_error import MIoTClientError, MIoTErrorCode from .miot_error import MIoTClientError, MIoTErrorCode
from .miot_mips import ( from .miot_mips import (
@ -253,7 +253,18 @@ class MIoTClient:
if not self._user_config: if not self._user_config:
# Integration need to be add again # Integration need to be add again
raise MIoTClientError('load_user_config_async error') raise MIoTClientError('load_user_config_async error')
_LOGGER.debug('user config, %s', json.dumps(self._user_config)) # Hide sensitive info in printing
p_user_config: dict = deepcopy(self._user_config)
p_access_token: str = p_user_config['auth_info']['access_token']
p_refresh_token: str = p_user_config['auth_info']['refresh_token']
p_mac_key: str = p_user_config['auth_info']['mac_key']
p_user_config['auth_info'][
'access_token'] = f"{p_access_token[:5]}***{p_access_token[-5:]}"
p_user_config['auth_info'][
'refresh_token'] = f"{p_refresh_token[:5]}***{p_refresh_token[-5:]}"
p_user_config['auth_info'][
'mac_key'] = f"{p_mac_key[:5]}***{p_mac_key[-5:]}"
_LOGGER.debug('user config, %s', json.dumps(p_user_config))
# MIoT i18n client # MIoT i18n client
self._i18n = MIoTI18n( self._i18n = MIoTI18n(
lang=self._entry_data.get( lang=self._entry_data.get(
@ -488,9 +499,9 @@ class MIoTClient:
return self._display_binary_bool return self._display_binary_bool
@property @property
def cover_closed_position(self) -> int: def cover_dead_zone_width(self) -> int:
return self._entry_data.get('cover_closed_position', return self._entry_data.get('cover_dead_zone_width',
DEFAULT_COVER_CLOSED_POSITION) DEFAULT_COVER_DEAD_ZONE_WIDTH)
@display_devices_changed_notify.setter @display_devices_changed_notify.setter
def display_devices_changed_notify(self, value: list[str]) -> None: def display_devices_changed_notify(self, value: list[str]) -> None:
@ -1363,10 +1374,13 @@ class MIoTClient:
"""Update cloud devices. """Update cloud devices.
NOTICE: This function will operate the cloud_list NOTICE: This function will operate the cloud_list
""" """
# MIoT cloud service may not publish the online state updating message # MIoT cloud may not publish the online state updating message
# for the BLE device. Assume that all BLE devices are online. # for the BLE device. Assume that all BLE devices are online.
# MIoT cloud does not publish the online state updating message for the
# child device under the proxy gateway (eg, VRF air conditioner
# controller). Assume that all proxy gateway child devices are online.
for did, info in cloud_list.items(): for did, info in cloud_list.items():
if did.startswith('blt.'): if did.startswith('blt.') or did.startswith('proxy.'):
info['online'] = True info['online'] = True
for did, info in self._device_list_cache.items(): for did, info in self._device_list_cache.items():
if filter_dids and did not in filter_dids: if filter_dids and did not in filter_dids:

View File

@ -1108,6 +1108,8 @@ class MIoTServiceEntity(Entity):
): ):
continue continue
value: Any = prop.value_format(params['value']) value: Any = prop.value_format(params['value'])
value = prop.eval_expr(value)
value = prop.value_format(value)
self._prop_value_map[prop] = value self._prop_value_map[prop] = value
if prop in self._prop_changed_subs: if prop in self._prop_changed_subs:
self._prop_changed_subs[prop](prop, value) self._prop_changed_subs[prop](prop, value)
@ -1279,8 +1281,9 @@ class MIoTPropertyEntity(Entity):
def __on_value_changed(self, params: dict, ctx: Any) -> None: def __on_value_changed(self, params: dict, ctx: Any) -> None:
_LOGGER.debug('property changed, %s', params) _LOGGER.debug('property changed, %s', params)
self._value = self.spec.value_format(params['value']) value: Any = self.spec.value_format(params['value'])
self._value = self.spec.eval_expr(self._value) value = self.spec.eval_expr(value)
self._value = self.spec.value_format(value)
if not self._pending_write_ha_state_timer: if not self._pending_write_ha_state_timer:
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -513,6 +513,7 @@ class _MipsClient(ABC):
""" """
self.__thread_check() self.__thread_check()
if not self._mqtt or not self._mqtt.is_connected(): if not self._mqtt or not self._mqtt.is_connected():
self.log_error(f'mips sub when not connected, {topic}')
return return
try: try:
if topic not in self._mips_sub_pending_map: if topic not in self._mips_sub_pending_map:
@ -531,6 +532,7 @@ class _MipsClient(ABC):
""" """
self.__thread_check() self.__thread_check()
if not self._mqtt or not self._mqtt.is_connected(): if not self._mqtt or not self._mqtt.is_connected():
self.log_debug(f'mips unsub when not connected, {topic}')
return return
try: try:
result, mid = self._mqtt.unsubscribe(topic=topic) result, mid = self._mqtt.unsubscribe(topic=topic)
@ -639,6 +641,7 @@ class _MipsClient(ABC):
_LOGGER.error('__on_connect, but mqtt is None') _LOGGER.error('__on_connect, but mqtt is None')
return return
if not self._mqtt.is_connected(): if not self._mqtt.is_connected():
_LOGGER.error('__on_connect, but mqtt is disconnected')
return return
self.log_info(f'mips connect, {flags}, {rc}, {props}') self.log_info(f'mips connect, {flags}, {rc}, {props}')
self.__reset_reconnect_time() self.__reset_reconnect_time()
@ -995,9 +998,11 @@ class MipsCloudClient(_MipsClient):
did, MIoTDeviceState.ONLINE if msg['event'] == 'online' did, MIoTDeviceState.ONLINE if msg['event'] == 'online'
else MIoTDeviceState.OFFLINE, ctx) else MIoTDeviceState.OFFLINE, ctx)
if did.startswith('blt.'): if did.startswith('blt.') or did.startswith('proxy.'):
# MIoT cloud may not publish BLE device online/offline state message. # MIoT cloud may not publish BLE device or proxy gateway child device
# Do not subscribe BLE device online/offline state. # online/offline state message.
# Do not subscribe BLE device or proxy gateway child device
# online/offline state.
return True return True
return self.__reg_broadcast_external( return self.__reg_broadcast_external(
topic=topic, handler=on_state_msg, handler_ctx=handler_ctx) topic=topic, handler=on_state_msg, handler_ctx=handler_ctx)

View File

@ -601,7 +601,10 @@ class MIoTSpecProperty(_MIoTSpecBase):
if value is None: if value is None:
return None return None
if self.format_ == int: if self.format_ == int:
if self.value_range is None:
return int(round(value)) return int(round(value))
return int(
round(value / self.value_range.step) * self.value_range.step)
if self.format_ == float: if self.format_ == float:
return round(value, self.precision) return round(value, self.precision)
if self.format_ == bool: if self.format_ == bool:

File diff suppressed because it is too large Load Diff

View File

@ -48,3 +48,9 @@ urn:miot-spec-v2:device:thermostat:0000A031:tofan-wk01:
services: services:
- '2' - '2'
- '4' - '4'
urn:miot-spec-v2:device:vacuum:0000A006:narwa-001:
services:
- '*'
urn:miot-spec-v2:device:vacuum:0000A006:narwa-ax11:
services:
- '*'

View File

@ -1,6 +1,10 @@
urn:miot-spec-v2:device:air-condition-outlet:0000A045:lumi-mcn04:1: urn:miot-spec-v2:device:air-condition-outlet:0000A045:lumi-mcn04:1:
prop.3.4: prop.3.4:
format: uint8 format: uint8
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:1:
prop.10.6:
unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c17:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c20:1:
prop.10.6: prop.10.6:
unit: none unit: none
@ -15,6 +19,19 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1:
prop.10.6: prop.10.6:
unit: none unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c35:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h40h00:1:
prop.10.6:
unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:1:
prop.10.6:
unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-h43h00:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1:
prop.10.6:
unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m16:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:1: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:1: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
@ -28,6 +45,12 @@ urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1:
prop.10.6: prop.10.6:
unit: none unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1 urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-mt0:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1:
prop.10.6:
unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-rr0r00:1
urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgd1st:1: urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-cgd1st:1:
prop.3.7: prop.3.7:
value-range: value-range:
@ -86,6 +109,13 @@ urn:miot-spec-v2:device:bath-heater:0000A028:xiaomi-s1:1:
urn:miot-spec-v2:device:curtain:0000A00C:bjkcz-kczble:1:0000D031: urn:miot-spec-v2:device:curtain:0000A00C:bjkcz-kczble:1:0000D031:
prop.2.2: prop.2.2:
name: status-a name: status-a
urn:miot-spec-v2:device:electronic-valve:0000A0A7:sanmei-s1:1:
prop.3.1:
expr: round(src_value/100, 2)
prop.3.2:
expr: round(src_value/100, 2)
prop.3.3:
expr: round(src_value/10, 1)
urn:miot-spec-v2:device:fan:0000A005:dmaker-p33:1: urn:miot-spec-v2:device:fan:0000A005:dmaker-p33:1:
prop.2.2: prop.2.2:
name: fan-level-a name: fan-level-a
@ -101,12 +131,21 @@ urn:miot-spec-v2:device:fan:0000A005:dmaker-p5:1:
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p43:1: urn:miot-spec-v2:device:fan:0000A005:xiaomi-p43:1:
prop.2.2: prop.2.2:
name: fan-level-a name: fan-level-a
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p45:1:0000D062:
prop.2.4:
name: fan-level-a
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1: urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1:
prop.2.2: prop.2.2:
name: fan-level-a name: fan-level-a
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p69:1:0000D062: urn:miot-spec-v2:device:fan:0000A005:xiaomi-p69:1:0000D062:
prop.2.4: prop.2.4:
name: fan-level-a name: fan-level-a
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p70:1:0000D062:
prop.2.4:
name: fan-level-a
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p76:1:0000D062:
prop.2.4:
name: fan-level-a
urn:miot-spec-v2:device:fan:0000A005:zhimi-sa1:3: urn:miot-spec-v2:device:fan:0000A005:zhimi-sa1:3:
prop.2.2: prop.2.2:
name: fan-level-a name: fan-level-a
@ -152,6 +191,10 @@ urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:2: urn:miot-spec-v2:device:hood
urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:3: urn:miot-spec-v2:device:hood:0000A01B:cykj-jyj22:3:
prop.3.1: prop.3.1:
name: on-ventilation name: on-ventilation
urn:miot-spec-v2:device:humidifier:0000A00E:zhimi-ca4:2:
prop.2.7:
unit: percentage
expr: round(src_value*0.83)
urn:miot-spec-v2:device:kettle:0000A009:yunmi-r3:1: urn:miot-spec-v2:device:kettle:0000A009:yunmi-r3:1:
prop.3.1: prop.3.1:
unit: ppm unit: ppm
@ -172,7 +215,7 @@ urn:miot-spec-v2:device:magnet-sensor:0000A016:linp-m1:1:
description: open description: open
- value: 1 - value: 1
description: closed description: closed
expr: src_value!=1 expr: (src_value!=1)
urn:miot-spec-v2:device:motion-sensor:0000A014:lumi-acn001:1: urn:miot-spec-v2:device:motion-sensor:0000A014:lumi-acn001:1:
prop.3.2: prop.3.2:
access: access:
@ -213,6 +256,9 @@ urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2:
unit: mA unit: mA
prop.3.2: prop.3.2:
expr: round(src_value/10, 1) expr: round(src_value/10, 1)
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2d:1:
prop.3.2:
expr: round(src_value/1000, 2)
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1: urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1:
prop.11.1: prop.11.1:
name: power-consumption name: power-consumption
@ -257,7 +303,7 @@ urn:miot-spec-v2:device:router:0000A036:xiaomi-rd08:1:
urn:miot-spec-v2:device:safe-box:0000A042:loock-v1:1: urn:miot-spec-v2:device:safe-box:0000A042:loock-v1:1:
prop.5.1: prop.5.1:
name: contact-state name: contact-state
expr: src_value!=1 expr: (src_value!=1)
urn:miot-spec-v2:device:switch:0000A003:090615-x1tpm:1:0000D042: urn:miot-spec-v2:device:switch:0000A003:090615-x1tpm:1:0000D042:
prop.27.3: prop.27.3:
name: light-on name: light-on

View File

@ -170,6 +170,11 @@ SPEC_DEVICE_TRANS_MAP: dict = {
'properties': { 'properties': {
'battery-level': {'read'} 'battery-level': {'read'}
} }
},
'optional': {
'actions': {
'start-charge'
}
} }
} }
}, },

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Entitätskonvertierungsregeln aktualisieren", "update_trans_rules": "Entitätskonvertierungsregeln aktualisieren",
"update_lan_ctrl_config": "LAN-Steuerungskonfiguration aktualisieren", "update_lan_ctrl_config": "LAN-Steuerungskonfiguration aktualisieren",
"network_detect_config": "Integrierte Netzwerkkonfiguration", "network_detect_config": "Integrierte Netzwerkkonfiguration",
"cover_closed_position": "Die Position der geschlossenen Vorhänge" "cover_dead_zone_width": "Die Breite des toten Winkels des Vorhangs"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Bestätigen Sie die Konfiguration", "title": "Bestätigen Sie die Konfiguration",
"description": "**{nick_name}**, bitte bestätigen Sie die neuesten Konfigurationsinformationen und klicken Sie dann auf \"Senden\". Die Integration wird mit den aktualisierten Konfigurationen erneut geladen.\r\n\r\nIntegrationsprache:\t{lang_new}\r\nBenutzername:\t{nick_name_new}\r\nAction-Debug-Modus:\t{action_debug}\r\nVerstecke Nicht-Standard-Entitäten:\t{hide_non_standard_entities}\r\nDie Position der geschlossenen Vorhänge:\t{cover_pos_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", "description": "**{nick_name}**, bitte bestätigen Sie die neuesten Konfigurationsinformationen und klicken Sie dann auf \"Senden\". Die Integration wird mit den aktualisierten Konfigurationen erneut geladen.\r\n\r\nIntegrationsprache:\t{lang_new}\r\nBenutzername:\t{nick_name_new}\r\nAction-Debug-Modus:\t{action_debug}\r\nVerstecke Nicht-Standard-Entitäten:\t{hide_non_standard_entities}\r\nDie Breite des toten Winkels des Vorhangs:\t{cover_width_new}\r\nGerätestatusänderungen anzeigen:\t{display_devices_changed_notify}\r\nGeräteänderungen:\t{devices_add} neue Geräte hinzufügen, {devices_remove} Geräte entfernen\r\nKonvertierungsregeländerungen:\tInsgesamt {trans_rules_count} Regeln, aktualisiert {trans_rules_count_success} Regeln",
"data": { "data": {
"confirm": "Änderungen bestätigen" "confirm": "Änderungen bestätigen"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Update entity conversion rules", "update_trans_rules": "Update entity conversion rules",
"update_lan_ctrl_config": "Update LAN control configuration", "update_lan_ctrl_config": "Update LAN control configuration",
"network_detect_config": "Integrated network configuration", "network_detect_config": "Integrated network configuration",
"cover_closed_position": "Cover closed position" "cover_dead_zone_width": "Cover dead zone width"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Confirm Configuration", "title": "Confirm Configuration",
"description": "Hello **{nick_name}**, please confirm the latest configuration information and then Click SUBMIT.\r\nThe integration will reload using the updated configuration.\r\n\r\nIntegration Language:\t{lang_new}\r\nNickname:\t{nick_name_new}\r\nDebug mode for action:\t{action_debug}\r\nHide non-standard created entities:\t{hide_non_standard_entities}\r\nCover closed position:\t{cover_pos_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", "description": "Hello **{nick_name}**, please confirm the latest configuration information and then Click SUBMIT.\r\nThe integration will reload using the updated configuration.\r\n\r\nIntegration Language:\t{lang_new}\r\nNickname:\t{nick_name_new}\r\nDebug mode for action:\t{action_debug}\r\nHide non-standard created entities:\t{hide_non_standard_entities}\r\nCover dead zone width:\t{cover_width_new}\r\nDisplay device status change notifications:\t{display_devices_changed_notify}\r\nDevice Changes:\tAdd **{devices_add}** devices, Remove **{devices_remove}** devices\r\nTransformation rules change:\tThere are a total of **{trans_rules_count}** rules, and updated **{trans_rules_count_success}** rules",
"data": { "data": {
"confirm": "Confirm the change" "confirm": "Confirm the change"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Actualizar reglas de conversión de entidad", "update_trans_rules": "Actualizar reglas de conversión de entidad",
"update_lan_ctrl_config": "Actualizar configuración de control LAN", "update_lan_ctrl_config": "Actualizar configuración de control LAN",
"network_detect_config": "Configuración de Red Integrada", "network_detect_config": "Configuración de Red Integrada",
"cover_closed_position": "La posición de las cortinas cerradas" "cover_dead_zone_width": "Anchura del punto ciego de la cortina"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Confirmar configuración", "title": "Confirmar configuración",
"description": "¡Hola, **{nick_name}**! Por favor, confirme la última información de configuración y haga clic en \"Enviar\" para finalizar la configuración.\r\nLa integración se volverá a cargar con la nueva configuración.\r\n\r\nIdioma de la integración:\t{lang_new}\r\nApodo de usuario:\t{nick_name_new}\r\nModo de depuración de Action:\t{action_debug}\r\nOcultar entidades generadas no estándar:\t{hide_non_standard_entities}\r\nLa posición de las cortinas cerradas:\t{cover_pos_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", "description": "¡Hola, **{nick_name}**! Por favor, confirme la última información de configuración y haga clic en \"Enviar\" para finalizar la configuración.\r\nLa integración se volverá a cargar con la nueva configuración.\r\n\r\nIdioma de la integración:\t{lang_new}\r\nApodo de usuario:\t{nick_name_new}\r\nModo de depuración de Action:\t{action_debug}\r\nOcultar entidades generadas no estándar:\t{hide_non_standard_entities}\r\nAnchura del punto ciego de la cortina:\t{cover_width_new}\r\nMostrar notificaciones de cambio de estado del dispositivo:\t{display_devices_changed_notify}\r\nCambios de dispositivos:\t{devices_add} dispositivos agregados, {devices_remove} dispositivos eliminados\r\nCambios en las reglas de conversión:\t{trans_rules_count} reglas en total, {trans_rules_count_success} reglas actualizadas",
"data": { "data": {
"confirm": "Confirmar modificación" "confirm": "Confirmar modificación"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Mettre à jour les règles de conversion d'entités", "update_trans_rules": "Mettre à jour les règles de conversion d'entités",
"update_lan_ctrl_config": "Mettre à jour la configuration de contrôle LAN", "update_lan_ctrl_config": "Mettre à jour la configuration de contrôle LAN",
"network_detect_config": "Configuration Réseau Intégrée", "network_detect_config": "Configuration Réseau Intégrée",
"cover_closed_position": "La position des rideaux fermés" "cover_dead_zone_width": "Largeur de la zone aveugle du rideau"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Confirmer la configuration", "title": "Confirmer la configuration",
"description": "**{nick_name}** Bonjour ! Veuillez confirmer les dernières informations de configuration et cliquer sur \"Soumettre\".\r\nL'intégration rechargera avec la nouvelle configuration.\r\n\r\nLangue d'intégration : {lang_new}\r\nPseudo utilisateur : {nick_name_new}\r\nMode de débogage d'action : {action_debug}\r\nMasquer les entités générées non standard : {hide_non_standard_entities}\r\nLa position des rideaux fermés:\t{cover_pos_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", "description": "**{nick_name}** Bonjour ! Veuillez confirmer les dernières informations de configuration et cliquer sur \"Soumettre\".\r\nL'intégration rechargera avec la nouvelle configuration.\r\n\r\nLangue d'intégration : {lang_new}\r\nPseudo utilisateur : {nick_name_new}\r\nMode de débogage d'action : {action_debug}\r\nMasquer les entités générées non standard : {hide_non_standard_entities}\r\nLargeur de la zone aveugle du rideau:\t{cover_width_new}\r\nAfficher les notifications de changement d'état de l'appareil:\t{display_devices_changed_notify}\r\nModifications des appareils : Ajouter **{devices_add}** appareils, supprimer **{devices_remove}** appareils\r\nModifications des règles de conversion : **{trans_rules_count}** règles au total, mise à jour de **{trans_rules_count_success}** règles",
"data": { "data": {
"confirm": "Confirmer la modification" "confirm": "Confirmer la modification"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Aggiorna le regole di conversione delle entità", "update_trans_rules": "Aggiorna le regole di conversione delle entità",
"update_lan_ctrl_config": "Aggiorna configurazione del controllo LAN", "update_lan_ctrl_config": "Aggiorna configurazione del controllo LAN",
"network_detect_config": "Configurazione di Rete Integrata", "network_detect_config": "Configurazione di Rete Integrata",
"cover_closed_position": "La posizione delle tende chiuse" "cover_dead_zone_width": "Larghezza dellangolo cieco della tenda"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Conferma Configurazione", "title": "Conferma Configurazione",
"description": "Ciao **{nick_name}**, si prega di confermare le informazioni di configurazione più recenti e poi fare clic su INVIA.\r\nL'integrazione verrà ricaricata utilizzando la configurazione aggiornata.\r\n\r\nLingua dell'Integrazione:\t{lang_new}\r\nSoprannome:\t{nick_name_new}\r\nModalità di debug per azione:\t{action_debug}\r\nNascondi entità create non standard:\t{hide_non_standard_entities}\r\nLa posizione delle tende chiuse:\t{cover_pos_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", "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 dellangolo cieco della tenda:\t{cover_width_new}\r\nMostra notifiche di cambio stato del dispositivo:\t{display_devices_changed_notify}\r\nCambiamenti del Dispositivo:\tAggiungi **{devices_add}** dispositivi, Rimuovi **{devices_remove}** dispositivi\r\nCambiamenti delle regole di trasformazione:\tCi sono un totale di **{trans_rules_count}** regole, e aggiornate **{trans_rules_count_success}** regole",
"data": { "data": {
"confirm": "Conferma la modifica" "confirm": "Conferma la modifica"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "エンティティ変換ルールを更新する", "update_trans_rules": "エンティティ変換ルールを更新する",
"update_lan_ctrl_config": "LAN制御構成を更新する", "update_lan_ctrl_config": "LAN制御構成を更新する",
"network_detect_config": "統合ネットワーク構成", "network_detect_config": "統合ネットワーク構成",
"cover_closed_position": "カーテンを閉じた位置" "cover_dead_zone_width": "カーテンの死角幅"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "構成を確認する", "title": "構成を確認する",
"description": "**{nick_name}** さん、こんにちは! 最新の構成情報を確認してください。[送信] をクリックして、更新された構成を使用して再度読み込みます。\r\n\r\n統合言語\t{lang_new}\r\nユーザー名\t{nick_name_new}\r\nAction デバッグモード:\t{action_debug}\r\n非標準生成エンティティを非表示にする\t{hide_non_standard_entities}\r\nカーテンを閉じた位置\t{cover_pos_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}** 個の規則", "description": "**{nick_name}** さん、こんにちは! 最新の構成情報を確認してください。[送信] をクリックして、更新された構成を使用して再度読み込みます。\r\n\r\n統合言語\t{lang_new}\r\nユーザー名\t{nick_name_new}\r\nAction デバッグモード:\t{action_debug}\r\n非標準生成エンティティを非表示にする\t{hide_non_standard_entities}\r\nカーテンの死角幅\t{cover_width_new}\r\nデバイスの状態変化通知を表示:\t{display_devices_changed_notify}\r\nデバイス変更\t追加 **{devices_add}** 個のデバイス、削除 **{devices_remove}** 個のデバイス\r\n変換ルール変更\t合計 **{trans_rules_count}** 個の規則、更新 **{trans_rules_count_success}** 個の規則",
"data": { "data": {
"confirm": "変更を確認する" "confirm": "変更を確認する"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Werk entiteitsconversieregels bij", "update_trans_rules": "Werk entiteitsconversieregels bij",
"update_lan_ctrl_config": "Werk LAN controleconfiguratie bij", "update_lan_ctrl_config": "Werk LAN controleconfiguratie bij",
"network_detect_config": "Geïntegreerde Netwerkconfiguratie", "network_detect_config": "Geïntegreerde Netwerkconfiguratie",
"cover_closed_position": "De positie van de gesloten gordijnen" "cover_dead_zone_width": "Breedte van de dode hoek van het gordijn"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Bevestig Configuratie", "title": "Bevestig Configuratie",
"description": "Hallo **{nick_name}**, bevestig alstublieft de nieuwste configuratie-informatie en klik vervolgens op INDENKEN.\r\nDe integratie zal opnieuw laden met de bijgewerkte configuratie.\r\n\r\nIntegratietaal:\t{lang_new}\r\nBijnaam:\t{nick_name_new}\r\nDebugmodus voor actie:\t{action_debug}\r\nVerberg niet-standaard gemaakte entiteiten:\t{hide_non_standard_entities}\r\nDe positie van de gesloten gordijnen:\t{cover_pos_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", "description": "Hallo **{nick_name}**, bevestig alstublieft de nieuwste configuratie-informatie en klik vervolgens op INDENKEN.\r\nDe integratie zal opnieuw laden met de bijgewerkte configuratie.\r\n\r\nIntegratietaal:\t{lang_new}\r\nBijnaam:\t{nick_name_new}\r\nDebugmodus voor actie:\t{action_debug}\r\nVerberg niet-standaard gemaakte entiteiten:\t{hide_non_standard_entities}\r\nBreedte van de dode hoek van het gordijn:\t{cover_width_new}\r\nApparaatstatuswijzigingen weergeven:\t{display_devices_changed_notify}\r\nWijzigingen in apparaten:\tVoeg **{devices_add}** apparaten toe, Verwijder **{devices_remove}** apparaten\r\nWijzigingen in transformateregels:\tEr zijn in totaal **{trans_rules_count}** regels, en **{trans_rules_count_success}** regels zijn bijgewerkt",
"data": { "data": {
"confirm": "Bevestig de wijziging" "confirm": "Bevestig de wijziging"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Atualizar regras de conversão de entidades", "update_trans_rules": "Atualizar regras de conversão de entidades",
"update_lan_ctrl_config": "Atualizar configuração de controle LAN", "update_lan_ctrl_config": "Atualizar configuração de controle LAN",
"network_detect_config": "Configuração de Rede Integrada", "network_detect_config": "Configuração de Rede Integrada",
"cover_closed_position": "A posição das cortinas fechadas" "cover_dead_zone_width": "Largura da área cega da cortina"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Confirmar Configuração", "title": "Confirmar Configuração",
"description": "Olá **{nick_name}**, confirme as informações da configuração mais recente e depois clique em ENVIAR.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nApelido:\t{nick_name_new}\r\nModo de depuração para ação:\t{action_debug}\r\nOcultar entidades não padrão criadas:\t{hide_non_standard_entities}\r\nA posição das cortinas fechadas:\t{cover_pos_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", "description": "Olá **{nick_name}**, confirme as informações da configuração mais recente e depois clique em ENVIAR.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nApelido:\t{nick_name_new}\r\nModo de depuração para ação:\t{action_debug}\r\nOcultar entidades não padrão criadas:\t{hide_non_standard_entities}\r\nLargura da área cega da cortina:\t{cover_width_new}\r\nExibir notificações de mudança de status do dispositivo:\t{display_devices_changed_notify}\r\nAlterações de Dispositivos:\tAdicionar **{devices_add}** dispositivos, Remover **{devices_remove}** dispositivos\r\nAlteração nas Regras de Transformação:\tUm total de **{trans_rules_count}** regras, e **{trans_rules_count_success}** regras atualizadas",
"data": { "data": {
"confirm": "Confirmar a mudança" "confirm": "Confirmar a mudança"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Atualizar regras de conversão de entidades", "update_trans_rules": "Atualizar regras de conversão de entidades",
"update_lan_ctrl_config": "Atualizar configuração de controlo LAN", "update_lan_ctrl_config": "Atualizar configuração de controlo LAN",
"network_detect_config": "Configuração de Rede Integrada", "network_detect_config": "Configuração de Rede Integrada",
"cover_closed_position": "A posição das cortinas fechadas" "cover_dead_zone_width": "Largura da zona cega da cortina"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Confirmar Configuração", "title": "Confirmar Configuração",
"description": "Olá **{nick_name}**, confirme a informação da configuração mais recente e depois clique em SUBMETER.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nAlcunha:\t{nick_name_new}\r\nModo de depuração de ação:\t{action_debug}\r\nOcultar entidades não padrão:\t{hide_non_standard_entities}\r\nA posição das cortinas fechadas:\t{cover_pos_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", "description": "Olá **{nick_name}**, confirme a informação da configuração mais recente e depois clique em SUBMETER.\r\nA integração será recarregada com a configuração atualizada.\r\n\r\nIdioma da Integração:\t{lang_new}\r\nAlcunha:\t{nick_name_new}\r\nModo de depuração de ação:\t{action_debug}\r\nOcultar entidades não padrão:\t{hide_non_standard_entities}\r\nLargura da zona cega da cortina:\t{cover_width_new}\r\nExibir notificações de mudança de status do dispositivo:\t{display_devices_changed_notify}\r\nAlterações aos Dispositivos:\tAdicionar **{devices_add}** dispositivos, Remover **{devices_remove}** dispositivos\r\nAlteração das Regras de Transformação:\tExistem **{trans_rules_count}** regras no total, com **{trans_rules_count_success}** regras atualizadas",
"data": { "data": {
"confirm": "Confirmar a alteração" "confirm": "Confirmar a alteração"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "Обновить правила преобразования сущностей", "update_trans_rules": "Обновить правила преобразования сущностей",
"update_lan_ctrl_config": "Обновить конфигурацию управления LAN", "update_lan_ctrl_config": "Обновить конфигурацию управления LAN",
"network_detect_config": "Интегрированная Сетевая Конфигурация", "network_detect_config": "Интегрированная Сетевая Конфигурация",
"cover_closed_position": "Положение закрытых штор" "cover_dead_zone_width": "Ширина «мертвой зоны» шторы"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "Подтверждение настройки", "title": "Подтверждение настройки",
"description": "**{nick_name}** Здравствуйте! Подтвердите последнюю информацию о настройке и нажмите «Отправить». Интеграция будет перезагружена с использованием обновленных настроек.\r\n\r\nЯзык интеграции:\t{lang_new}\r\nИмя пользователя:\t{nick_name_new}\r\nРежим отладки Action:\t{action_debug}\r\nСкрыть непроизводственные сущности:\t{hide_non_standard_entities}\r\nПоложение закрытых штор:\t{cover_pos_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}** правил", "description": "**{nick_name}** Здравствуйте! Подтвердите последнюю информацию о настройке и нажмите «Отправить». Интеграция будет перезагружена с использованием обновленных настроек.\r\n\r\nЯзык интеграции:\t{lang_new}\r\nИмя пользователя:\t{nick_name_new}\r\nРежим отладки Action:\t{action_debug}\r\nСкрыть непроизводственные сущности:\t{hide_non_standard_entities}\r\nШирина «мертвой зоны» шторы:\t{cover_width_new}\r\nОтображать уведомления о изменении состояния устройства:\t{display_devices_changed_notify}\r\nИзменение устройства:\tДобавлено **{devices_add}** устройство, удалено **{devices_remove}** устройства\r\nИзменение правил преобразования:\tВсего **{trans_rules_count}** правил, обновлено **{trans_rules_count_success}** правил",
"data": { "data": {
"confirm": "Подтвердить изменения" "confirm": "Подтвердить изменения"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "更新实体转换规则", "update_trans_rules": "更新实体转换规则",
"update_lan_ctrl_config": "更新局域网控制配置", "update_lan_ctrl_config": "更新局域网控制配置",
"network_detect_config": "集成网络配置", "network_detect_config": "集成网络配置",
"cover_closed_position": "窗帘关闭位置" "cover_dead_zone_width": "窗帘盲区宽度"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "确认配置", "title": "确认配置",
"description": "**{nick_name}** 您好!请确认最新的配置信息,然后点击“提交”。\r\n集成将会使用更新后的配置重新载入。\r\n\r\n集成语言\t{lang_new}\r\n用户昵称\t{nick_name_new}\r\nAction 调试模式:\t{action_debug}\r\n隐藏非标准生成实体\t{hide_non_standard_entities}\r\n窗帘关闭位置\t{cover_pos_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}** 条规则", "description": "**{nick_name}** 您好!请确认最新的配置信息,然后点击“提交”。\r\n集成将会使用更新后的配置重新载入。\r\n\r\n集成语言\t{lang_new}\r\n用户昵称\t{nick_name_new}\r\nAction 调试模式:\t{action_debug}\r\n隐藏非标准生成实体\t{hide_non_standard_entities}\r\n窗帘盲区宽度\t{cover_width_new}\r\n显示设备状态变化通知\t{display_devices_changed_notify}\r\n设备变化\t新增 **{devices_add}** 个设备,移除 **{devices_remove}** 个设备\r\n转换规则变化\t共条 **{trans_rules_count}** 规则,更新 **{trans_rules_count_success}** 条规则",
"data": { "data": {
"confirm": "确认修改" "confirm": "确认修改"
} }

View File

@ -125,7 +125,7 @@
"update_trans_rules": "更新實體轉換規則", "update_trans_rules": "更新實體轉換規則",
"update_lan_ctrl_config": "更新局域網控制配置", "update_lan_ctrl_config": "更新局域網控制配置",
"network_detect_config": "集成網絡配置", "network_detect_config": "集成網絡配置",
"cover_closed_position": "窗簾關閉位置" "cover_dead_zone_width": "窗簾盲區寬度"
} }
}, },
"update_user_info": { "update_user_info": {
@ -184,7 +184,7 @@
}, },
"config_confirm": { "config_confirm": {
"title": "確認配置", "title": "確認配置",
"description": "**{nick_name}** 您好!請確認最新的配置信息,然後點擊“提交”。\r\n集成將會使用更新後的配置重新載入。\r\n\r\n集成語言\t{lang_new}\r\n用戶暱稱\t{nick_name_new}\r\nAction 調試模式:\t{action_debug}\r\n隱藏非標準生成實體\t{hide_non_standard_entities}\r\n窗簾關閉位置\t{cover_pos_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}** 條規則", "description": "**{nick_name}** 您好!請確認最新的配置信息,然後點擊“提交”。\r\n集成將會使用更新後的配置重新載入。\r\n\r\n集成語言\t{lang_new}\r\n用戶暱稱\t{nick_name_new}\r\nAction 調試模式:\t{action_debug}\r\n隱藏非標準生成實體\t{hide_non_standard_entities}\r\n窗簾盲區寬度\t{cover_width_new}\r\n顯示設備狀態變化通知\t{display_devices_changed_notify}\r\n設備變化\t新增 **{devices_add}** 個設備,移除 **{devices_remove}** 個設備\r\n轉換規則變化\t共條 **{trans_rules_count}** 規則,更新 **{trans_rules_count_success}** 條規則",
"data": { "data": {
"confirm": "確認修改" "confirm": "確認修改"
} }

View File

@ -60,6 +60,12 @@ from .miot.const import DOMAIN
from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData from .miot.miot_device import MIoTDevice, MIoTServiceEntity, MIoTEntityData
from .miot.miot_spec import (MIoTSpecAction, MIoTSpecProperty) from .miot.miot_spec import (MIoTSpecAction, MIoTSpecProperty)
try: # VacuumActivity is introduced in HA core 2025.1.0
from homeassistant.components.vacuum import VacuumActivity
HA_CORE_HAS_ACTIVITY = True
except ImportError:
HA_CORE_HAS_ACTIVITY = False
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -85,6 +91,11 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
_prop_status: Optional[MIoTSpecProperty] _prop_status: Optional[MIoTSpecProperty]
_prop_fan_level: Optional[MIoTSpecProperty] _prop_fan_level: Optional[MIoTSpecProperty]
_prop_battery_level: Optional[MIoTSpecProperty] _prop_battery_level: Optional[MIoTSpecProperty]
_prop_status_cleaning: Optional[list[int]]
_prop_status_docked: Optional[list[int]]
_prop_status_paused: Optional[list[int]]
_prop_status_returning: Optional[list[int]]
_prop_status_error: Optional[list[int]]
_action_start_sweep: Optional[MIoTSpecAction] _action_start_sweep: Optional[MIoTSpecAction]
_action_stop_sweeping: Optional[MIoTSpecAction] _action_stop_sweeping: Optional[MIoTSpecAction]
@ -107,6 +118,11 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
self._prop_status = None self._prop_status = None
self._prop_fan_level = None self._prop_fan_level = None
self._prop_battery_level = None self._prop_battery_level = None
self._prop_status_cleaning = []
self._prop_status_docked = []
self._prop_status_paused = []
self._prop_status_returning = []
self._prop_status_error = []
self._action_start_sweep = None self._action_start_sweep = None
self._action_stop_sweeping = None self._action_stop_sweeping = None
self._action_pause_sweeping = None self._action_pause_sweeping = None
@ -126,6 +142,35 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
self._status_map = prop.value_list.to_map() self._status_map = prop.value_list.to_map()
self._attr_supported_features |= VacuumEntityFeature.STATE self._attr_supported_features |= VacuumEntityFeature.STATE
self._prop_status = prop self._prop_status = prop
for item in prop.value_list.items:
item_str: str = item.name
item_name: str = re.sub(r'[^a-z]', '', item_str)
if item_name in {
'charging', 'charged', 'chargingcompleted',
'fullcharge', 'fullpower', 'findchargerpause',
'drying', 'washing', 'wash', 'inthewash',
'inthedry', 'stationworking', 'dustcollecting',
'upgrade', 'upgrading', 'updating'
}:
self._prop_status_docked.append(item.value)
elif item_name in {'paused', 'pause'}:
self._prop_status_paused.append(item.value)
elif item_name in {
'gocharging', 'cleancompletegocharging',
'findchargewash', 'backtowashmop', 'gowash',
'gowashing', 'summon'
}:
self._prop_status_returning.append(item.value)
elif item_name in {
'error', 'breakcharging', 'gochargebreak'
}:
self._prop_status_error.append(item.value)
elif (item_name.find('sweeping') != -1) or (
item_name.find('mopping') != -1) or (item_name in {
'cleaning', 'remoteclean', 'continuesweep',
'busy', 'building', 'buildingmap', 'mapping'
}):
self._prop_status_cleaning.append(item.value)
elif prop.name == 'fan-level': elif prop.name == 'fan-level':
if not prop.value_list: if not prop.value_list:
_LOGGER.error('invalid fan-level value_list, %s', _LOGGER.error('invalid fan-level value_list, %s',
@ -158,18 +203,22 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
self._attr_supported_features |= VacuumEntityFeature.LOCATE self._attr_supported_features |= VacuumEntityFeature.LOCATE
self._action_identify = action self._action_identify = action
# Use start-charge from battery service as fallback
# if stop-and-gocharge is not available
if self._action_stop_and_gocharge is None:
for action in entity_data.actions:
if action.name == 'start-charge':
self._attr_supported_features |= (
VacuumEntityFeature.RETURN_HOME)
self._action_stop_and_gocharge = action
break
async def async_start(self) -> None: async def async_start(self) -> None:
"""Start or resume the cleaning task.""" """Start or resume the cleaning task."""
try: # VacuumActivity is introduced in HA core 2025.1.0 if self._prop_status is not None:
# pylint: disable=import-outside-toplevel status = self.get_prop_value(prop=self._prop_status)
from homeassistant.components.vacuum import VacuumActivity if (status in self._prop_status_paused
if (self.activity ) and self._action_continue_sweep:
== VacuumActivity.PAUSED) and self._action_continue_sweep:
await self.action_async(action=self._action_continue_sweep)
return
except ImportError:
if self.state and (self.state in {'paused', 'pause'
}) and self._action_continue_sweep:
await self.action_async(action=self._action_continue_sweep) await self.action_async(action=self._action_continue_sweep)
return return
await self.action_async(action=self._action_start_sweep) await self.action_async(action=self._action_start_sweep)
@ -203,9 +252,22 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
return self._device_name return self._device_name
@property @property
def state(self) -> Optional[str]: def battery_level(self) -> Optional[int]:
"""Return the current state of the vacuum cleaner. """The current battery level of the vacuum cleaner."""
return self.get_prop_value(prop=self._prop_battery_level)
@property
def fan_speed(self) -> Optional[str]:
"""The current fan speed of the vacuum cleaner."""
return self.get_map_value(
map_=self._fan_level_map,
key=self.get_prop_value(prop=self._prop_fan_level))
if HA_CORE_HAS_ACTIVITY:
@property
def activity(self) -> Optional[str]:
"""The current vacuum activity.
To fix the HA warning below: To fix the HA warning below:
Detected that custom integration 'xiaomi_home' is setting state Detected that custom integration 'xiaomi_home' is setting state
directly.Entity XXX(<class 'custom_components.xiaomi_home.vacuum directly.Entity XXX(<class 'custom_components.xiaomi_home.vacuum
@ -221,57 +283,26 @@ class Vacuum(MIoTServiceEntity, StateVacuumEntity):
string as before if there is no match. In Home Assistant 2026.1, every string as before if there is no match. In Home Assistant 2026.1, every
state should map to a VacuumActivity enum. state should map to a VacuumActivity enum.
""" """
return self.activity
@property
def activity(self) -> Optional[str]:
"""The current vacuum activity."""
status = self.get_prop_value(prop=self._prop_status) status = self.get_prop_value(prop=self._prop_status)
if status is None: if status is None:
return None return None
status_value = self.get_map_value(map_=self._status_map, key=status) if status in self._prop_status_cleaning:
if status_value is None:
return None
try:
# pylint: disable=import-outside-toplevel
from homeassistant.components.vacuum import VacuumActivity
status_value = status_value.lower()
status_str = re.sub(r'[^a-z]', '', status_value)
if status_str in {
'charging', 'charged', 'chargingcompleted', 'fullcharge',
'fullpower', 'findchargerpause', 'drying', 'washing',
'wash', 'inthewash', 'inthedry', 'stationworking',
'dustcollecting', 'upgrade', 'upgrading', 'updating'
}:
return VacuumActivity.DOCKED
if status_str in {'paused', 'pause'}:
return VacuumActivity.PAUSED
if status_str in {
'gocharging', 'cleancompletegocharging', 'findchargewash',
'backtowashmop', 'gowash', 'gowashing', 'summon'
}:
return VacuumActivity.RETURNING
if (status_str.find('sweeping')
!= -1) or (status_str.find('mopping')
!= -1) or (status_str in {
'cleaning', 'remoteclean', 'continuesweep',
'busy', 'building', 'buildingmap', 'mapping'
}):
return VacuumActivity.CLEANING return VacuumActivity.CLEANING
if status_str in {'error', 'breakcharging', 'gochargebreak'}: if status in self._prop_status_docked:
return VacuumActivity.DOCKED
if status in self._prop_status_paused:
return VacuumActivity.PAUSED
if status in self._prop_status_returning:
return VacuumActivity.RETURNING
if status in self._prop_status_error:
return VacuumActivity.ERROR return VacuumActivity.ERROR
return VacuumActivity.IDLE return VacuumActivity.IDLE
except ImportError:
return status_value else:
@property @property
def battery_level(self) -> Optional[int]: def state(self) -> Optional[str]:
"""The current battery level of the vacuum cleaner.""" """The current state of the vacuum."""
return self.get_prop_value(prop=self._prop_battery_level) status = self.get_prop_value(prop=self._prop_status)
return None if (status is None) else self.get_map_value(
@property map_=self._status_map, key=status)
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))

View File

@ -168,7 +168,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
if operation_mode == STATE_ON: if operation_mode == STATE_ON:
await self.set_property_async(prop=self._prop_on, value=True) await self.set_property_async(prop=self._prop_on, value=True)
return return
if self.get_prop_value(prop=self._prop_on) is False: if self.get_prop_value(prop=self._prop_on) is not True:
await self.set_property_async(prop=self._prop_on, await self.set_property_async(prop=self._prop_on,
value=True, value=True,
write_ha_state=False) write_ha_state=False)

View File

@ -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 团队的通知。
小米中枢网关仅在中国大陆可用,在其他地区不可用。 小米中枢网关仅在中国大陆可用,在其他地区不可用。