sync main

This commit is contained in:
GavinIves 2025-12-28 15:10:02 +00:00
parent b95a2c18ee
commit 4c7b6d570d
5 changed files with 75 additions and 80 deletions

View File

@ -1415,8 +1415,9 @@ class MIoTClient:
_LOGGER.error('on event msg error, %s, %s', params, err) _LOGGER.error('on event msg error, %s, %s', params, err)
@final @final
def __check_device_state(self, cloud_state: Optional[bool], gw_state: bool, def __check_device_state(
lan_state: bool) -> Optional[bool]: self, cloud_state: Optional[bool], gw_state: bool, lan_state: bool
) -> Optional[bool]:
if cloud_state is None and not gw_state and not lan_state: if cloud_state is None and not gw_state and not lan_state:
# Device remove # Device remove
return None return None
@ -1698,10 +1699,9 @@ class MIoTClient:
self.__request_show_devices_changed_notify() self.__request_show_devices_changed_notify()
@final @final
def __request_refresh_gw_devices_by_group_id(self, def __request_refresh_gw_devices_by_group_id(
group_id: str, self, group_id: str, immediately: bool = False
immediately: bool = False ) -> None:
) -> None:
"""Request refresh gateway devices by group_id""" """Request refresh gateway devices by group_id"""
refresh_timer = self._mips_local_state_changed_timers.get( refresh_timer = self._mips_local_state_changed_timers.get(
group_id, None) group_id, None)
@ -1865,9 +1865,11 @@ class MIoTClient:
if not self._refresh_props_list: if not self._refresh_props_list:
return return
# Cloud, Central hub gateway, Lan control # Cloud, Central hub gateway, Lan control
if (await self.__refresh_props_from_cloud() or if (
await self.__refresh_props_from_gw() or await self.__refresh_props_from_cloud()
await self.__refresh_props_from_lan()): or await self.__refresh_props_from_gw()
or await self.__refresh_props_from_lan()
):
self._refresh_props_retry_count = 0 self._refresh_props_retry_count = 0
if self._refresh_props_list: if self._refresh_props_list:
self._refresh_props_timer = self._main_loop.call_later( self._refresh_props_timer = self._main_loop.call_later(

View File

@ -624,9 +624,9 @@ class MIoTHttpClient:
devices.update(result) devices.update(result)
return devices return devices
async def get_devices_async(self, async def get_devices_async(
home_ids: Optional[list[str]] = None self, home_ids: Optional[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] = {}
@ -787,11 +787,9 @@ class MIoTHttpClient:
self._get_prop_timer = None self._get_prop_timer = None
return True return True
async def get_prop_async(self, async def get_prop_async(
did: str, self, did: str, siid: int, piid: int, immediately: bool = False
siid: int, ) -> Any:
piid: int,
immediately: bool = False) -> 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}'
@ -843,8 +841,9 @@ class MIoTHttpClient:
return res_obj['result'] return res_obj['result']
async def action_async(self, did: str, siid: int, aiid: int, async def action_async(
in_list: list[dict]) -> dict: self, did: str, siid: int, aiid: int, in_list: list[dict]
) -> dict:
""" """
params = {"did": "xxxx", "siid": 2, "aiid": 1, "in": []} params = {"did": "xxxx", "siid": 2, "aiid": 1, "in": []}
""" """

View File

@ -78,6 +78,7 @@ from homeassistant.const import (
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.components.switch import SwitchDeviceClass from homeassistant.components.switch import SwitchDeviceClass
# pylint: disable=relative-beyond-top-level # pylint: disable=relative-beyond-top-level
from .specs.specv2entity import ( from .specs.specv2entity import (
SPEC_ACTION_TRANS_MAP, SPEC_ACTION_TRANS_MAP,
@ -242,8 +243,8 @@ class MIoTDevice:
did=self._did, siid=siid, aiid=aiid, in_list=in_list) did=self._did, siid=siid, aiid=aiid, in_list=in_list)
def sub_device_state( def sub_device_state(
self, key: str, handler: Callable[[str, MIoTDeviceState], self, key: str, handler: Callable[[str, MIoTDeviceState], None]
None]) -> int: ) -> int:
sub_id = self.__gen_sub_id() sub_id = self.__gen_sub_id()
if key in self._device_state_sub_list: if key in self._device_state_sub_list:
self._device_state_sub_list[key][str(sub_id)] = handler self._device_state_sub_list[key][str(sub_id)] = handler
@ -409,7 +410,8 @@ class MIoTDevice:
self._action_list[action.platform].append(action) self._action_list[action.platform].append(action)
def parse_miot_device_entity( def parse_miot_device_entity(
self, spec_instance: MIoTSpecInstance) -> Optional[MIoTEntityData]: self, spec_instance: MIoTSpecInstance
) -> Optional[MIoTEntityData]:
if spec_instance.name not in SPEC_DEVICE_TRANS_MAP: if spec_instance.name not in SPEC_DEVICE_TRANS_MAP:
return None return None
spec_name: str = spec_instance.name spec_name: str = spec_instance.name
@ -866,8 +868,9 @@ class MIoTDevice:
self._sub_id += 1 self._sub_id += 1
return self._sub_id return self._sub_id
def __on_device_state_changed(self, did: str, state: MIoTDeviceState, def __on_device_state_changed(
ctx: Any) -> None: self, did: str, state: MIoTDeviceState, ctx: Any
) -> None:
self._online = state == MIoTDeviceState.ONLINE self._online = state == MIoTDeviceState.ONLINE
for key, sub_list in self._device_state_sub_list.items(): for key, sub_list in self._device_state_sub_list.items():
for handler in sub_list.values(): for handler in sub_list.values():
@ -1148,9 +1151,9 @@ class MIoTServiceEntity(Entity):
self.async_write_ha_state() self.async_write_ha_state()
return result return result
async def action_async(self, async def action_async(
action: MIoTSpecAction, self, action: MIoTSpecAction, in_list: Optional[list] = None
in_list: Optional[list] = None) -> bool: ) -> bool:
if not action: if not action:
raise RuntimeError( raise RuntimeError(
f'action failed, action is None, {self.entity_id}, {self.name}') f'action failed, action is None, {self.entity_id}, {self.name}')
@ -1449,10 +1452,9 @@ class MIoTEventEntity(Entity):
sub_id=self._value_sub_id) sub_id=self._value_sub_id)
@abstractmethod @abstractmethod
def on_event_occurred(self, def on_event_occurred(
name: str, self, name: str, arguments: dict[str, Any] | None = None
arguments: dict[str, Any] | None = None) -> None: ) -> None: ...
...
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)

View File

@ -71,6 +71,7 @@ from .miot_mdns import MipsService, MipsServiceState
from .common import ( from .common import (
randomize_float, load_yaml_file, gen_absolute_path, MIoTMatcher) randomize_float, load_yaml_file, gen_absolute_path, MIoTMatcher)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -812,11 +813,9 @@ class MIoTLan:
return True return True
@final @final
async def get_prop_async(self, async def get_prop_async(
did: str, self, did: str, siid: int, piid: int, timeout_ms: int = 10000
siid: int, ) -> Any:
piid: int,
timeout_ms: int = 10000) -> Any:
self.__assert_service_ready() self.__assert_service_ready()
result_obj = await self.__call_api_async( result_obj = await self.__call_api_async(
did=did, msg={ did=did, msg={
@ -920,10 +919,9 @@ class MIoTLan:
timeout_ms=timeout_ms)) timeout_ms=timeout_ms))
return await fut return await fut
async def __call_api_async(self, async def __call_api_async(
did: str, self, did: str, msg: dict, timeout_ms: int = 10000
msg: dict, ) -> dict:
timeout_ms: int = 10000) -> dict:
def call_api_handler(msg: dict, fut: asyncio.Future): def call_api_handler(msg: dict, fut: asyncio.Future):
self._main_loop.call_soon_threadsafe( self._main_loop.call_soon_threadsafe(
@ -1026,7 +1024,6 @@ class MIoTLan:
handler_ctx: Any = None, handler_ctx: Any = None,
timeout_ms: Optional[int] = None timeout_ms: Optional[int] = None
) -> None: ) -> None:
def request_timeout_handler(req_data: _MIoTLanRequestData): def request_timeout_handler(req_data: _MIoTLanRequestData):
self._pending_requests.pop(req_data.msg_id, None) self._pending_requests.pop(req_data.msg_id, None)
if req_data and req_data.handler: if req_data and req_data.handler:
@ -1253,8 +1250,9 @@ class MIoTLan:
except Exception as err: # pylint: disable=broad-exception-caught except Exception as err: # pylint: disable=broad-exception-caught
_LOGGER.error('socket read handler error, %s', err) _LOGGER.error('socket read handler error, %s', err)
def __raw_message_handler(self, data: bytearray, data_len: int, ip: str, def __raw_message_handler(
if_name: str) -> None: self, data: bytearray, data_len: int, ip: str, if_name: str
) -> None:
if data[:2] != self.OT_HEADER: if data[:2] != self.OT_HEADER:
return return
# Keep alive message # Keep alive message
@ -1278,8 +1276,11 @@ class MIoTLan:
int(data[28]) == self.OT_SUPPORT_WILDCARD_SUB) int(data[28]) == self.OT_SUPPORT_WILDCARD_SUB)
sub_ts = struct.unpack('>I', data[20:24])[0] sub_ts = struct.unpack('>I', data[20:24])[0]
sub_type = int(data[27]) sub_type = int(data[27])
if (device.supported_wildcard_sub and sub_type in [0, 1, 4] and if (
sub_ts != device.sub_ts): device.supported_wildcard_sub
and sub_type in [0, 1, 4]
and sub_ts != device.sub_ts
):
device.subscribed = False device.subscribed = False
device.subscribe() device.subscribe()
if data_len > self.OT_PROBE_LEN: if data_len > self.OT_PROBE_LEN:
@ -1357,8 +1358,9 @@ class MIoTLan:
filter_id) filter_id)
return False return False
def __sendto(self, if_name: Optional[str], data: bytes, address: str, def __sendto(
port: int) -> None: self, if_name: Optional[str], data: bytes, address: str, port: int
) -> None:
if if_name is None: if if_name is None:
# Broadcast # Broadcast
for if_n, sock in self._broadcast_socks.items(): for if_n, sock in self._broadcast_socks.items():

