mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2025-04-22 17:42:48 +08:00
fix: fix type error, wrong use of any and Any (#338)
* fix: fix type error, wrong use of any and Any * fix: wrong use of session close * fix: fix test_lan type error * fix: remove __del__ * feat: oauth, http add deinit_async
This commit is contained in:
parent
afef709839
commit
c1867e2baf
@ -47,7 +47,7 @@ Climate entities for Xiaomi Home.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -415,7 +415,7 @@ class AirConditioner(MIoTServiceEntity, ClimateEntity):
|
|||||||
return SWING_OFF
|
return SWING_OFF
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __ac_state_changed(self, prop: MIoTSpecProperty, value: any) -> None:
|
def __ac_state_changed(self, prop: MIoTSpecProperty, value: Any) -> None:
|
||||||
del prop
|
del prop
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
|
@ -315,6 +315,9 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
_LOGGER.error('task_oauth exception, %s', error)
|
_LOGGER.error('task_oauth exception, %s', error)
|
||||||
self._config_error_reason = str(error)
|
self._config_error_reason = str(error)
|
||||||
return self.async_show_progress_done(next_step_id='oauth_error')
|
return self.async_show_progress_done(next_step_id='oauth_error')
|
||||||
|
if self._miot_oauth:
|
||||||
|
await self._miot_oauth.deinit_async()
|
||||||
|
self._miot_oauth = None
|
||||||
return self.async_show_progress_done(next_step_id='homes_select')
|
return self.async_show_progress_done(next_step_id='homes_select')
|
||||||
return self.async_show_progress(
|
return self.async_show_progress(
|
||||||
step_id='oauth',
|
step_id='oauth',
|
||||||
@ -336,10 +339,16 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
try:
|
try:
|
||||||
auth_info = await self._miot_oauth.get_access_token_async(
|
auth_info = await self._miot_oauth.get_access_token_async(
|
||||||
code=oauth_code)
|
code=oauth_code)
|
||||||
self._miot_http = MIoTHttpClient(
|
if not self._miot_http:
|
||||||
cloud_server=self._cloud_server,
|
self._miot_http = MIoTHttpClient(
|
||||||
client_id=OAUTH2_CLIENT_ID,
|
cloud_server=self._cloud_server,
|
||||||
access_token=auth_info['access_token'])
|
client_id=OAUTH2_CLIENT_ID,
|
||||||
|
access_token=auth_info['access_token'])
|
||||||
|
else:
|
||||||
|
self._miot_http.update_http_header(
|
||||||
|
cloud_server=self._cloud_server,
|
||||||
|
client_id=OAUTH2_CLIENT_ID,
|
||||||
|
access_token=auth_info['access_token'])
|
||||||
self._auth_info = auth_info
|
self._auth_info = auth_info
|
||||||
# Gen uuid
|
# Gen uuid
|
||||||
self._uuid = hashlib.sha256(
|
self._uuid = hashlib.sha256(
|
||||||
@ -449,6 +458,9 @@ class XiaomiMihomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
# Auth success, unregister oauth webhook
|
# Auth success, unregister oauth webhook
|
||||||
webhook_async_unregister(self.hass, webhook_id=self._virtual_did)
|
webhook_async_unregister(self.hass, webhook_id=self._virtual_did)
|
||||||
|
if self._miot_http:
|
||||||
|
await self._miot_http.deinit_async()
|
||||||
|
self._miot_http = None
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
'__check_oauth_async, webhook.async_unregister: %s',
|
'__check_oauth_async, webhook.async_unregister: %s',
|
||||||
self._virtual_did)
|
self._virtual_did)
|
||||||
|
@ -46,6 +46,7 @@ off Xiaomi or its affiliates' products.
|
|||||||
Event entities for Xiaomi Home.
|
Event entities for Xiaomi Home.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -84,6 +85,6 @@ class Event(MIoTEventEntity, EventEntity):
|
|||||||
# Set device_class
|
# Set device_class
|
||||||
self._attr_device_class = spec.device_class
|
self._attr_device_class = spec.device_class
|
||||||
|
|
||||||
def on_event_occurred(self, name: str, arguments: list[dict[int, any]]):
|
def on_event_occurred(self, name: str, arguments: list[dict[int, Any]]):
|
||||||
"""An event is occurred."""
|
"""An event is occurred."""
|
||||||
self._trigger_event(event_type=name, event_attributes=arguments)
|
self._trigger_event(event_type=name, event_attributes=arguments)
|
||||||
|
@ -93,7 +93,7 @@ class Fan(MIoTServiceEntity, FanEntity):
|
|||||||
_speed_min: Optional[int]
|
_speed_min: Optional[int]
|
||||||
_speed_max: Optional[int]
|
_speed_max: Optional[int]
|
||||||
_speed_step: Optional[int]
|
_speed_step: Optional[int]
|
||||||
_mode_list: Optional[dict[any, any]]
|
_mode_list: Optional[dict[Any, Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
|
@ -47,7 +47,7 @@ Humidifier entities for Xiaomi Home.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -97,7 +97,7 @@ class Humidifier(MIoTServiceEntity, HumidifierEntity):
|
|||||||
_prop_target_humidity: Optional[MIoTSpecProperty]
|
_prop_target_humidity: Optional[MIoTSpecProperty]
|
||||||
_prop_humidity: Optional[MIoTSpecProperty]
|
_prop_humidity: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
_mode_list: dict[any, any]
|
_mode_list: dict[Any, Any]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
|
@ -47,7 +47,7 @@ Light entities for Xiaomi Home.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -102,7 +102,7 @@ class Light(MIoTServiceEntity, LightEntity):
|
|||||||
_prop_mode: Optional[MIoTSpecProperty]
|
_prop_mode: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
_brightness_scale: Optional[tuple[int, int]]
|
_brightness_scale: Optional[tuple[int, int]]
|
||||||
_mode_list: Optional[dict[any, any]]
|
_mode_list: Optional[dict[Any, Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
|
@ -48,9 +48,9 @@ Common utilities.
|
|||||||
import json
|
import json
|
||||||
from os import path
|
from os import path
|
||||||
import random
|
import random
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
import hashlib
|
import hashlib
|
||||||
from paho.mqtt.client import MQTTMatcher
|
from paho.mqtt.matcher import MQTTMatcher
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
MIOT_ROOT_PATH: str = path.dirname(path.abspath(__file__))
|
MIOT_ROOT_PATH: str = path.dirname(path.abspath(__file__))
|
||||||
@ -87,7 +87,7 @@ def randomize_int(value: int, ratio: float) -> int:
|
|||||||
class MIoTMatcher(MQTTMatcher):
|
class MIoTMatcher(MQTTMatcher):
|
||||||
"""MIoT Pub/Sub topic matcher."""
|
"""MIoT Pub/Sub topic matcher."""
|
||||||
|
|
||||||
def iter_all_nodes(self) -> any:
|
def iter_all_nodes(self) -> Any:
|
||||||
"""Return an iterator on all nodes with their paths and contents."""
|
"""Return an iterator on all nodes with their paths and contents."""
|
||||||
def rec(node, path_):
|
def rec(node, path_):
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
@ -97,7 +97,7 @@ class MIoTMatcher(MQTTMatcher):
|
|||||||
yield from rec(child, path_ + [part])
|
yield from rec(child, path_ + [part])
|
||||||
return rec(self._root, [])
|
return rec(self._root, [])
|
||||||
|
|
||||||
def get(self, topic: str) -> Optional[any]:
|
def get(self, topic: str) -> Optional[Any]:
|
||||||
try:
|
try:
|
||||||
return self[topic]
|
return self[topic]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -1,7 +1,52 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""MIoT client instance."""
|
"""
|
||||||
|
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 client instance.
|
||||||
|
"""
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Callable, Optional, final
|
from typing import Any, Callable, Optional, final
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -36,9 +81,9 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
@dataclass
|
@dataclass
|
||||||
class MIoTClientSub:
|
class MIoTClientSub:
|
||||||
"""MIoT client subscription."""
|
"""MIoT client subscription."""
|
||||||
topic: str = None
|
topic: Optional[str]
|
||||||
handler: Callable[[dict, any], None] = None
|
handler: Callable[[dict, Any], None]
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'{self.topic}, {id(self.handler)}, {id(self.handler_ctx)}'
|
return f'{self.topic}, {id(self.handler)}, {id(self.handler_ctx)}'
|
||||||
@ -345,6 +390,8 @@ class MIoTClient:
|
|||||||
if self._show_devices_changed_notify_timer:
|
if self._show_devices_changed_notify_timer:
|
||||||
self._show_devices_changed_notify_timer.cancel()
|
self._show_devices_changed_notify_timer.cancel()
|
||||||
self._show_devices_changed_notify_timer = None
|
self._show_devices_changed_notify_timer = None
|
||||||
|
await self._oauth.deinit_async()
|
||||||
|
await self._http.deinit_async()
|
||||||
# Remove notify
|
# Remove notify
|
||||||
self._persistence_notify(
|
self._persistence_notify(
|
||||||
self.__gen_notify_key('dev_list_changed'), None, None)
|
self.__gen_notify_key('dev_list_changed'), None, None)
|
||||||
@ -526,7 +573,7 @@ class MIoTClient:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
async def set_prop_async(
|
async def set_prop_async(
|
||||||
self, did: str, siid: int, piid: int, value: any
|
self, did: str, siid: int, piid: int, value: Any
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if did not in self._device_list_cache:
|
if did not in self._device_list_cache:
|
||||||
raise MIoTClientError(f'did not exist, {did}')
|
raise MIoTClientError(f'did not exist, {did}')
|
||||||
@ -612,7 +659,7 @@ class MIoTClient:
|
|||||||
0.2, lambda: self._main_loop.create_task(
|
0.2, lambda: self._main_loop.create_task(
|
||||||
self.__refresh_props_handler()))
|
self.__refresh_props_handler()))
|
||||||
|
|
||||||
async def get_prop_async(self, did: str, siid: int, piid: int) -> any:
|
async def get_prop_async(self, did: str, siid: int, piid: int) -> Any:
|
||||||
if did not in self._device_list_cache:
|
if did not in self._device_list_cache:
|
||||||
raise MIoTClientError(f'did not exist, {did}')
|
raise MIoTClientError(f'did not exist, {did}')
|
||||||
|
|
||||||
@ -717,8 +764,8 @@ class MIoTClient:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def sub_prop(
|
def sub_prop(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, piid: int = None, handler_ctx: any = None
|
siid: int = None, piid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if did not in self._device_list_cache:
|
if did not in self._device_list_cache:
|
||||||
raise MIoTClientError(f'did not exist, {did}')
|
raise MIoTClientError(f'did not exist, {did}')
|
||||||
@ -741,8 +788,8 @@ class MIoTClient:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def sub_event(
|
def sub_event(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, eiid: int = None, handler_ctx: any = None
|
siid: int = None, eiid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if did not in self._device_list_cache:
|
if did not in self._device_list_cache:
|
||||||
raise MIoTClientError(f'did not exist, {did}')
|
raise MIoTClientError(f'did not exist, {did}')
|
||||||
@ -764,8 +811,8 @@ class MIoTClient:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def sub_device_state(
|
def sub_device_state(
|
||||||
self, did: str, handler: Callable[[str, MIoTDeviceState, any], None],
|
self, did: str, handler: Callable[[str, MIoTDeviceState, Any], None],
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Call callback handler in main loop"""
|
"""Call callback handler in main loop"""
|
||||||
if did not in self._device_list_cache:
|
if did not in self._device_list_cache:
|
||||||
@ -1060,7 +1107,7 @@ class MIoTClient:
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def __on_cloud_device_state_changed(
|
def __on_cloud_device_state_changed(
|
||||||
self, did: str, state: MIoTDeviceState, ctx: any
|
self, did: str, state: MIoTDeviceState, ctx: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
_LOGGER.info('cloud device state changed, %s, %s', did, state)
|
_LOGGER.info('cloud device state changed, %s, %s', did, state)
|
||||||
cloud_device = self._device_list_cloud.get(did, None)
|
cloud_device = self._device_list_cloud.get(did, None)
|
||||||
@ -1110,7 +1157,7 @@ class MIoTClient:
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
async def __on_lan_device_state_changed(
|
async def __on_lan_device_state_changed(
|
||||||
self, did: str, state: dict, ctx: any
|
self, did: str, state: dict, ctx: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
_LOGGER.info('lan device state changed, %s, %s', did, state)
|
_LOGGER.info('lan device state changed, %s, %s', did, state)
|
||||||
lan_state_new: bool = state.get('online', False)
|
lan_state_new: bool = state.get('online', False)
|
||||||
@ -1146,7 +1193,7 @@ class MIoTClient:
|
|||||||
self.__request_show_devices_changed_notify()
|
self.__request_show_devices_changed_notify()
|
||||||
|
|
||||||
@final
|
@final
|
||||||
def __on_prop_msg(self, params: dict, ctx: any) -> None:
|
def __on_prop_msg(self, params: dict, ctx: Any) -> None:
|
||||||
"""params MUST contain did, siid, piid, value"""
|
"""params MUST contain did, siid, piid, value"""
|
||||||
# BLE device has no online/offline msg
|
# BLE device has no online/offline msg
|
||||||
try:
|
try:
|
||||||
@ -1158,7 +1205,7 @@ class MIoTClient:
|
|||||||
_LOGGER.error('on prop msg error, %s, %s', params, err)
|
_LOGGER.error('on prop msg error, %s, %s', params, err)
|
||||||
|
|
||||||
@final
|
@final
|
||||||
def __on_event_msg(self, params: dict, ctx: any) -> None:
|
def __on_event_msg(self, params: dict, ctx: Any) -> None:
|
||||||
try:
|
try:
|
||||||
subs: list[MIoTClientSub] = list(self._sub_tree.iter_match(
|
subs: list[MIoTClientSub] = list(self._sub_tree.iter_match(
|
||||||
f'{params["did"]}/e/{params["siid"]}/{params["eiid"]}'))
|
f'{params["did"]}/e/{params["siid"]}/{params["eiid"]}'))
|
||||||
|
@ -51,7 +51,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
@ -94,10 +94,11 @@ 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._session = aiohttp.ClientSession()
|
self._session = aiohttp.ClientSession(loop=self._main_loop)
|
||||||
|
|
||||||
def __del__(self):
|
async def deinit_async(self) -> None:
|
||||||
self._session.close()
|
if self._session and not self._session.closed:
|
||||||
|
await self._session.close()
|
||||||
|
|
||||||
def set_redirect_url(self, redirect_url: str) -> None:
|
def set_redirect_url(self, redirect_url: str) -> None:
|
||||||
if not isinstance(redirect_url, str) or redirect_url.strip() == '':
|
if not isinstance(redirect_url, str) or redirect_url.strip() == '':
|
||||||
@ -250,10 +251,11 @@ class MIoTHttpClient:
|
|||||||
cloud_server=cloud_server, client_id=client_id,
|
cloud_server=cloud_server, client_id=client_id,
|
||||||
access_token=access_token)
|
access_token=access_token)
|
||||||
|
|
||||||
self._session = aiohttp.ClientSession()
|
self._session = aiohttp.ClientSession(loop=self._main_loop)
|
||||||
|
|
||||||
def __del__(self):
|
async def deinit_async(self) -> None:
|
||||||
self._session.close()
|
if self._session and not self._session.closed:
|
||||||
|
await self._session.close()
|
||||||
|
|
||||||
def update_http_header(
|
def update_http_header(
|
||||||
self, cloud_server: Optional[str] = None,
|
self, cloud_server: Optional[str] = None,
|
||||||
@ -581,7 +583,7 @@ class MIoTHttpClient:
|
|||||||
self, home_ids: list[str] = None
|
self, home_ids: list[str] = None
|
||||||
) -> dict[str, dict]:
|
) -> dict[str, dict]:
|
||||||
homeinfos = await self.get_homeinfos_async()
|
homeinfos = await self.get_homeinfos_async()
|
||||||
homes: dict[str, dict[str, any]] = {}
|
homes: dict[str, dict[str, Any]] = {}
|
||||||
devices: dict[str, dict] = {}
|
devices: dict[str, dict] = {}
|
||||||
for device_type in ['home_list', 'share_home_list']:
|
for device_type in ['home_list', 'share_home_list']:
|
||||||
homes.setdefault(device_type, {})
|
homes.setdefault(device_type, {})
|
||||||
@ -661,7 +663,7 @@ class MIoTHttpClient:
|
|||||||
raise MIoTHttpError('invalid response result')
|
raise MIoTHttpError('invalid response result')
|
||||||
return res_obj['result']
|
return res_obj['result']
|
||||||
|
|
||||||
async def __get_prop_async(self, did: str, siid: int, piid: int) -> any:
|
async def __get_prop_async(self, did: str, siid: int, piid: int) -> Any:
|
||||||
results = await self.get_props_async(
|
results = await self.get_props_async(
|
||||||
params=[{'did': did, 'siid': siid, 'piid': piid}])
|
params=[{'did': did, 'siid': siid, 'piid': piid}])
|
||||||
if not results:
|
if not results:
|
||||||
@ -722,7 +724,7 @@ class MIoTHttpClient:
|
|||||||
|
|
||||||
async def get_prop_async(
|
async def get_prop_async(
|
||||||
self, did: str, siid: int, piid: int, immediately: bool = False
|
self, did: str, siid: int, piid: int, immediately: bool = False
|
||||||
) -> any:
|
) -> Any:
|
||||||
if immediately:
|
if immediately:
|
||||||
return await self.__get_prop_async(did, siid, piid)
|
return await self.__get_prop_async(did, siid, piid)
|
||||||
key: str = f'{did}.{siid}.{piid}'
|
key: str = f'{did}.{siid}.{piid}'
|
||||||
|
@ -47,7 +47,7 @@ MIoT device instance.
|
|||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
@ -103,7 +103,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
class MIoTEntityData:
|
class MIoTEntityData:
|
||||||
"""MIoT Entity Data."""
|
"""MIoT Entity Data."""
|
||||||
platform: str
|
platform: str
|
||||||
device_class: any
|
device_class: Any
|
||||||
spec: MIoTSpecInstance | MIoTSpecService
|
spec: MIoTSpecInstance | MIoTSpecService
|
||||||
|
|
||||||
props: set[MIoTSpecProperty]
|
props: set[MIoTSpecProperty]
|
||||||
@ -243,8 +243,8 @@ class MIoTDevice:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def sub_property(
|
def sub_property(
|
||||||
self, handler: Callable[[dict, any], None], siid: int = None,
|
self, handler: Callable[[dict, Any], None], siid: int = None,
|
||||||
piid: int = None, handler_ctx: any = None
|
piid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
return self.miot_client.sub_prop(
|
return self.miot_client.sub_prop(
|
||||||
did=self._did, handler=handler, siid=siid, piid=piid,
|
did=self._did, handler=handler, siid=siid, piid=piid,
|
||||||
@ -254,8 +254,8 @@ class MIoTDevice:
|
|||||||
return self.miot_client.unsub_prop(did=self._did, siid=siid, piid=piid)
|
return self.miot_client.unsub_prop(did=self._did, siid=siid, piid=piid)
|
||||||
|
|
||||||
def sub_event(
|
def sub_event(
|
||||||
self, handler: Callable[[dict, any], None], siid: int = None,
|
self, handler: Callable[[dict, Any], None], siid: int = None,
|
||||||
eiid: int = None, handler_ctx: any = None
|
eiid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
return self.miot_client.sub_event(
|
return self.miot_client.sub_event(
|
||||||
did=self._did, handler=handler, siid=siid, eiid=eiid,
|
did=self._did, handler=handler, siid=siid, eiid=eiid,
|
||||||
@ -688,7 +688,7 @@ class MIoTDevice:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def __on_device_state_changed(
|
def __on_device_state_changed(
|
||||||
self, did: str, state: MIoTDeviceState, ctx: any
|
self, did: str, state: MIoTDeviceState, ctx: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
self._online = state
|
self._online = state
|
||||||
for key, handler in self._device_state_sub_list.items():
|
for key, handler in self._device_state_sub_list.items():
|
||||||
@ -704,11 +704,11 @@ class MIoTServiceEntity(Entity):
|
|||||||
entity_data: MIoTEntityData
|
entity_data: MIoTEntityData
|
||||||
|
|
||||||
_main_loop: asyncio.AbstractEventLoop
|
_main_loop: asyncio.AbstractEventLoop
|
||||||
_prop_value_map: dict[MIoTSpecProperty, any]
|
_prop_value_map: dict[MIoTSpecProperty, Any]
|
||||||
|
|
||||||
_event_occurred_handler: Callable[[MIoTSpecEvent, dict], None]
|
_event_occurred_handler: Callable[[MIoTSpecEvent, dict], None]
|
||||||
_prop_changed_subs: dict[
|
_prop_changed_subs: dict[
|
||||||
MIoTSpecProperty, Callable[[MIoTSpecProperty, any], None]]
|
MIoTSpecProperty, Callable[[MIoTSpecProperty, Any], None]]
|
||||||
|
|
||||||
_pending_write_ha_state_timer: Optional[asyncio.TimerHandle]
|
_pending_write_ha_state_timer: Optional[asyncio.TimerHandle]
|
||||||
|
|
||||||
@ -759,7 +759,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
|
|
||||||
def sub_prop_changed(
|
def sub_prop_changed(
|
||||||
self, prop: MIoTSpecProperty,
|
self, prop: MIoTSpecProperty,
|
||||||
handler: Callable[[MIoTSpecProperty, any], None]
|
handler: Callable[[MIoTSpecProperty, Any], None]
|
||||||
) -> None:
|
) -> None:
|
||||||
if not prop or not handler:
|
if not prop or not handler:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
@ -816,13 +816,13 @@ class MIoTServiceEntity(Entity):
|
|||||||
self.miot_device.unsub_event(
|
self.miot_device.unsub_event(
|
||||||
siid=event.service.iid, eiid=event.iid)
|
siid=event.service.iid, eiid=event.iid)
|
||||||
|
|
||||||
def get_map_description(self, map_: dict[int, any], key: int) -> any:
|
def get_map_description(self, map_: dict[int, Any], key: int) -> Any:
|
||||||
if map_ is None:
|
if map_ is None:
|
||||||
return None
|
return None
|
||||||
return map_.get(key, None)
|
return map_.get(key, None)
|
||||||
|
|
||||||
def get_map_value(
|
def get_map_value(
|
||||||
self, map_: dict[int, any], description: any
|
self, map_: dict[int, Any], description: Any
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
if map_ is None:
|
if map_ is None:
|
||||||
return None
|
return None
|
||||||
@ -831,7 +831,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
return key
|
return key
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_prop_value(self, prop: MIoTSpecProperty) -> any:
|
def get_prop_value(self, prop: MIoTSpecProperty) -> Any:
|
||||||
if not prop:
|
if not prop:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'get_prop_value error, property is None, %s, %s',
|
'get_prop_value error, property is None, %s, %s',
|
||||||
@ -839,7 +839,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
return None
|
return None
|
||||||
return self._prop_value_map.get(prop, None)
|
return self._prop_value_map.get(prop, None)
|
||||||
|
|
||||||
def set_prop_value(self, prop: MIoTSpecProperty, value: any) -> None:
|
def set_prop_value(self, prop: MIoTSpecProperty, value: Any) -> None:
|
||||||
if not prop:
|
if not prop:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'set_prop_value error, property is None, %s, %s',
|
'set_prop_value error, property is None, %s, %s',
|
||||||
@ -848,7 +848,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
self._prop_value_map[prop] = value
|
self._prop_value_map[prop] = value
|
||||||
|
|
||||||
async def set_property_async(
|
async def set_property_async(
|
||||||
self, prop: MIoTSpecProperty, value: any, update: bool = True
|
self, prop: MIoTSpecProperty, value: Any, update: bool = True
|
||||||
) -> bool:
|
) -> bool:
|
||||||
value = prop.value_format(value)
|
value = prop.value_format(value)
|
||||||
if not prop:
|
if not prop:
|
||||||
@ -875,7 +875,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def get_property_async(self, prop: MIoTSpecProperty) -> any:
|
async def get_property_async(self, prop: MIoTSpecProperty) -> Any:
|
||||||
if not prop:
|
if not prop:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'get property failed, property is None, %s, %s',
|
'get property failed, property is None, %s, %s',
|
||||||
@ -914,7 +914,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
f'{e}, {self.entity_id}, {self.name}, {action.name}') from e
|
f'{e}, {self.entity_id}, {self.name}, {action.name}') from e
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __on_properties_changed(self, params: dict, ctx: any) -> None:
|
def __on_properties_changed(self, params: dict, ctx: Any) -> None:
|
||||||
_LOGGER.debug('properties changed, %s', params)
|
_LOGGER.debug('properties changed, %s', params)
|
||||||
for prop in self.entity_data.props:
|
for prop in self.entity_data.props:
|
||||||
if (
|
if (
|
||||||
@ -922,7 +922,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
or prop.service.iid != params['siid']
|
or prop.service.iid != params['siid']
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
value: any = prop.value_format(params['value'])
|
value: Any = prop.value_format(params['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)
|
||||||
@ -930,7 +930,7 @@ class MIoTServiceEntity(Entity):
|
|||||||
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()
|
||||||
|
|
||||||
def __on_event_occurred(self, params: dict, ctx: any) -> None:
|
def __on_event_occurred(self, params: dict, ctx: Any) -> None:
|
||||||
_LOGGER.debug('event occurred, %s', params)
|
_LOGGER.debug('event occurred, %s', params)
|
||||||
if self._event_occurred_handler is None:
|
if self._event_occurred_handler is None:
|
||||||
return
|
return
|
||||||
@ -988,9 +988,9 @@ class MIoTPropertyEntity(Entity):
|
|||||||
_main_loop: asyncio.AbstractEventLoop
|
_main_loop: asyncio.AbstractEventLoop
|
||||||
# {'min':int, 'max':int, 'step': int}
|
# {'min':int, 'max':int, 'step': int}
|
||||||
_value_range: dict[str, int]
|
_value_range: dict[str, int]
|
||||||
# {any: any}
|
# {Any: Any}
|
||||||
_value_list: dict[any, any]
|
_value_list: dict[Any, Any]
|
||||||
_value: any
|
_value: Any
|
||||||
|
|
||||||
_pending_write_ha_state_timer: Optional[asyncio.TimerHandle]
|
_pending_write_ha_state_timer: Optional[asyncio.TimerHandle]
|
||||||
|
|
||||||
@ -1054,12 +1054,12 @@ class MIoTPropertyEntity(Entity):
|
|||||||
self.miot_device.unsub_property(
|
self.miot_device.unsub_property(
|
||||||
siid=self.service.iid, piid=self.spec.iid)
|
siid=self.service.iid, piid=self.spec.iid)
|
||||||
|
|
||||||
def get_vlist_description(self, value: any) -> str:
|
def get_vlist_description(self, value: Any) -> str:
|
||||||
if not self._value_list:
|
if not self._value_list:
|
||||||
return None
|
return None
|
||||||
return self._value_list.get(value, None)
|
return self._value_list.get(value, None)
|
||||||
|
|
||||||
def get_vlist_value(self, description: str) -> any:
|
def get_vlist_value(self, description: str) -> Any:
|
||||||
if not self._value_list:
|
if not self._value_list:
|
||||||
return None
|
return None
|
||||||
for key, value in self._value_list.items():
|
for key, value in self._value_list.items():
|
||||||
@ -1067,7 +1067,7 @@ class MIoTPropertyEntity(Entity):
|
|||||||
return key
|
return key
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def set_property_async(self, value: any) -> bool:
|
async def set_property_async(self, value: Any) -> bool:
|
||||||
if not self.spec.writable:
|
if not self.spec.writable:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f'set property failed, not writable, '
|
f'set property failed, not writable, '
|
||||||
@ -1084,7 +1084,7 @@ class MIoTPropertyEntity(Entity):
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def get_property_async(self) -> any:
|
async def get_property_async(self) -> Any:
|
||||||
if not self.spec.readable:
|
if not self.spec.readable:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'get property failed, not readable, %s, %s',
|
'get property failed, not readable, %s, %s',
|
||||||
@ -1095,7 +1095,7 @@ class MIoTPropertyEntity(Entity):
|
|||||||
did=self.miot_device.did, siid=self.spec.service.iid,
|
did=self.miot_device.did, siid=self.spec.service.iid,
|
||||||
piid=self.spec.iid))
|
piid=self.spec.iid))
|
||||||
|
|
||||||
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'])
|
self._value = self.spec.value_format(params['value'])
|
||||||
if not self._pending_write_ha_state_timer:
|
if not self._pending_write_ha_state_timer:
|
||||||
@ -1135,7 +1135,7 @@ class MIoTEventEntity(Entity):
|
|||||||
service: MIoTSpecService
|
service: MIoTSpecService
|
||||||
|
|
||||||
_main_loop: asyncio.AbstractEventLoop
|
_main_loop: asyncio.AbstractEventLoop
|
||||||
_value: any
|
_value: Any
|
||||||
_attr_event_types: list[str]
|
_attr_event_types: list[str]
|
||||||
_arguments_map: dict[int, str]
|
_arguments_map: dict[int, str]
|
||||||
|
|
||||||
@ -1192,10 +1192,10 @@ class MIoTEventEntity(Entity):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_event_occurred(
|
def on_event_occurred(
|
||||||
self, name: str, arguments: list[dict[int, any]]
|
self, name: str, arguments: list[dict[int, Any]]
|
||||||
): ...
|
): ...
|
||||||
|
|
||||||
def __on_event_occurred(self, params: dict, ctx: any) -> None:
|
def __on_event_occurred(self, params: dict, ctx: Any) -> None:
|
||||||
_LOGGER.debug('event occurred, %s', params)
|
_LOGGER.debug('event occurred, %s', params)
|
||||||
trans_arg = {}
|
trans_arg = {}
|
||||||
try:
|
try:
|
||||||
|
@ -46,6 +46,7 @@ off Xiaomi or its affiliates' products.
|
|||||||
MIoT error code and exception.
|
MIoT error code and exception.
|
||||||
"""
|
"""
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class MIoTErrorCode(Enum):
|
class MIoTErrorCode(Enum):
|
||||||
@ -78,10 +79,10 @@ class MIoTErrorCode(Enum):
|
|||||||
class MIoTError(Exception):
|
class MIoTError(Exception):
|
||||||
"""MIoT error."""
|
"""MIoT error."""
|
||||||
code: MIoTErrorCode
|
code: MIoTErrorCode
|
||||||
message: any
|
message: Any
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, message: any, code: MIoTErrorCode = MIoTErrorCode.CODE_UNKNOWN
|
self, message: Any, code: MIoTErrorCode = MIoTErrorCode.CODE_UNKNOWN
|
||||||
) -> None:
|
) -> None:
|
||||||
self.message = message
|
self.message = message
|
||||||
self.code = code
|
self.code = code
|
||||||
|
@ -49,7 +49,7 @@ import selectors
|
|||||||
import heapq
|
import heapq
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Callable, TypeVar
|
from typing import Any, Callable, TypeVar
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
@ -64,17 +64,17 @@ TimeoutHandle = TypeVar('TimeoutHandle')
|
|||||||
class MIoTFdHandler:
|
class MIoTFdHandler:
|
||||||
"""File descriptor handler."""
|
"""File descriptor handler."""
|
||||||
fd: int
|
fd: int
|
||||||
read_handler: Callable[[any], None]
|
read_handler: Callable[[Any], None]
|
||||||
read_handler_ctx: any
|
read_handler_ctx: Any
|
||||||
write_handler: Callable[[any], None]
|
write_handler: Callable[[Any], None]
|
||||||
write_handler_ctx: any
|
write_handler_ctx: Any
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, fd: int,
|
self, fd: int,
|
||||||
read_handler: Callable[[any], None] = None,
|
read_handler: Callable[[Any], None] = None,
|
||||||
read_handler_ctx: any = None,
|
read_handler_ctx: Any = None,
|
||||||
write_handler: Callable[[any], None] = None,
|
write_handler: Callable[[Any], None] = None,
|
||||||
write_handler_ctx: any = None
|
write_handler_ctx: Any = None
|
||||||
) -> None:
|
) -> None:
|
||||||
self.fd = fd
|
self.fd = fd
|
||||||
self.read_handler = read_handler
|
self.read_handler = read_handler
|
||||||
@ -87,13 +87,13 @@ class MIoTTimeout:
|
|||||||
"""Timeout handler."""
|
"""Timeout handler."""
|
||||||
key: TimeoutHandle
|
key: TimeoutHandle
|
||||||
target: int
|
target: int
|
||||||
handler: Callable[[any], None]
|
handler: Callable[[Any], None]
|
||||||
handler_ctx: any
|
handler_ctx: Any
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, key: str = None, target: int = None,
|
self, key: str = None, target: int = None,
|
||||||
handler: Callable[[any], None] = None,
|
handler: Callable[[Any], None] = None,
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
) -> None:
|
) -> None:
|
||||||
self.key = key
|
self.key = key
|
||||||
self.target = target
|
self.target = target
|
||||||
@ -185,8 +185,8 @@ class MIoTEventLoop:
|
|||||||
self._timer_handlers = {}
|
self._timer_handlers = {}
|
||||||
|
|
||||||
def set_timeout(
|
def set_timeout(
|
||||||
self, timeout_ms: int, handler: Callable[[any], None],
|
self, timeout_ms: int, handler: Callable[[Any], None],
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
) -> TimeoutHandle:
|
) -> TimeoutHandle:
|
||||||
"""Set a timer."""
|
"""Set a timer."""
|
||||||
if timeout_ms is None or handler is None:
|
if timeout_ms is None or handler is None:
|
||||||
@ -211,7 +211,7 @@ class MIoTEventLoop:
|
|||||||
heapq.heapify(self._timer_heap)
|
heapq.heapify(self._timer_heap)
|
||||||
|
|
||||||
def set_read_handler(
|
def set_read_handler(
|
||||||
self, fd: int, handler: Callable[[any], None], handler_ctx: any = None
|
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Set a read handler for a file descriptor.
|
"""Set a read handler for a file descriptor.
|
||||||
|
|
||||||
@ -222,7 +222,7 @@ class MIoTEventLoop:
|
|||||||
fd, is_read=True, handler=handler, handler_ctx=handler_ctx)
|
fd, is_read=True, handler=handler, handler_ctx=handler_ctx)
|
||||||
|
|
||||||
def set_write_handler(
|
def set_write_handler(
|
||||||
self, fd: int, handler: Callable[[any], None], handler_ctx: any = None
|
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Set a write handler for a file descriptor.
|
"""Set a write handler for a file descriptor.
|
||||||
|
|
||||||
@ -233,8 +233,8 @@ class MIoTEventLoop:
|
|||||||
fd, is_read=False, handler=handler, handler_ctx=handler_ctx)
|
fd, is_read=False, handler=handler, handler_ctx=handler_ctx)
|
||||||
|
|
||||||
def __set_handler(
|
def __set_handler(
|
||||||
self, fd, is_read: bool, handler: Callable[[any], None],
|
self, fd, is_read: bool, handler: Callable[[Any], None],
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Set a handler."""
|
"""Set a handler."""
|
||||||
if fd is None:
|
if fd is None:
|
||||||
|
@ -58,7 +58,7 @@ import threading
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import Callable, Optional, final
|
from typing import Any, Callable, Optional, final
|
||||||
|
|
||||||
from paho.mqtt.client import (
|
from paho.mqtt.client import (
|
||||||
MQTT_ERR_SUCCESS,
|
MQTT_ERR_SUCCESS,
|
||||||
@ -173,9 +173,9 @@ class MipsCmdType(Enum):
|
|||||||
class MipsCmd:
|
class MipsCmd:
|
||||||
"""MIoT Pub/Sub command."""
|
"""MIoT Pub/Sub command."""
|
||||||
type_: MipsCmdType
|
type_: MipsCmdType
|
||||||
data: any
|
data: Any
|
||||||
|
|
||||||
def __init__(self, type_: MipsCmdType, data: any) -> None:
|
def __init__(self, type_: MipsCmdType, data: Any) -> None:
|
||||||
self.type_ = type_
|
self.type_ = type_
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
@ -184,8 +184,8 @@ class MipsCmd:
|
|||||||
class MipsRequest:
|
class MipsRequest:
|
||||||
"""MIoT Pub/Sub request."""
|
"""MIoT Pub/Sub request."""
|
||||||
mid: int = None
|
mid: int = None
|
||||||
on_reply: Callable[[str, any], None] = None
|
on_reply: Callable[[str, Any], None] = None
|
||||||
on_reply_ctx: any = None
|
on_reply_ctx: Any = None
|
||||||
timer: TimeoutHandle = None
|
timer: TimeoutHandle = None
|
||||||
|
|
||||||
|
|
||||||
@ -194,8 +194,8 @@ class MipsRequestData:
|
|||||||
"""MIoT Pub/Sub request data."""
|
"""MIoT Pub/Sub request data."""
|
||||||
topic: str = None
|
topic: str = None
|
||||||
payload: str = None
|
payload: str = None
|
||||||
on_reply: Callable[[str, any], None] = None
|
on_reply: Callable[[str, Any], None] = None
|
||||||
on_reply_ctx: any = None
|
on_reply_ctx: Any = None
|
||||||
timeout_ms: int = None
|
timeout_ms: int = None
|
||||||
|
|
||||||
|
|
||||||
@ -223,8 +223,8 @@ class MipsApi:
|
|||||||
param2: payload
|
param2: payload
|
||||||
param3: handler_ctx
|
param3: handler_ctx
|
||||||
"""
|
"""
|
||||||
handler: Callable[[MipsIncomingApiCall, str, any], None] = None
|
handler: Callable[[MipsIncomingApiCall, str, Any], None] = None
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
|
|
||||||
|
|
||||||
class MipsRegApi(MipsApi):
|
class MipsRegApi(MipsApi):
|
||||||
@ -247,8 +247,8 @@ class MipsBroadcast:
|
|||||||
param 2: msg payload
|
param 2: msg payload
|
||||||
param 3: handle_ctx
|
param 3: handle_ctx
|
||||||
"""
|
"""
|
||||||
handler: Callable[[str, str, any], None] = None
|
handler: Callable[[str, str, Any], None] = None
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'{self.topic}, {id(self.handler)}, {id(self.handler_ctx)}'
|
return f'{self.topic}, {id(self.handler)}, {id(self.handler_ctx)}'
|
||||||
@ -265,7 +265,6 @@ class MipsState:
|
|||||||
"""
|
"""
|
||||||
str: key
|
str: key
|
||||||
bool: mips connect state
|
bool: mips connect state
|
||||||
any: ctx
|
|
||||||
"""
|
"""
|
||||||
handler: Callable[[str, bool], asyncio.Future] = None
|
handler: Callable[[str, bool], asyncio.Future] = None
|
||||||
|
|
||||||
@ -288,10 +287,10 @@ class MipsDeviceState:
|
|||||||
"""handler
|
"""handler
|
||||||
str: did
|
str: did
|
||||||
MIoTDeviceState: online/offline/disable
|
MIoTDeviceState: online/offline/disable
|
||||||
any: ctx
|
Any: ctx
|
||||||
"""
|
"""
|
||||||
handler: Callable[[str, MIoTDeviceState, any], None] = None
|
handler: Callable[[str, MIoTDeviceState, Any], None] = None
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
|
|
||||||
|
|
||||||
class MipsRegDeviceState(MipsDeviceState):
|
class MipsRegDeviceState(MipsDeviceState):
|
||||||
@ -512,8 +511,8 @@ class MipsClient(ABC):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def mev_set_timeout(
|
def mev_set_timeout(
|
||||||
self, timeout_ms: int, handler: Callable[[any], None],
|
self, timeout_ms: int, handler: Callable[[Any], None],
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
) -> Optional[TimeoutHandle]:
|
) -> Optional[TimeoutHandle]:
|
||||||
"""set timeout.
|
"""set timeout.
|
||||||
NOTICE: Internal function, only mips threads are allowed to call
|
NOTICE: Internal function, only mips threads are allowed to call
|
||||||
@ -534,7 +533,7 @@ class MipsClient(ABC):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def mev_set_read_handler(
|
def mev_set_read_handler(
|
||||||
self, fd: int, handler: Callable[[any], None], handler_ctx: any
|
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""set read handler.
|
"""set read handler.
|
||||||
NOTICE: Internal function, only mips threads are allowed to call
|
NOTICE: Internal function, only mips threads are allowed to call
|
||||||
@ -546,7 +545,7 @@ class MipsClient(ABC):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def mev_set_write_handler(
|
def mev_set_write_handler(
|
||||||
self, fd: int, handler: Callable[[any], None], handler_ctx: any
|
self, fd: int, handler: Callable[[Any], None], handler_ctx: Any
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""set write handler.
|
"""set write handler.
|
||||||
NOTICE: Internal function, only mips threads are allowed to call
|
NOTICE: Internal function, only mips threads are allowed to call
|
||||||
@ -604,8 +603,8 @@ class MipsClient(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def sub_prop(
|
def sub_prop(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, piid: int = None, handler_ctx: any = None
|
siid: int = None, piid: int = None, handler_ctx: Any = None
|
||||||
) -> bool: ...
|
) -> bool: ...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@ -615,8 +614,8 @@ class MipsClient(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def sub_event(
|
def sub_event(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, eiid: int = None, handler_ctx: any = None
|
siid: int = None, eiid: int = None, handler_ctx: Any = None
|
||||||
) -> bool: ...
|
) -> bool: ...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@ -632,11 +631,11 @@ class MipsClient(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def get_prop_async(
|
async def get_prop_async(
|
||||||
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
||||||
) -> any: ...
|
) -> Any: ...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def set_prop_async(
|
async def set_prop_async(
|
||||||
self, did: str, siid: int, piid: int, value: any,
|
self, did: str, siid: int, piid: int, value: Any,
|
||||||
timeout_ms: int = 10000
|
timeout_ms: int = 10000
|
||||||
) -> bool: ...
|
) -> bool: ...
|
||||||
|
|
||||||
@ -709,7 +708,7 @@ class MipsClient(ABC):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@final
|
@final
|
||||||
def _mips_send_cmd(self, type_: MipsCmdType, data: any) -> bool:
|
def _mips_send_cmd(self, type_: MipsCmdType, data: Any) -> bool:
|
||||||
if self._mips_queue is None or self._cmd_event_fd is None:
|
if self._mips_queue is None or self._cmd_event_fd is None:
|
||||||
raise MIoTMipsError('send mips cmd disable')
|
raise MIoTMipsError('send mips cmd disable')
|
||||||
# Put data to queue
|
# Put data to queue
|
||||||
@ -723,7 +722,7 @@ class MipsClient(ABC):
|
|||||||
if threading.current_thread() is not self._mips_thread:
|
if threading.current_thread() is not self._mips_thread:
|
||||||
raise MIoTMipsError('illegal call')
|
raise MIoTMipsError('illegal call')
|
||||||
|
|
||||||
def __mips_cmd_read_handler(self, ctx: any) -> None:
|
def __mips_cmd_read_handler(self, ctx: Any) -> None:
|
||||||
fd_value = os.eventfd_read(self._cmd_event_fd)
|
fd_value = os.eventfd_read(self._cmd_event_fd)
|
||||||
if fd_value == 0:
|
if fd_value == 0:
|
||||||
return
|
return
|
||||||
@ -763,20 +762,20 @@ class MipsClient(ABC):
|
|||||||
if self._on_mips_cmd:
|
if self._on_mips_cmd:
|
||||||
self._on_mips_cmd(mips_cmd=mips_cmd)
|
self._on_mips_cmd(mips_cmd=mips_cmd)
|
||||||
|
|
||||||
def __mqtt_read_handler(self, ctx: any) -> None:
|
def __mqtt_read_handler(self, ctx: Any) -> None:
|
||||||
self.__mqtt_loop_handler(ctx=ctx)
|
self.__mqtt_loop_handler(ctx=ctx)
|
||||||
|
|
||||||
def __mqtt_write_handler(self, ctx: any) -> None:
|
def __mqtt_write_handler(self, ctx: Any) -> None:
|
||||||
self.mev_set_write_handler(self._mqtt_fd, None, None)
|
self.mev_set_write_handler(self._mqtt_fd, None, None)
|
||||||
self.__mqtt_loop_handler(ctx=ctx)
|
self.__mqtt_loop_handler(ctx=ctx)
|
||||||
|
|
||||||
def __mqtt_timer_handler(self, ctx: any) -> None:
|
def __mqtt_timer_handler(self, ctx: Any) -> None:
|
||||||
self.__mqtt_loop_handler(ctx=ctx)
|
self.__mqtt_loop_handler(ctx=ctx)
|
||||||
if self._mqtt:
|
if self._mqtt:
|
||||||
self._mqtt_timer = self.mev_set_timeout(
|
self._mqtt_timer = self.mev_set_timeout(
|
||||||
self.MQTT_INTERVAL_MS, self.__mqtt_timer_handler, None)
|
self.MQTT_INTERVAL_MS, self.__mqtt_timer_handler, None)
|
||||||
|
|
||||||
def __mqtt_loop_handler(self, ctx: any) -> None:
|
def __mqtt_loop_handler(self, ctx: Any) -> None:
|
||||||
try:
|
try:
|
||||||
if self._mqtt:
|
if self._mqtt:
|
||||||
self._mqtt.loop_read()
|
self._mqtt.loop_read()
|
||||||
@ -896,7 +895,7 @@ class MipsClient(ABC):
|
|||||||
self._mips_reconnect_timer = self.mev_set_timeout(
|
self._mips_reconnect_timer = self.mev_set_timeout(
|
||||||
interval, self.__mips_connect, None)
|
interval, self.__mips_connect, None)
|
||||||
|
|
||||||
def __mips_sub_internal_pending_handler(self, ctx: any) -> None:
|
def __mips_sub_internal_pending_handler(self, ctx: Any) -> None:
|
||||||
subbed_count = 1
|
subbed_count = 1
|
||||||
for topic in list(self._mips_sub_pending_map.keys()):
|
for topic in list(self._mips_sub_pending_map.keys()):
|
||||||
if subbed_count > self.MIPS_SUB_PATCH:
|
if subbed_count > self.MIPS_SUB_PATCH:
|
||||||
@ -923,7 +922,7 @@ class MipsClient(ABC):
|
|||||||
else:
|
else:
|
||||||
self._mips_sub_pending_timer = None
|
self._mips_sub_pending_timer = None
|
||||||
|
|
||||||
def __mips_connect(self, ctx: any = None) -> None:
|
def __mips_connect(self, ctx: Any = None) -> None:
|
||||||
result = MQTT_ERR_UNKNOWN
|
result = MQTT_ERR_UNKNOWN
|
||||||
if self._mips_reconnect_timer:
|
if self._mips_reconnect_timer:
|
||||||
self.mev_clear_timeout(self._mips_reconnect_timer)
|
self.mev_clear_timeout(self._mips_reconnect_timer)
|
||||||
@ -1034,8 +1033,8 @@ class MipsCloudClient(MipsClient):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def sub_prop(
|
def sub_prop(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, piid: int = None, handler_ctx: any = None
|
siid: int = None, piid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if not isinstance(did, str) or handler is None:
|
if not isinstance(did, str) or handler is None:
|
||||||
raise MIoTMipsError('invalid params')
|
raise MIoTMipsError('invalid params')
|
||||||
@ -1044,7 +1043,7 @@ class MipsCloudClient(MipsClient):
|
|||||||
f'device/{did}/up/properties_changed/'
|
f'device/{did}/up/properties_changed/'
|
||||||
f'{"#" if siid is None or piid is None else f"{siid}/{piid}"}')
|
f'{"#" if siid is None or piid is None else f"{siid}/{piid}"}')
|
||||||
|
|
||||||
def on_prop_msg(topic: str, payload: str, ctx: any) -> bool:
|
def on_prop_msg(topic: str, payload: str, ctx: Any) -> bool:
|
||||||
try:
|
try:
|
||||||
msg: dict = json.loads(payload)
|
msg: dict = json.loads(payload)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
@ -1077,8 +1076,8 @@ class MipsCloudClient(MipsClient):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def sub_event(
|
def sub_event(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, eiid: int = None, handler_ctx: any = None
|
siid: int = None, eiid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if not isinstance(did, str) or handler is None:
|
if not isinstance(did, str) or handler is None:
|
||||||
raise MIoTMipsError('invalid params')
|
raise MIoTMipsError('invalid params')
|
||||||
@ -1087,7 +1086,7 @@ class MipsCloudClient(MipsClient):
|
|||||||
f'device/{did}/up/event_occured/'
|
f'device/{did}/up/event_occured/'
|
||||||
f'{"#" if siid is None or eiid is None else f"{siid}/{eiid}"}')
|
f'{"#" if siid is None or eiid is None else f"{siid}/{eiid}"}')
|
||||||
|
|
||||||
def on_event_msg(topic: str, payload: str, ctx: any) -> bool:
|
def on_event_msg(topic: str, payload: str, ctx: Any) -> bool:
|
||||||
try:
|
try:
|
||||||
msg: dict = json.loads(payload)
|
msg: dict = json.loads(payload)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
@ -1122,15 +1121,15 @@ class MipsCloudClient(MipsClient):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def sub_device_state(
|
def sub_device_state(
|
||||||
self, did: str, handler: Callable[[str, MIoTDeviceState, any], None],
|
self, did: str, handler: Callable[[str, MIoTDeviceState, Any], None],
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""subscribe online state."""
|
"""subscribe online state."""
|
||||||
if not isinstance(did, str) or handler is None:
|
if not isinstance(did, str) or handler is None:
|
||||||
raise MIoTMipsError('invalid params')
|
raise MIoTMipsError('invalid params')
|
||||||
topic: str = f'device/{did}/state/#'
|
topic: str = f'device/{did}/state/#'
|
||||||
|
|
||||||
def on_state_msg(topic: str, payload: str, ctx: any) -> None:
|
def on_state_msg(topic: str, payload: str, ctx: Any) -> None:
|
||||||
msg: dict = json.loads(payload)
|
msg: dict = json.loads(payload)
|
||||||
# {"device_id":"xxxx","device_name":"米家智能插座3 ","event":"online",
|
# {"device_id":"xxxx","device_name":"米家智能插座3 ","event":"online",
|
||||||
# "model": "cuco.plug.v3","timestamp":1709001070828,"uid":xxxx}
|
# "model": "cuco.plug.v3","timestamp":1709001070828,"uid":xxxx}
|
||||||
@ -1163,11 +1162,11 @@ class MipsCloudClient(MipsClient):
|
|||||||
|
|
||||||
async def get_prop_async(
|
async def get_prop_async(
|
||||||
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
||||||
) -> any:
|
) -> Any:
|
||||||
raise NotImplementedError('please call in http client')
|
raise NotImplementedError('please call in http client')
|
||||||
|
|
||||||
async def set_prop_async(
|
async def set_prop_async(
|
||||||
self, did: str, siid: int, piid: int, value: any,
|
self, did: str, siid: int, piid: int, value: Any,
|
||||||
timeout_ms: int = 10000
|
timeout_ms: int = 10000
|
||||||
) -> bool:
|
) -> bool:
|
||||||
raise NotImplementedError('please call in http client')
|
raise NotImplementedError('please call in http client')
|
||||||
@ -1199,8 +1198,8 @@ class MipsCloudClient(MipsClient):
|
|||||||
self._mips_unsub_internal(topic=unreg_bc.topic)
|
self._mips_unsub_internal(topic=unreg_bc.topic)
|
||||||
|
|
||||||
def __reg_broadcast(
|
def __reg_broadcast(
|
||||||
self, topic: str, handler: Callable[[str, str, any], None],
|
self, topic: str, handler: Callable[[str, str, Any], None],
|
||||||
handler_ctx: any = None
|
handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
return self._mips_send_cmd(
|
return self._mips_send_cmd(
|
||||||
type_=MipsCmdType.REG_BROADCAST,
|
type_=MipsCmdType.REG_BROADCAST,
|
||||||
@ -1259,7 +1258,7 @@ class MipsLocalClient(MipsClient):
|
|||||||
_device_state_sub_map: dict[str, MipsDeviceState]
|
_device_state_sub_map: dict[str, MipsDeviceState]
|
||||||
_get_prop_queue: dict[str, list]
|
_get_prop_queue: dict[str, list]
|
||||||
_get_prop_timer: asyncio.TimerHandle
|
_get_prop_timer: asyncio.TimerHandle
|
||||||
_on_dev_list_changed: Callable[[any, list[str]], asyncio.Future]
|
_on_dev_list_changed: Callable[[Any, list[str]], asyncio.Future]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, did: str, host: str, group_id: str,
|
self, did: str, host: str, group_id: str,
|
||||||
@ -1347,14 +1346,14 @@ class MipsLocalClient(MipsClient):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def sub_prop(
|
def sub_prop(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, piid: int = None, handler_ctx: any = None
|
siid: int = None, piid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
topic: str = (
|
topic: str = (
|
||||||
f'appMsg/notify/iot/{did}/property/'
|
f'appMsg/notify/iot/{did}/property/'
|
||||||
f'{"#" if siid is None or piid is None else f"{siid}.{piid}"}')
|
f'{"#" if siid is None or piid is None else f"{siid}.{piid}"}')
|
||||||
|
|
||||||
def on_prop_msg(topic: str, payload: str, ctx: any):
|
def on_prop_msg(topic: str, payload: str, ctx: Any):
|
||||||
msg: dict = json.loads(payload)
|
msg: dict = json.loads(payload)
|
||||||
if (
|
if (
|
||||||
msg is None
|
msg is None
|
||||||
@ -1380,14 +1379,14 @@ class MipsLocalClient(MipsClient):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
def sub_event(
|
def sub_event(
|
||||||
self, did: str, handler: Callable[[dict, any], None],
|
self, did: str, handler: Callable[[dict, Any], None],
|
||||||
siid: int = None, eiid: int = None, handler_ctx: any = None
|
siid: int = None, eiid: int = None, handler_ctx: Any = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
topic: str = (
|
topic: str = (
|
||||||
f'appMsg/notify/iot/{did}/event/'
|
f'appMsg/notify/iot/{did}/event/'
|
||||||
f'{"#" if siid is None or eiid is None else f"{siid}.{eiid}"}')
|
f'{"#" if siid is None or eiid is None else f"{siid}.{eiid}"}')
|
||||||
|
|
||||||
def on_event_msg(topic: str, payload: str, ctx: any):
|
def on_event_msg(topic: str, payload: str, ctx: Any):
|
||||||
msg: dict = json.loads(payload)
|
msg: dict = json.loads(payload)
|
||||||
if (
|
if (
|
||||||
msg is None
|
msg is None
|
||||||
@ -1414,7 +1413,7 @@ class MipsLocalClient(MipsClient):
|
|||||||
@final
|
@final
|
||||||
async def get_prop_safe_async(
|
async def get_prop_safe_async(
|
||||||
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
||||||
) -> any:
|
) -> Any:
|
||||||
self._get_prop_queue.setdefault(did, [])
|
self._get_prop_queue.setdefault(did, [])
|
||||||
fut: asyncio.Future = self.main_loop.create_future()
|
fut: asyncio.Future = self.main_loop.create_future()
|
||||||
self._get_prop_queue[did].append({
|
self._get_prop_queue[did].append({
|
||||||
@ -1434,7 +1433,7 @@ class MipsLocalClient(MipsClient):
|
|||||||
@final
|
@final
|
||||||
async def get_prop_async(
|
async def get_prop_async(
|
||||||
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
self, did: str, siid: int, piid: int, timeout_ms: int = 10000
|
||||||
) -> any:
|
) -> Any:
|
||||||
result_obj = await self.__request_async(
|
result_obj = await self.__request_async(
|
||||||
topic='proxy/get',
|
topic='proxy/get',
|
||||||
payload=json.dumps({
|
payload=json.dumps({
|
||||||
@ -1449,7 +1448,7 @@ class MipsLocalClient(MipsClient):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
async def set_prop_async(
|
async def set_prop_async(
|
||||||
self, did: str, siid: int, piid: int, value: any,
|
self, did: str, siid: int, piid: int, value: Any,
|
||||||
timeout_ms: int = 10000
|
timeout_ms: int = 10000
|
||||||
) -> dict:
|
) -> dict:
|
||||||
payload_obj: dict = {
|
payload_obj: dict = {
|
||||||
@ -1580,13 +1579,13 @@ class MipsLocalClient(MipsClient):
|
|||||||
|
|
||||||
@final
|
@final
|
||||||
@property
|
@property
|
||||||
def on_dev_list_changed(self) -> Callable[[any, list[str]], asyncio.Future]:
|
def on_dev_list_changed(self) -> Callable[[Any, list[str]], asyncio.Future]:
|
||||||
return self._on_dev_list_changed
|
return self._on_dev_list_changed
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@on_dev_list_changed.setter
|
@on_dev_list_changed.setter
|
||||||
def on_dev_list_changed(
|
def on_dev_list_changed(
|
||||||
self, func: Callable[[any, list[str]], asyncio.Future]
|
self, func: Callable[[Any, list[str]], asyncio.Future]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""run in main loop."""
|
"""run in main loop."""
|
||||||
self._on_dev_list_changed = func
|
self._on_dev_list_changed = func
|
||||||
@ -1731,8 +1730,8 @@ class MipsLocalClient(MipsClient):
|
|||||||
|
|
||||||
def __request(
|
def __request(
|
||||||
self, topic: str, payload: str,
|
self, topic: str, payload: str,
|
||||||
on_reply: Callable[[str, any], None],
|
on_reply: Callable[[str, Any], None],
|
||||||
on_reply_ctx: any = None, timeout_ms: int = 10000
|
on_reply_ctx: Any = None, timeout_ms: int = 10000
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if topic is None or payload is None or on_reply is None:
|
if topic is None or payload is None or on_reply is None:
|
||||||
raise MIoTMipsError('invalid params')
|
raise MIoTMipsError('invalid params')
|
||||||
@ -1745,8 +1744,8 @@ class MipsLocalClient(MipsClient):
|
|||||||
return self._mips_send_cmd(type_=MipsCmdType.CALL_API, data=req_data)
|
return self._mips_send_cmd(type_=MipsCmdType.CALL_API, data=req_data)
|
||||||
|
|
||||||
def __reg_broadcast(
|
def __reg_broadcast(
|
||||||
self, topic: str, handler: Callable[[str, str, any], None],
|
self, topic: str, handler: Callable[[str, str, Any], None],
|
||||||
handler_ctx: any
|
handler_ctx: Any
|
||||||
) -> bool:
|
) -> bool:
|
||||||
return self._mips_send_cmd(
|
return self._mips_send_cmd(
|
||||||
type_=MipsCmdType.REG_BROADCAST,
|
type_=MipsCmdType.REG_BROADCAST,
|
||||||
@ -1764,7 +1763,7 @@ class MipsLocalClient(MipsClient):
|
|||||||
) -> dict:
|
) -> dict:
|
||||||
fut_handler: asyncio.Future = self.main_loop.create_future()
|
fut_handler: asyncio.Future = self.main_loop.create_future()
|
||||||
|
|
||||||
def on_msg_reply(payload: str, ctx: any):
|
def on_msg_reply(payload: str, ctx: Any):
|
||||||
fut: asyncio.Future = ctx
|
fut: asyncio.Future = ctx
|
||||||
if fut:
|
if fut:
|
||||||
self.main_loop.call_soon_threadsafe(fut.set_result, payload)
|
self.main_loop.call_soon_threadsafe(fut.set_result, payload)
|
||||||
|
@ -49,7 +49,7 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import platform
|
import platform
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
import logging
|
import logging
|
||||||
@ -78,9 +78,9 @@ class MIoTSpecBase:
|
|||||||
|
|
||||||
# External params
|
# External params
|
||||||
platform: str
|
platform: str
|
||||||
device_class: any
|
device_class: Any
|
||||||
icon: str
|
icon: str
|
||||||
external_unit: any
|
external_unit: Any
|
||||||
|
|
||||||
spec_id: str
|
spec_id: str
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ class MIoTSpecProperty(MIoTSpecBase):
|
|||||||
def notifiable(self):
|
def notifiable(self):
|
||||||
return self._notifiable
|
return self._notifiable
|
||||||
|
|
||||||
def value_format(self, value: any) -> any:
|
def value_format(self, value: Any) -> Any:
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
if self.format_ == 'int':
|
if self.format_ == 'int':
|
||||||
@ -296,7 +296,7 @@ class MIoTSpecInstance:
|
|||||||
|
|
||||||
# External params
|
# External params
|
||||||
platform: str
|
platform: str
|
||||||
device_class: any
|
device_class: Any
|
||||||
icon: str
|
icon: str
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -56,7 +56,7 @@ import hashlib
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Union
|
from typing import Any, Optional, Union
|
||||||
import logging
|
import logging
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
@ -419,7 +419,7 @@ class MIoTStorage:
|
|||||||
return await fut
|
return await fut
|
||||||
|
|
||||||
def update_user_config(
|
def update_user_config(
|
||||||
self, uid: str, cloud_server: str, config: Optional[dict[str, any]],
|
self, uid: str, cloud_server: str, config: Optional[dict[str, Any]],
|
||||||
replace: bool = False
|
replace: bool = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if config is not None and len(config) == 0:
|
if config is not None and len(config) == 0:
|
||||||
@ -443,7 +443,7 @@ class MIoTStorage:
|
|||||||
domain=config_domain, name=config_name, data=local_config)
|
domain=config_domain, name=config_name, data=local_config)
|
||||||
|
|
||||||
async def update_user_config_async(
|
async def update_user_config_async(
|
||||||
self, uid: str, cloud_server: str, config: Optional[dict[str, any]],
|
self, uid: str, cloud_server: str, config: Optional[dict[str, Any]],
|
||||||
replace: bool = False
|
replace: bool = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Update user configuration.
|
"""Update user configuration.
|
||||||
@ -480,7 +480,7 @@ class MIoTStorage:
|
|||||||
|
|
||||||
def load_user_config(
|
def load_user_config(
|
||||||
self, uid: str, cloud_server: str, keys: Optional[list[str]] = None
|
self, uid: str, cloud_server: str, keys: Optional[list[str]] = None
|
||||||
) -> dict[str, any]:
|
) -> dict[str, Any]:
|
||||||
if keys is not None and len(keys) == 0:
|
if keys is not None and len(keys) == 0:
|
||||||
# Do nothing
|
# Do nothing
|
||||||
return {}
|
return {}
|
||||||
@ -494,7 +494,7 @@ class MIoTStorage:
|
|||||||
|
|
||||||
async def load_user_config_async(
|
async def load_user_config_async(
|
||||||
self, uid: str, cloud_server: str, keys: Optional[list[str]] = None
|
self, uid: str, cloud_server: str, keys: Optional[list[str]] = None
|
||||||
) -> dict[str, any]:
|
) -> dict[str, Any]:
|
||||||
"""Load user configuration.
|
"""Load user configuration.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -503,7 +503,7 @@ class MIoTStorage:
|
|||||||
query key list, return all config item if keys is None
|
query key list, return all config item if keys is None
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict[str, any]: query result
|
dict[str, Any]: query result
|
||||||
"""
|
"""
|
||||||
if keys is not None and len(keys) == 0:
|
if keys is not None and len(keys) == 0:
|
||||||
# Do nothing
|
# Do nothing
|
||||||
|
@ -47,6 +47,7 @@ Sensor entities for Xiaomi Home.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -107,7 +108,7 @@ class Sensor(MIoTPropertyEntity, SensorEntity):
|
|||||||
self._attr_icon = spec.icon
|
self._attr_icon = spec.icon
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> any:
|
def native_value(self) -> Any:
|
||||||
"""Return the current value of the sensor."""
|
"""Return the current value of the sensor."""
|
||||||
if self._value_range and isinstance(self._value, (int, float)):
|
if self._value_range and isinstance(self._value, (int, float)):
|
||||||
if (
|
if (
|
||||||
|
@ -47,7 +47,7 @@ Water heater entities for Xiaomi Home.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -93,7 +93,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
_prop_target_temp: Optional[MIoTSpecProperty]
|
_prop_target_temp: Optional[MIoTSpecProperty]
|
||||||
_prop_mode: Optional[MIoTSpecProperty]
|
_prop_mode: Optional[MIoTSpecProperty]
|
||||||
|
|
||||||
_mode_list: Optional[dict[any, any]]
|
_mode_list: Optional[dict[Any, Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
self, miot_device: MIoTDevice, entity_data: MIoTEntityData
|
||||||
@ -164,7 +164,7 @@ class WaterHeater(MIoTServiceEntity, WaterHeaterEntity):
|
|||||||
"""Turn the water heater off."""
|
"""Turn the water heater off."""
|
||||||
await self.set_property_async(prop=self._prop_on, value=False)
|
await self.set_property_async(prop=self._prop_on, value=False)
|
||||||
|
|
||||||
async def async_set_temperature(self, **kwargs: any) -> None:
|
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||||
"""Set the temperature the water heater should heat water to."""
|
"""Set the temperature the water heater should heat water to."""
|
||||||
await self.set_property_async(
|
await self.set_property_async(
|
||||||
prop=self._prop_target_temp, value=kwargs[ATTR_TEMPERATURE])
|
prop=self._prop_target_temp, value=kwargs[ATTR_TEMPERATURE])
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Unit test for miot_lan.py."""
|
"""Unit test for miot_lan.py."""
|
||||||
|
from typing import Any
|
||||||
import pytest
|
import pytest
|
||||||
import asyncio
|
import asyncio
|
||||||
from zeroconf import IPVersion
|
from zeroconf import IPVersion
|
||||||
@ -79,7 +80,7 @@ async def test_lan_async(test_devices: dict):
|
|||||||
evt_push_unavailable = asyncio.Event()
|
evt_push_unavailable = asyncio.Event()
|
||||||
await miot_lan.vote_for_lan_ctrl_async(key='test', vote=True)
|
await miot_lan.vote_for_lan_ctrl_async(key='test', vote=True)
|
||||||
|
|
||||||
async def device_state_change(did: str, state: dict, ctx: any):
|
async def device_state_change(did: str, state: dict, ctx: Any):
|
||||||
print('device state change, ', did, state)
|
print('device state change, ', did, state)
|
||||||
if did != test_did:
|
if did != test_did:
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user