mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2025-04-12 04:32:46 +08:00
Merge branch 'XiaoMi:main' into main
This commit is contained in:
commit
72f3a5df3e
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,5 +1,16 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## v0.1.5b1
|
||||||
|
This version will cause some Xiaomi routers that do not support access (#564) to become unavailable. You can update the device list in the configuration or delete it manually.
|
||||||
|
### Added
|
||||||
|
- Fan entity support direction ctrl [#556](https://github.com/XiaoMi/ha_xiaomi_home/pull/556)
|
||||||
|
### Changed
|
||||||
|
- Filter miwifi.* devices and xiaomi.router.rd03 [#564](https://github.com/XiaoMi/ha_xiaomi_home/pull/564)
|
||||||
|
### Fixed
|
||||||
|
- Fix multi ha instance login [#560](https://github.com/XiaoMi/ha_xiaomi_home/pull/560)
|
||||||
|
- Fix fan speed [#464](https://github.com/XiaoMi/ha_xiaomi_home/pull/464)
|
||||||
|
- The number of profile models updated from 660 to 823. [#583](https://github.com/XiaoMi/ha_xiaomi_home/pull/583)
|
||||||
|
|
||||||
## v0.1.5b0
|
## v0.1.5b0
|
||||||
### Added
|
### Added
|
||||||
- Add missing parameter state_class [#101](https://github.com/XiaoMi/ha_xiaomi_home/pull/101)
|
- Add missing parameter state_class [#101](https://github.com/XiaoMi/ha_xiaomi_home/pull/101)
|
||||||
|
@ -68,6 +68,7 @@ from homeassistant.components.webhook import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.data_entry_flow import AbortFlow
|
from homeassistant.data_entry_flow import AbortFlow
|
||||||
|
from homeassistant.helpers.instance_id import async_get
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
from .miot.const import (
|
from .miot.const import (
|
||||||
@ -247,6 +248,13 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
if user_input:
|
if user_input:
|
||||||
self._cloud_server = user_input.get(
|
self._cloud_server = user_input.get(
|
||||||
'cloud_server', self._cloud_server)
|
'cloud_server', self._cloud_server)
|
||||||
|
# Gen instance uuid
|
||||||
|
ha_uuid = await async_get(self.hass)
|
||||||
|
if not ha_uuid:
|
||||||
|
raise AbortFlow(reason='ha_uuid_get_failed')
|
||||||
|
self._uuid = hashlib.sha256(
|
||||||
|
f'{ha_uuid}.{self._virtual_did}.{self._cloud_server}'.encode(
|
||||||
|
'utf-8')).hexdigest()[:32]
|
||||||
self._integration_language = user_input.get(
|
self._integration_language = user_input.get(
|
||||||
'integration_language', DEFAULT_INTEGRATION_LANGUAGE)
|
'integration_language', DEFAULT_INTEGRATION_LANGUAGE)
|
||||||
self._miot_i18n = MIoTI18n(
|
self._miot_i18n = MIoTI18n(
|
||||||
@ -415,9 +423,11 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
miot_oauth = MIoTOauthClient(
|
miot_oauth = MIoTOauthClient(
|
||||||
client_id=OAUTH2_CLIENT_ID,
|
client_id=OAUTH2_CLIENT_ID,
|
||||||
redirect_url=self._oauth_redirect_url_full,
|
redirect_url=self._oauth_redirect_url_full,
|
||||||
cloud_server=self._cloud_server
|
cloud_server=self._cloud_server,
|
||||||
)
|
uuid=self._uuid,
|
||||||
state = str(secrets.randbits(64))
|
loop=self._main_loop)
|
||||||
|
state = hashlib.sha1(
|
||||||
|
f'd=ha.{self._uuid}'.encode('utf-8')).hexdigest()
|
||||||
self.hass.data[DOMAIN][self._virtual_did]['oauth_state'] = state
|
self.hass.data[DOMAIN][self._virtual_did]['oauth_state'] = state
|
||||||
self._cc_oauth_auth_url = miot_oauth.gen_auth_url(
|
self._cc_oauth_auth_url = miot_oauth.gen_auth_url(
|
||||||
redirect_url=self._oauth_redirect_url_full, state=state)
|
redirect_url=self._oauth_redirect_url_full, state=state)
|
||||||
@ -498,11 +508,6 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
client_id=OAUTH2_CLIENT_ID,
|
client_id=OAUTH2_CLIENT_ID,
|
||||||
access_token=auth_info['access_token'])
|
access_token=auth_info['access_token'])
|
||||||
self._auth_info = auth_info
|
self._auth_info = auth_info
|
||||||
# Gen uuid
|
|
||||||
self._uuid = hashlib.sha256(
|
|
||||||
f'{self._virtual_did}.{auth_info["access_token"]}'.encode(
|
|
||||||
'utf-8')
|
|
||||||
).hexdigest()[:32]
|
|
||||||
try:
|
try:
|
||||||
self._nick_name = (
|
self._nick_name = (
|
||||||
await self._miot_http.get_user_info_async() or {}
|
await self._miot_http.get_user_info_async() or {}
|
||||||
@ -1145,7 +1150,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
async def async_step_oauth(self, user_input=None):
|
async def async_step_oauth(self, user_input=None):
|
||||||
try:
|
try:
|
||||||
if self._cc_task_oauth is None:
|
if self._cc_task_oauth is None:
|
||||||
state = str(secrets.randbits(64))
|
state = hashlib.sha1(
|
||||||
|
f'd=ha.{self._entry_data["uuid"]}'.encode('utf-8')
|
||||||
|
).hexdigest()
|
||||||
self.hass.data[DOMAIN][self._virtual_did]['oauth_state'] = state
|
self.hass.data[DOMAIN][self._virtual_did]['oauth_state'] = state
|
||||||
self._miot_oauth.set_redirect_url(
|
self._miot_oauth.set_redirect_url(
|
||||||
redirect_url=self._oauth_redirect_url_full)
|
redirect_url=self._oauth_redirect_url_full)
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"cryptography",
|
"cryptography",
|
||||||
"psutil"
|
"psutil"
|
||||||
],
|
],
|
||||||
"version": "v0.1.5b0",
|
"version": "v0.1.5b1",
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
"_miot-central._tcp.local."
|
"_miot-central._tcp.local."
|
||||||
]
|
]
|
||||||
|
@ -257,6 +257,7 @@ class MIoTClient:
|
|||||||
client_id=OAUTH2_CLIENT_ID,
|
client_id=OAUTH2_CLIENT_ID,
|
||||||
redirect_url=self._entry_data['oauth_redirect_url'],
|
redirect_url=self._entry_data['oauth_redirect_url'],
|
||||||
cloud_server=self._cloud_server,
|
cloud_server=self._cloud_server,
|
||||||
|
uuid=self._entry_data["uuid"],
|
||||||
loop=self._main_loop)
|
loop=self._main_loop)
|
||||||
# MIoT http client instance
|
# MIoT http client instance
|
||||||
self._http = MIoTHttpClient(
|
self._http = MIoTHttpClient(
|
||||||
@ -356,7 +357,7 @@ class MIoTClient:
|
|||||||
# Cloud mips
|
# Cloud mips
|
||||||
self._mips_cloud.unsub_mips_state(
|
self._mips_cloud.unsub_mips_state(
|
||||||
key=f'{self._uid}-{self._cloud_server}')
|
key=f'{self._uid}-{self._cloud_server}')
|
||||||
self._mips_cloud.disconnect()
|
self._mips_cloud.deinit()
|
||||||
# Cancel refresh cloud devices
|
# Cancel refresh cloud devices
|
||||||
if self._refresh_cloud_devices_timer:
|
if self._refresh_cloud_devices_timer:
|
||||||
self._refresh_cloud_devices_timer.cancel()
|
self._refresh_cloud_devices_timer.cancel()
|
||||||
@ -369,7 +370,7 @@ class MIoTClient:
|
|||||||
for mips in self._mips_local.values():
|
for mips in self._mips_local.values():
|
||||||
mips.on_dev_list_changed = None
|
mips.on_dev_list_changed = None
|
||||||
mips.unsub_mips_state(key=mips.group_id)
|
mips.unsub_mips_state(key=mips.group_id)
|
||||||
mips.disconnect()
|
mips.deinit()
|
||||||
if self._mips_local_state_changed_timers:
|
if self._mips_local_state_changed_timers:
|
||||||
for timer_item in (
|
for timer_item in (
|
||||||
self._mips_local_state_changed_timers.values()):
|
self._mips_local_state_changed_timers.values()):
|
||||||
|
@ -75,10 +75,11 @@ class MIoTOauthClient:
|
|||||||
_oauth_host: str
|
_oauth_host: str
|
||||||
_client_id: int
|
_client_id: int
|
||||||
_redirect_url: str
|
_redirect_url: str
|
||||||
|
_device_id: str
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, client_id: str, redirect_url: str, cloud_server: str,
|
self, client_id: str, redirect_url: str, cloud_server: str,
|
||||||
loop: Optional[asyncio.AbstractEventLoop] = None
|
uuid: str, loop: Optional[asyncio.AbstractEventLoop] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
self._main_loop = loop or asyncio.get_running_loop()
|
self._main_loop = loop or asyncio.get_running_loop()
|
||||||
if client_id is None or client_id.strip() == '':
|
if client_id is None or client_id.strip() == '':
|
||||||
@ -87,6 +88,8 @@ class MIoTOauthClient:
|
|||||||
raise MIoTOauthError('invalid redirect_url')
|
raise MIoTOauthError('invalid redirect_url')
|
||||||
if not cloud_server:
|
if not cloud_server:
|
||||||
raise MIoTOauthError('invalid cloud_server')
|
raise MIoTOauthError('invalid cloud_server')
|
||||||
|
if not uuid:
|
||||||
|
raise MIoTOauthError('invalid uuid')
|
||||||
|
|
||||||
self._client_id = int(client_id)
|
self._client_id = int(client_id)
|
||||||
self._redirect_url = redirect_url
|
self._redirect_url = redirect_url
|
||||||
@ -94,6 +97,7 @@ class MIoTOauthClient:
|
|||||||
self._oauth_host = DEFAULT_OAUTH2_API_HOST
|
self._oauth_host = DEFAULT_OAUTH2_API_HOST
|
||||||
else:
|
else:
|
||||||
self._oauth_host = f'{cloud_server}.{DEFAULT_OAUTH2_API_HOST}'
|
self._oauth_host = f'{cloud_server}.{DEFAULT_OAUTH2_API_HOST}'
|
||||||
|
self._device_id = f'ha.{uuid}'
|
||||||
self._session = aiohttp.ClientSession(loop=self._main_loop)
|
self._session = aiohttp.ClientSession(loop=self._main_loop)
|
||||||
|
|
||||||
async def deinit_async(self) -> None:
|
async def deinit_async(self) -> None:
|
||||||
@ -132,6 +136,7 @@ class MIoTOauthClient:
|
|||||||
'redirect_uri': redirect_url or self._redirect_url,
|
'redirect_uri': redirect_url or self._redirect_url,
|
||||||
'client_id': self._client_id,
|
'client_id': self._client_id,
|
||||||
'response_type': 'code',
|
'response_type': 'code',
|
||||||
|
'device_id': self._device_id
|
||||||
}
|
}
|
||||||
if state:
|
if state:
|
||||||
params['state'] = state
|
params['state'] = state
|
||||||
@ -191,6 +196,7 @@ class MIoTOauthClient:
|
|||||||
'client_id': self._client_id,
|
'client_id': self._client_id,
|
||||||
'redirect_uri': self._redirect_url,
|
'redirect_uri': self._redirect_url,
|
||||||
'code': code,
|
'code': code,
|
||||||
|
'device_id': self._device_id
|
||||||
})
|
})
|
||||||
|
|
||||||
async def refresh_access_token_async(self, refresh_token: str) -> dict:
|
async def refresh_access_token_async(self, refresh_token: str) -> dict:
|
||||||
|
@ -1,324 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Copyright (C) 2024 Xiaomi Corporation.
|
|
||||||
|
|
||||||
The ownership and intellectual property rights of Xiaomi Home Assistant
|
|
||||||
Integration and related Xiaomi cloud service API interface provided under this
|
|
||||||
license, including source code and object code (collectively, "Licensed Work"),
|
|
||||||
are owned by Xiaomi. Subject to the terms and conditions of this License, Xiaomi
|
|
||||||
hereby grants you a personal, limited, non-exclusive, non-transferable,
|
|
||||||
non-sublicensable, and royalty-free license to reproduce, use, modify, and
|
|
||||||
distribute the Licensed Work only for your use of Home Assistant for
|
|
||||||
non-commercial purposes. For the avoidance of doubt, Xiaomi does not authorize
|
|
||||||
you to use the Licensed Work for any other purpose, including but not limited
|
|
||||||
to use Licensed Work to develop applications (APP), Web services, and other
|
|
||||||
forms of software.
|
|
||||||
|
|
||||||
You may reproduce and distribute copies of the Licensed Work, with or without
|
|
||||||
modifications, whether in source or object form, provided that you must give
|
|
||||||
any other recipients of the Licensed Work a copy of this License and retain all
|
|
||||||
copyright and disclaimers.
|
|
||||||
|
|
||||||
Xiaomi provides the Licensed Work on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
||||||
CONDITIONS OF ANY KIND, either express or implied, including, without
|
|
||||||
limitation, any warranties, undertakes, or conditions of TITLE, NO ERROR OR
|
|
||||||
OMISSION, CONTINUITY, RELIABILITY, NON-INFRINGEMENT, MERCHANTABILITY, or
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE. In any event, you are solely responsible
|
|
||||||
for any direct, indirect, special, incidental, or consequential damages or
|
|
||||||
losses arising from the use or inability to use the Licensed Work.
|
|
||||||
|
|
||||||
Xiaomi reserves all rights not expressly granted to you in this License.
|
|
||||||
Except for the rights expressly granted by Xiaomi under this License, Xiaomi
|
|
||||||
does not authorize you in any form to use the trademarks, copyrights, or other
|
|
||||||
forms of intellectual property rights of Xiaomi and its affiliates, including,
|
|
||||||
without limitation, without obtaining other written permission from Xiaomi, you
|
|
||||||
shall not use "Xiaomi", "Mijia" and other words related to Xiaomi or words that
|
|
||||||
may make the public associate with Xiaomi in any form to publicize or promote
|
|
||||||
the software or hardware devices that use the Licensed Work.
|
|
||||||
|
|
||||||
Xiaomi has the right to immediately terminate all your authorization under this
|
|
||||||
License in the event:
|
|
||||||
1. You assert patent invalidation, litigation, or other claims against patents
|
|
||||||
or other intellectual property rights of Xiaomi or its affiliates; or,
|
|
||||||
2. You make, have made, manufacture, sell, or offer to sell products that knock
|
|
||||||
off Xiaomi or its affiliates' products.
|
|
||||||
|
|
||||||
MIoT event loop.
|
|
||||||
"""
|
|
||||||
import selectors
|
|
||||||
import heapq
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
from typing import Any, Callable, TypeVar
|
|
||||||
import logging
|
|
||||||
import threading
|
|
||||||
|
|
||||||
# pylint: disable=relative-beyond-top-level
|
|
||||||
from .miot_error import MIoTEvError
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
TimeoutHandle = TypeVar('TimeoutHandle')
|
|
||||||
|
|
||||||
|
|
||||||
class MIoTFdHandler:
|
|
||||||
"""File descriptor handler."""
|
|
||||||
fd: int
|
|
||||||
read_handler: Callable[[Any], None]
|
|
||||||
read_handler_ctx: Any
|
|
||||||
write_handler: Callable[[Any], None]
|
|
||||||
write_handler_ctx: Any
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, fd: int,
|
|
||||||
read_handler: Callable[[Any], None] = None,
|
|
||||||
read_handler_ctx: Any = None,
|
|
||||||
write_handler: Callable[[Any], None] = None,
|
|
||||||
write_handler_ctx: Any = None
|
|
||||||
) -> None:
|
|
||||||
self.fd = fd
|
|
||||||
self.read_handler = read_handler
|
|
||||||
self.read_handler_ctx = read_handler_ctx
|
|
||||||
self.write_handler = write_handler
|
|
||||||
self.write_handler_ctx = write_handler_ctx
|
|
||||||
|
|
||||||
|
|
||||||
class MIoTTimeout:
|
|
||||||
"""Timeout handler."""
|
|
||||||
key: TimeoutHandle
|
|
||||||
target: int
|
|
||||||
handler: Callable[[Any], None]
|
|
||||||
handler_ctx: Any
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, key: str = None, target: int = None,
|
|
||||||
handler: Callable[[Any], None] = None,
|
|
||||||
handler_ctx: Any = None
|
|
||||||
) -> None:
|
|
||||||
self.key = key
|
|
||||||
self.target = target
|
|
||||||
self.handler = handler
|
|
||||||
self.handler_ctx = handler_ctx
|
|
||||||
|
|
||||||
def __lt__(self, other):
|
|
||||||
return self.target < other.target
|
|
||||||
|
|
||||||
|
|
||||||
class MIoTEventLoop:
|
|
||||||
"""MIoT event loop."""
|
|
||||||
_poll_fd: selectors.DefaultSelector
|
|
||||||
|
|
||||||
_fd_handlers: dict[str, MIoTFdHandler]
|
|
||||||
|
|
||||||
_timer_heap: list[MIoTTimeout]
|
|
||||||
_timer_handlers: dict[str, MIoTTimeout]
|
|
||||||
_timer_handle_seed: int
|
|
||||||
|
|
||||||
# Label if the current fd handler is freed inside a read handler to
|
|
||||||
# avoid invalid reading.
|
|
||||||
_fd_handler_freed_in_read_handler: bool
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self._poll_fd = selectors.DefaultSelector()
|
|
||||||
self._timer_heap = []
|
|
||||||
self._timer_handlers = {}
|
|
||||||
self._timer_handle_seed = 1
|
|
||||||
self._fd_handlers = {}
|
|
||||||
self._fd_handler_freed_in_read_handler = False
|
|
||||||
|
|
||||||
def loop_forever(self) -> None:
|
|
||||||
"""Run an event loop in current thread."""
|
|
||||||
next_timeout: int
|
|
||||||
while True:
|
|
||||||
next_timeout = 0
|
|
||||||
# Handle timer
|
|
||||||
now_ms: int = self.__get_monotonic_ms
|
|
||||||
while len(self._timer_heap) > 0:
|
|
||||||
timer: MIoTTimeout = self._timer_heap[0]
|
|
||||||
if timer is None:
|
|
||||||
break
|
|
||||||
if timer.target <= now_ms:
|
|
||||||
heapq.heappop(self._timer_heap)
|
|
||||||
del self._timer_handlers[timer.key]
|
|
||||||
if timer.handler:
|
|
||||||
timer.handler(timer.handler_ctx)
|
|
||||||
else:
|
|
||||||
next_timeout = timer.target-now_ms
|
|
||||||
break
|
|
||||||
# Are there any files to listen to
|
|
||||||
if next_timeout == 0 and self._fd_handlers:
|
|
||||||
next_timeout = None # None == infinite
|
|
||||||
# Wait for timers & fds
|
|
||||||
if next_timeout == 0:
|
|
||||||
# Neither timer nor fds exist, exit loop
|
|
||||||
break
|
|
||||||
# Handle fd event
|
|
||||||
events = self._poll_fd.select(
|
|
||||||
timeout=next_timeout/1000.0 if next_timeout else next_timeout)
|
|
||||||
for key, mask in events:
|
|
||||||
fd_handler: MIoTFdHandler = key.data
|
|
||||||
if fd_handler is None:
|
|
||||||
continue
|
|
||||||
self._fd_handler_freed_in_read_handler = False
|
|
||||||
fd_key = str(id(fd_handler.fd))
|
|
||||||
if fd_key not in self._fd_handlers:
|
|
||||||
continue
|
|
||||||
if (
|
|
||||||
mask & selectors.EVENT_READ > 0
|
|
||||||
and fd_handler.read_handler
|
|
||||||
):
|
|
||||||
fd_handler.read_handler(fd_handler.read_handler_ctx)
|
|
||||||
if (
|
|
||||||
mask & selectors.EVENT_WRITE > 0
|
|
||||||
and self._fd_handler_freed_in_read_handler is False
|
|
||||||
and fd_handler.write_handler
|
|
||||||
):
|
|
||||||
fd_handler.write_handler(fd_handler.write_handler_ctx)
|
|
||||||
|
|
||||||
def loop_stop(self) -> None:
|
|
||||||
"""Stop the event loop."""
|
|
||||||
if self._poll_fd:
|
|
||||||
self._poll_fd.close()
|
|
||||||
self._poll_fd = None
|
|
||||||
self._fd_handlers = {}
|
|
||||||
self._timer_heap = []
|
|
||||||
self._timer_handlers = {}
|
|
||||||
|
|
||||||
def set_timeout(
|
|
||||||
self, timeout_ms: int, handler: Callable[[Any], None],
|
|
||||||
handler_ctx: Any = None
|
|
||||||
) -> TimeoutHandle:
|
|
||||||
"""Set a timer."""
|
|
||||||
if timeout_ms is None or handler is None:
|
|
||||||
raise MIoTEvError('invalid params')
|
|
||||||
new_timeout: MIoTTimeout = MIoTTimeout()
|
|
||||||
new_timeout.key = self.__get_next_timeout_handle
|
|
||||||
new_timeout.target = self.__get_monotonic_ms + timeout_ms
|
|
||||||
new_timeout.handler = handler
|
|
||||||
new_timeout.handler_ctx = handler_ctx
|
|
||||||
heapq.heappush(self._timer_heap, new_timeout)
|
|
||||||
self._timer_handlers[new_timeout.key] = new_timeout
|
|
||||||
return new_timeout.key
|
|
||||||
|
|
||||||
def clear_timeout(self, timer_key: TimeoutHandle) -> None:
|
|
||||||
"""Stop and remove the timer."""
|
|
||||||
if timer_key is None:
|
|
||||||
return
|
|
||||||
timer: MIoTTimeout = self._timer_handlers.pop(timer_key, None)
|
|
||||||
if timer:
|
|
||||||
self._timer_heap = list(self._timer_heap)
|
|
||||||
self._timer_heap.remove(timer)
|
|
||||||
heapq.heapify(self._timer_heap)
|
|
||||||
|
|
||||||
def set_read_handler(
|
|
||||||
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any = None
|
|
||||||
) -> bool:
|
|
||||||
"""Set a read handler for a file descriptor.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True, success. False, failed.
|
|
||||||
"""
|
|
||||||
self.__set_handler(
|
|
||||||
fd, is_read=True, handler=handler, handler_ctx=handler_ctx)
|
|
||||||
|
|
||||||
def set_write_handler(
|
|
||||||
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any = None
|
|
||||||
) -> bool:
|
|
||||||
"""Set a write handler for a file descriptor.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True, success. False, failed.
|
|
||||||
"""
|
|
||||||
self.__set_handler(
|
|
||||||
fd, is_read=False, handler=handler, handler_ctx=handler_ctx)
|
|
||||||
|
|
||||||
def __set_handler(
|
|
||||||
self, fd, is_read: bool, handler: Callable[[Any], None],
|
|
||||||
handler_ctx: Any = None
|
|
||||||
) -> bool:
|
|
||||||
"""Set a handler."""
|
|
||||||
if fd is None:
|
|
||||||
raise MIoTEvError('invalid params')
|
|
||||||
|
|
||||||
if not self._poll_fd:
|
|
||||||
raise MIoTEvError('event loop not started')
|
|
||||||
|
|
||||||
fd_key: str = str(id(fd))
|
|
||||||
fd_handler = self._fd_handlers.get(fd_key, None)
|
|
||||||
|
|
||||||
if fd_handler is None:
|
|
||||||
fd_handler = MIoTFdHandler(fd=fd)
|
|
||||||
fd_handler.fd = fd
|
|
||||||
self._fd_handlers[fd_key] = fd_handler
|
|
||||||
|
|
||||||
read_handler_existed = fd_handler.read_handler is not None
|
|
||||||
write_handler_existed = fd_handler.write_handler is not None
|
|
||||||
if is_read is True:
|
|
||||||
fd_handler.read_handler = handler
|
|
||||||
fd_handler.read_handler_ctx = handler_ctx
|
|
||||||
else:
|
|
||||||
fd_handler.write_handler = handler
|
|
||||||
fd_handler.write_handler_ctx = handler_ctx
|
|
||||||
|
|
||||||
if fd_handler.read_handler is None and fd_handler.write_handler is None:
|
|
||||||
# Remove from epoll and map
|
|
||||||
try:
|
|
||||||
self._poll_fd.unregister(fd)
|
|
||||||
except (KeyError, ValueError, OSError) as e:
|
|
||||||
del e
|
|
||||||
self._fd_handlers.pop(fd_key, None)
|
|
||||||
# May be inside a read handler, if not, this has no effect
|
|
||||||
self._fd_handler_freed_in_read_handler = True
|
|
||||||
elif read_handler_existed is False and write_handler_existed is False:
|
|
||||||
# Add to epoll
|
|
||||||
events = 0x0
|
|
||||||
if fd_handler.read_handler:
|
|
||||||
events |= selectors.EVENT_READ
|
|
||||||
if fd_handler.write_handler:
|
|
||||||
events |= selectors.EVENT_WRITE
|
|
||||||
try:
|
|
||||||
self._poll_fd.register(fd, events=events, data=fd_handler)
|
|
||||||
except (KeyError, ValueError, OSError) as e:
|
|
||||||
_LOGGER.error(
|
|
||||||
'%s, register fd, error, %s, %s, %s, %s, %s',
|
|
||||||
threading.current_thread().name,
|
|
||||||
'read' if is_read else 'write',
|
|
||||||
fd_key, handler, e, traceback.format_exc())
|
|
||||||
self._fd_handlers.pop(fd_key, None)
|
|
||||||
return False
|
|
||||||
elif (
|
|
||||||
read_handler_existed != (fd_handler.read_handler is not None)
|
|
||||||
or write_handler_existed != (fd_handler.write_handler is not None)
|
|
||||||
):
|
|
||||||
# Modify epoll
|
|
||||||
events = 0x0
|
|
||||||
if fd_handler.read_handler:
|
|
||||||
events |= selectors.EVENT_READ
|
|
||||||
if fd_handler.write_handler:
|
|
||||||
events |= selectors.EVENT_WRITE
|
|
||||||
try:
|
|
||||||
self._poll_fd.modify(fd, events=events, data=fd_handler)
|
|
||||||
except (KeyError, ValueError, OSError) as e:
|
|
||||||
_LOGGER.error(
|
|
||||||
'%s, modify fd, error, %s, %s, %s, %s, %s',
|
|
||||||
threading.current_thread().name,
|
|
||||||
'read' if is_read else 'write',
|
|
||||||
fd_key, handler, e, traceback.format_exc())
|
|
||||||
self._fd_handlers.pop(fd_key, None)
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def __get_next_timeout_handle(self) -> str:
|
|
||||||
# Get next timeout handle, that is not larger than the maximum
|
|
||||||
# value of UINT64 type.
|
|
||||||
self._timer_handle_seed += 1
|
|
||||||
# uint64 max
|
|
||||||
self._timer_handle_seed %= 0xFFFFFFFFFFFFFFFF
|
|
||||||
return str(self._timer_handle_seed)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def __get_monotonic_ms(self) -> int:
|
|
||||||
"""Get monotonic ms timestamp."""
|
|
||||||
return int(time.monotonic()*1000)
|
|
@ -48,7 +48,7 @@ MIoT internationalization translation.
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
from typing import Optional, Union
|
||||||
|
|
||||||
# pylint: disable=relative-beyond-top-level
|
# pylint: disable=relative-beyond-top-level
|
||||||
from .common import load_json_file
|
from .common import load_json_file
|
||||||
@ -98,7 +98,7 @@ class MIoTI18n:
|
|||||||
|
|
||||||
def translate(
|
def translate(
|
||||||
self, key: str, replace: Optional[dict[str, str]] = None
|
self, key: str, replace: Optional[dict[str, str]] = None
|
||||||
) -> str | dict | None:
|
) -> Union[str, dict, None]:
|
||||||
result = self._data
|
result = self._data
|
||||||
for item in key.split('.'):
|
for item in key.split('.'):
|
||||||
if item not in result:
|
if item not in result:
|
||||||
|
@ -381,7 +381,8 @@ class _MIoTLanDevice:
|
|||||||
_MIoTLanDeviceState(state.value+1))
|
_MIoTLanDeviceState(state.value+1))
|
||||||
# Fast ping
|
# Fast ping
|
||||||
if self._if_name is None:
|
if self._if_name is None:
|
||||||
_LOGGER.error('if_name is Not set for device, %s', self.did)
|
_LOGGER.error(
|
||||||
|
'if_name is Not set for device, %s', self.did)
|
||||||
return
|
return
|
||||||
if self.ip is None:
|
if self.ip is None:
|
||||||
_LOGGER.error('ip is Not set for device, %s', self.did)
|
_LOGGER.error('ip is Not set for device, %s', self.did)
|
||||||
@ -419,10 +420,10 @@ class _MIoTLanDevice:
|
|||||||
self.online = True
|
self.online = True
|
||||||
else:
|
else:
|
||||||
_LOGGER.info('unstable device detected, %s', self.did)
|
_LOGGER.info('unstable device detected, %s', self.did)
|
||||||
self._online_offline_timer = \
|
self._online_offline_timer = (
|
||||||
self._manager.internal_loop.call_later(
|
self._manager.internal_loop.call_later(
|
||||||
self.NETWORK_UNSTABLE_RESUME_TH,
|
self.NETWORK_UNSTABLE_RESUME_TH,
|
||||||
self.__online_resume_handler)
|
self.__online_resume_handler))
|
||||||
|
|
||||||
def __online_resume_handler(self) -> None:
|
def __online_resume_handler(self) -> None:
|
||||||
_LOGGER.info('unstable resume threshold past, %s', self.did)
|
_LOGGER.info('unstable resume threshold past, %s', self.did)
|
||||||
@ -508,9 +509,9 @@ class MIoTLan:
|
|||||||
key='miot_lan', group_id='*',
|
key='miot_lan', group_id='*',
|
||||||
handler=self.__on_mips_service_change)
|
handler=self.__on_mips_service_change)
|
||||||
self._enable_subscribe = enable_subscribe
|
self._enable_subscribe = enable_subscribe
|
||||||
self._virtual_did = str(virtual_did) \
|
self._virtual_did = (
|
||||||
if (virtual_did is not None) \
|
str(virtual_did) if (virtual_did is not None)
|
||||||
else str(secrets.randbits(64))
|
else str(secrets.randbits(64)))
|
||||||
# Init socket probe message
|
# Init socket probe message
|
||||||
probe_bytes = bytearray(self.OT_PROBE_LEN)
|
probe_bytes = bytearray(self.OT_PROBE_LEN)
|
||||||
probe_bytes[:20] = (
|
probe_bytes[:20] = (
|
||||||
@ -948,7 +949,7 @@ class MIoTLan:
|
|||||||
|
|
||||||
# The following methods SHOULD ONLY be called in the internal loop
|
# The following methods SHOULD ONLY be called in the internal loop
|
||||||
|
|
||||||
def ping(self, if_name: str | None, target_ip: str) -> None:
|
def ping(self, if_name: Optional[str], target_ip: str) -> None:
|
||||||
if not target_ip:
|
if not target_ip:
|
||||||
return
|
return
|
||||||
self.__sendto(
|
self.__sendto(
|
||||||
@ -964,7 +965,7 @@ class MIoTLan:
|
|||||||
) -> None:
|
) -> None:
|
||||||
if timeout_ms and not handler:
|
if timeout_ms and not handler:
|
||||||
raise ValueError('handler is required when timeout_ms is set')
|
raise ValueError('handler is required when timeout_ms is set')
|
||||||
device: _MIoTLanDevice | None = self._lan_devices.get(did)
|
device: Optional[_MIoTLanDevice] = self._lan_devices.get(did)
|
||||||
if not device:
|
if not device:
|
||||||
raise ValueError('invalid device')
|
raise ValueError('invalid device')
|
||||||
if not device.cipher:
|
if not device.cipher:
|
||||||
@ -1232,7 +1233,7 @@ class MIoTLan:
|
|||||||
return
|
return
|
||||||
# Keep alive message
|
# Keep alive message
|
||||||
did: str = str(struct.unpack('>Q', data[4:12])[0])
|
did: str = str(struct.unpack('>Q', data[4:12])[0])
|
||||||
device: _MIoTLanDevice | None = self._lan_devices.get(did)
|
device: Optional[_MIoTLanDevice] = self._lan_devices.get(did)
|
||||||
if not device:
|
if not device:
|
||||||
return
|
return
|
||||||
timestamp: int = struct.unpack('>I', data[12:16])[0]
|
timestamp: int = struct.unpack('>I', data[12:16])[0]
|
||||||
@ -1272,8 +1273,8 @@ class MIoTLan:
|
|||||||
_LOGGER.warning('invalid message, no id, %s, %s', did, msg)
|
_LOGGER.warning('invalid message, no id, %s, %s', did, msg)
|
||||||
return
|
return
|
||||||
# Reply
|
# Reply
|
||||||
req: _MIoTLanRequestData | None = \
|
req: Optional[_MIoTLanRequestData] = (
|
||||||
self._pending_requests.pop(msg['id'], None)
|
self._pending_requests.pop(msg['id'], None))
|
||||||
if req:
|
if req:
|
||||||
if req.timeout:
|
if req.timeout:
|
||||||
req.timeout.cancel()
|
req.timeout.cancel()
|
||||||
@ -1334,7 +1335,7 @@ class MIoTLan:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def __sendto(
|
def __sendto(
|
||||||
self, if_name: str | None, data: bytes, address: str, port: int
|
self, if_name: Optional[str], data: bytes, address: str, port: int
|
||||||
) -> None:
|
) -> None:
|
||||||
if if_name is None:
|
if if_name is None:
|
||||||
# Broadcast
|
# Broadcast
|
||||||
@ -1356,7 +1357,7 @@ class MIoTLan:
|
|||||||
try:
|
try:
|
||||||
# Scan devices
|
# Scan devices
|
||||||
self.ping(if_name=None, target_ip='255.255.255.255')
|
self.ping(if_name=None, target_ip='255.255.255.255')
|
||||||
except Exception as err: # pylint: disable=broad-exception-caught
|
except Exception as err: # pylint: disable=broad-exception-caught
|
||||||
# Ignore any exceptions to avoid blocking the loop
|
# Ignore any exceptions to avoid blocking the loop
|
||||||
_LOGGER.error('ping device error, %s', err)
|
_LOGGER.error('ping device error, %s', err)
|
||||||
pass
|
pass
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Xiaomi MQTT Broker-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration."
|
"unreachable_mqtt_broker": "Xiaomi MQTT Broker-Adresse ist nicht erreichbar, bitte überprüfen Sie die Netzwerkkonfiguration."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Fehler beim Abrufen der Home Assistant-UUID.",
|
||||||
"network_connect_error": "Konfiguration fehlgeschlagen. Netzwerkverbindung fehlgeschlagen. Überprüfen Sie die Netzwerkkonfiguration des Geräts.",
|
"network_connect_error": "Konfiguration fehlgeschlagen. Netzwerkverbindung fehlgeschlagen. Überprüfen Sie die Netzwerkkonfiguration des Geräts.",
|
||||||
"already_configured": "Dieser Benutzer hat die Konfiguration bereits abgeschlossen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Konfiguration zu ändern.",
|
"already_configured": "Dieser Benutzer hat die Konfiguration bereits abgeschlossen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Konfiguration zu ändern.",
|
||||||
"invalid_auth_info": "Authentifizierungsinformationen sind abgelaufen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Authentifizierung erneut durchzuführen.",
|
"invalid_auth_info": "Authentifizierungsinformationen sind abgelaufen. Gehen Sie zur Integrationsseite und klicken Sie auf die Schaltfläche \"Konfiguration\", um die Authentifizierung erneut durchzuführen.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Unable to reach Xiaomi MQTT Broker address, please check network configuration."
|
"unreachable_mqtt_broker": "Unable to reach Xiaomi MQTT Broker address, please check network configuration."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Failed to get Home Assistant UUID.",
|
||||||
"network_connect_error": "Configuration failed. The network connection is abnormal. Please check the equipment network configuration.",
|
"network_connect_error": "Configuration failed. The network connection is abnormal. Please check the equipment network configuration.",
|
||||||
"already_configured": "Configuration for this user is already completed. Please go to the integration page and click the CONFIGURE button for modifications.",
|
"already_configured": "Configuration for this user is already completed. Please go to the integration page and click the CONFIGURE button for modifications.",
|
||||||
"invalid_auth_info": "Authentication information has expired. Please go to the integration page and click the CONFIGURE button to re-authenticate.",
|
"invalid_auth_info": "Authentication information has expired. Please go to the integration page and click the CONFIGURE button to re-authenticate.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "No se puede acceder a la dirección del Broker MQTT de Xiaomi, por favor verifique la configuración de la red."
|
"unreachable_mqtt_broker": "No se puede acceder a la dirección del Broker MQTT de Xiaomi, por favor verifique la configuración de la red."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Error al obtener el UUID de Home Assistant.",
|
||||||
"network_connect_error": "La configuración ha fallado. Existe un problema con la conexión de red, verifique la configuración de red del dispositivo.",
|
"network_connect_error": "La configuración ha fallado. Existe un problema con la conexión de red, verifique la configuración de red del dispositivo.",
|
||||||
"already_configured": "Esta cuenta ya ha finalizado la configuración. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para modificar la configuración.",
|
"already_configured": "Esta cuenta ya ha finalizado la configuración. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para modificar la configuración.",
|
||||||
"invalid_auth_info": "La información de autorización ha caducado. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para volver a autenticarse.",
|
"invalid_auth_info": "La información de autorización ha caducado. Ingrese a la página de integración y haga clic en el botón \"Configurar\" para volver a autenticarse.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Impossible d'atteindre l'adresse du Broker MQTT de Xiaomi, veuillez vérifier la configuration réseau."
|
"unreachable_mqtt_broker": "Impossible d'atteindre l'adresse du Broker MQTT de Xiaomi, veuillez vérifier la configuration réseau."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Échec de l'obtention de l'UUID de Home Assistant.",
|
||||||
"network_connect_error": "La configuration a échoué. Erreur de connexion réseau. Veuillez vérifier la configuration du réseau de l'appareil.",
|
"network_connect_error": "La configuration a échoué. Erreur de connexion réseau. Veuillez vérifier la configuration du réseau de l'appareil.",
|
||||||
"already_configured": "Cet utilisateur a déjà terminé la configuration. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour modifier la configuration.",
|
"already_configured": "Cet utilisateur a déjà terminé la configuration. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour modifier la configuration.",
|
||||||
"invalid_auth_info": "Les informations d'authentification ont expiré. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour vous authentifier à nouveau.",
|
"invalid_auth_info": "Les informations d'authentification ont expiré. Veuillez accéder à la page d'intégration et cliquer sur le bouton \"Configurer\" pour vous authentifier à nouveau.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Xiaomi MQTT ブローカーアドレスにアクセスできません。ネットワーク設定を確認してください。"
|
"unreachable_mqtt_broker": "Xiaomi MQTT ブローカーアドレスにアクセスできません。ネットワーク設定を確認してください。"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Home Assistant インスタンスIDを取得できませんでした。",
|
||||||
"network_connect_error": "設定に失敗しました。ネットワーク接続に異常があります。デバイスのネットワーク設定を確認してください。",
|
"network_connect_error": "設定に失敗しました。ネットワーク接続に異常があります。デバイスのネットワーク設定を確認してください。",
|
||||||
"already_configured": "このユーザーはすでに設定が完了しています。統合ページにアクセスして、「設定」ボタンをクリックして設定を変更してください。",
|
"already_configured": "このユーザーはすでに設定が完了しています。統合ページにアクセスして、「設定」ボタンをクリックして設定を変更してください。",
|
||||||
"invalid_auth_info": "認証情報が期限切れになりました。統合ページにアクセスして、「設定」ボタンをクリックして再度認証してください。",
|
"invalid_auth_info": "認証情報が期限切れになりました。統合ページにアクセスして、「設定」ボタンをクリックして再度認証してください。",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Kan Xiaomi MQTT Broker-adres niet bereiken, controleer de netwerkconfiguratie."
|
"unreachable_mqtt_broker": "Kan Xiaomi MQTT Broker-adres niet bereiken, controleer de netwerkconfiguratie."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Mislukt bij het ophalen van Home Assistant UUID.",
|
||||||
"network_connect_error": "Configuratie mislukt. De netwerkverbinding is abnormaal. Controleer de netwerkinstellingen van de apparatuur.",
|
"network_connect_error": "Configuratie mislukt. De netwerkverbinding is abnormaal. Controleer de netwerkinstellingen van de apparatuur.",
|
||||||
"already_configured": "Configuratie voor deze gebruiker is al voltooid. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om wijzigingen aan te brengen.",
|
"already_configured": "Configuratie voor deze gebruiker is al voltooid. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om wijzigingen aan te brengen.",
|
||||||
"invalid_auth_info": "Authenticatie-informatie is verlopen. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om opnieuw te authentiseren.",
|
"invalid_auth_info": "Authenticatie-informatie is verlopen. Ga naar de integratiepagina en klik op de CONFIGUREER-knop om opnieuw te authentiseren.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Falha ao obter o UUID do Home Assistant.",
|
||||||
"network_connect_error": "Configuração falhou. A conexão de rede está anormal. Verifique a configuração de rede do equipamento.",
|
"network_connect_error": "Configuração falhou. A conexão de rede está anormal. Verifique a configuração de rede do equipamento.",
|
||||||
"already_configured": "A configuração para este usuário já foi concluída. Vá para a página de integrações e clique no botão CONFIGURAR para modificações.",
|
"already_configured": "A configuração para este usuário já foi concluída. Vá para a página de integrações e clique no botão CONFIGURAR para modificações.",
|
||||||
"invalid_auth_info": "As informações de autenticação expiraram. Vá para a página de integrações e clique em CONFIGURAR para reautenticar.",
|
"invalid_auth_info": "As informações de autenticação expiraram. Vá para a página de integrações e clique em CONFIGURAR para reautenticar.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
"unreachable_mqtt_broker": "Não é possível acessar o endereço do Broker MQTT da Xiaomi, verifique a configuração da rede."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Não foi possível obter o UUID do Home Assistant.",
|
||||||
"network_connect_error": "A configuração falhou. A ligação de rede é anormal. Verifique a configuração de rede do equipamento.",
|
"network_connect_error": "A configuração falhou. A ligação de rede é anormal. Verifique a configuração de rede do equipamento.",
|
||||||
"already_configured": "A configuração para este utilizador já foi concluída. Vá à página de integrações e clique em CONFIGURAR para efetuar alterações.",
|
"already_configured": "A configuração para este utilizador já foi concluída. Vá à página de integrações e clique em CONFIGURAR para efetuar alterações.",
|
||||||
"invalid_auth_info": "A informação de autenticação expirou. Vá à página de integrações e clique em CONFIGURAR para reautenticar.",
|
"invalid_auth_info": "A informação de autenticação expirou. Vá à página de integrações e clique em CONFIGURAR para reautenticar.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "Не удается подключиться к адресу MQTT брокера Xiaomi, проверьте настройки сети."
|
"unreachable_mqtt_broker": "Не удается подключиться к адресу MQTT брокера Xiaomi, проверьте настройки сети."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "Не удалось получить UUID Home Assistant.",
|
||||||
"network_connect_error": "Ошибка настройки. Сетевое подключение недоступно. Проверьте настройки сети устройства.",
|
"network_connect_error": "Ошибка настройки. Сетевое подключение недоступно. Проверьте настройки сети устройства.",
|
||||||
"already_configured": "Этот пользователь уже настроен. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы изменить настройки.",
|
"already_configured": "Этот пользователь уже настроен. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы изменить настройки.",
|
||||||
"invalid_auth_info": "Информация об авторизации истекла. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы переавторизоваться.",
|
"invalid_auth_info": "Информация об авторизации истекла. Перейдите на страницу интеграции и нажмите кнопку «Настроить», чтобы переавторизоваться.",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "无法访问小米 MQTT Broker 地址,请检查网络配置。"
|
"unreachable_mqtt_broker": "无法访问小米 MQTT Broker 地址,请检查网络配置。"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "获取 Home Assistant UUID 失败。",
|
||||||
"network_connect_error": "配置失败。网络连接异常,请检查设备网络配置。",
|
"network_connect_error": "配置失败。网络连接异常,请检查设备网络配置。",
|
||||||
"already_configured": "该用户已配置完成。请进入集成页面,点击“配置”按钮修改配置。",
|
"already_configured": "该用户已配置完成。请进入集成页面,点击“配置”按钮修改配置。",
|
||||||
"invalid_auth_info": "认证信息已过期。请进入集成页面,点击“配置”按钮重新认证。",
|
"invalid_auth_info": "认证信息已过期。请进入集成页面,点击“配置”按钮重新认证。",
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
"unreachable_mqtt_broker": "無法訪問小米 MQTT Broker 地址,請檢查網絡配置。"
|
"unreachable_mqtt_broker": "無法訪問小米 MQTT Broker 地址,請檢查網絡配置。"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"ha_uuid_get_failed": "獲取 Home Assistant UUID 失敗。",
|
||||||
"network_connect_error": "配置失敗。網絡連接異常,請檢查設備網絡配置。",
|
"network_connect_error": "配置失敗。網絡連接異常,請檢查設備網絡配置。",
|
||||||
"already_configured": "該用戶已配置完成。請進入集成頁面,點擊“配置”按鈕修改配置。",
|
"already_configured": "該用戶已配置完成。請進入集成頁面,點擊“配置”按鈕修改配置。",
|
||||||
"invalid_auth_info": "認證信息已過期。請進入集成頁面,點擊“配置”按鈕重新認證。",
|
"invalid_auth_info": "認證信息已過期。請進入集成頁面,點擊“配置”按鈕重新認證。",
|
||||||
|
@ -20,7 +20,6 @@ def load_py_file():
|
|||||||
'const.py',
|
'const.py',
|
||||||
'miot_cloud.py',
|
'miot_cloud.py',
|
||||||
'miot_error.py',
|
'miot_error.py',
|
||||||
'miot_ev.py',
|
|
||||||
'miot_i18n.py',
|
'miot_i18n.py',
|
||||||
'miot_lan.py',
|
'miot_lan.py',
|
||||||
'miot_mdns.py',
|
'miot_mdns.py',
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""Unit test for miot_ev.py."""
|
|
||||||
import os
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
# pylint: disable=import-outside-toplevel, disable=unused-argument
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.github
|
|
||||||
def test_mev_timer_and_fd():
|
|
||||||
from miot.miot_ev import MIoTEventLoop, TimeoutHandle
|
|
||||||
|
|
||||||
mev = MIoTEventLoop()
|
|
||||||
assert mev
|
|
||||||
event_fd: os.eventfd = os.eventfd(0, os.O_NONBLOCK)
|
|
||||||
assert event_fd
|
|
||||||
timer4: TimeoutHandle = None
|
|
||||||
|
|
||||||
def event_handler(event_fd):
|
|
||||||
value: int = os.eventfd_read(event_fd)
|
|
||||||
if value == 1:
|
|
||||||
mev.clear_timeout(timer4)
|
|
||||||
print('cancel timer4')
|
|
||||||
elif value == 2:
|
|
||||||
print('event write twice in a row')
|
|
||||||
elif value == 3:
|
|
||||||
mev.set_read_handler(event_fd, None, None)
|
|
||||||
os.close(event_fd)
|
|
||||||
event_fd = None
|
|
||||||
print('close event fd')
|
|
||||||
|
|
||||||
def timer1_handler(event_fd):
|
|
||||||
os.eventfd_write(event_fd, 1)
|
|
||||||
|
|
||||||
def timer2_handler(event_fd):
|
|
||||||
os.eventfd_write(event_fd, 1)
|
|
||||||
os.eventfd_write(event_fd, 1)
|
|
||||||
|
|
||||||
def timer3_handler(event_fd):
|
|
||||||
os.eventfd_write(event_fd, 3)
|
|
||||||
|
|
||||||
def timer4_handler(event_fd):
|
|
||||||
raise ValueError('unreachable code')
|
|
||||||
|
|
||||||
mev.set_read_handler(
|
|
||||||
event_fd, event_handler, event_fd)
|
|
||||||
|
|
||||||
mev.set_timeout(500, timer1_handler, event_fd)
|
|
||||||
mev.set_timeout(1000, timer2_handler, event_fd)
|
|
||||||
mev.set_timeout(1500, timer3_handler, event_fd)
|
|
||||||
timer4 = mev.set_timeout(2000, timer4_handler, event_fd)
|
|
||||||
|
|
||||||
mev.loop_forever()
|
|
||||||
# Loop will exit when there are no timers or fd handlers.
|
|
||||||
mev.loop_stop()
|
|
Loading…
x
Reference in New Issue
Block a user