View File

@ -415,8 +415,9 @@ class _MipsClient(ABC):
self._mqtt.disable_logger() self._mqtt.disable_logger()
@final @final
def sub_mips_state(self, key: str, handler: Callable[[str, bool], def sub_mips_state(
Coroutine]) -> bool: self, key: str, handler: Callable[[str, bool], Coroutine]
) -> bool:
"""Subscribe mips state. """Subscribe mips state.
NOTICE: callback to main loop thread NOTICE: callback to main loop thread
This will be called before the client is connected. This will be called before the client is connected.
@ -484,12 +485,9 @@ class _MipsClient(ABC):
) -> dict[str, dict]: ... ) -> dict[str, dict]: ...
@abstractmethod @abstractmethod
async def get_prop_async(self, async def get_prop_async(
did: str, self, did: str, siid: int, piid: int, timeout_ms: int = 10000
siid: int, ) -> Any: ...
piid: int,
timeout_ms: int = 10000) -> Any:
...
@abstractmethod @abstractmethod
async def set_prop_async( async def set_prop_async(
@ -504,16 +502,13 @@ class _MipsClient(ABC):
) -> dict: ... ) -> dict: ...
@abstractmethod @abstractmethod
def _on_mips_message(self, topic: str, payload: bytes) -> None: def _on_mips_message(self, topic: str, payload: bytes) -> None: ...
...
@abstractmethod @abstractmethod
def _on_mips_connect(self, rc: int, props: dict) -> None: def _on_mips_connect(self, rc: int, props: dict) -> None: ...
...
@abstractmethod @abstractmethod
def _on_mips_disconnect(self, rc: int, props: dict) -> None: def _on_mips_disconnect(self, rc: int, props: dict) -> None: ...
...
@final @final
def _mips_sub_internal(self, topic: str) -> None: def _mips_sub_internal(self, topic: str) -> None:
@ -1273,11 +1268,9 @@ class MipsLocalClient(_MipsClient):
return self.__unreg_broadcast_external(topic=topic) return self.__unreg_broadcast_external(topic=topic)
@final @final
async def get_prop_safe_async(self, async def get_prop_safe_async(
did: str, self, did: str, siid: int, piid: int, timeout_ms: int = 10000
siid: int, ) -> Any:
piid: int,
timeout_ms: int = 10000) -> 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({
@ -1297,11 +1290,9 @@ class MipsLocalClient(_MipsClient):
return await fut return await fut
@final @final
async def get_prop_async(self, async def get_prop_async(
did: str, self, did: str, siid: int, piid: int, timeout_ms: int = 10000
siid: int, ) -> Any:
piid: int,
timeout_ms: int = 10000) -> 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({
@ -1457,9 +1448,9 @@ class MipsLocalClient(_MipsClient):
return result_obj['result'] return result_obj['result']
@final @final
async def exec_action_group_list_async(self, async def exec_action_group_list_async(
ag_id: str, self, ag_id: str, timeout_ms: int = 10000
timeout_ms: int = 10000) -> dict: ) -> dict:
result_obj = await self.__request_async( result_obj = await self.__request_async(
topic='proxy/execMijiaActionGroup', topic='proxy/execMijiaActionGroup',
payload=f'{{"id":"{ag_id}"}}', payload=f'{{"id":"{ag_id}"}}',
@ -1483,8 +1474,8 @@ class MipsLocalClient(_MipsClient):
@final @final
@on_dev_list_changed.setter @on_dev_list_changed.setter
def on_dev_list_changed( def on_dev_list_changed(
self, func: Optional[Callable[[Any, list[str]], self, func: Optional[Callable[[Any, list[str]], Coroutine]]
Coroutine]]) -> None: ) -> None:
"""run in main loop.""" """run in main loop."""
self._on_dev_list_changed = func self._on_dev_list_changed = func
@ -1657,10 +1648,9 @@ class MipsLocalClient(_MipsClient):
return True return True
@final @final
async def __request_async(self, async def __request_async(
topic: str, self, topic: str, payload: str, timeout_ms: int = 10000
payload: str, ) -> dict:
timeout_ms: int = 10000) -> 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):