Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve devices filter & optimize the network detection logic #458

Merged
merged 26 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b14fb6b
fix: fix miot http type error
topsworld Dec 25, 2024
748523c
style: change some miot cloud log level
topsworld Dec 26, 2024
b510dff
feat: improve devices filter
topsworld Dec 27, 2024
7606ae5
feat: update save devices logic
topsworld Dec 27, 2024
405c435
refator: refactor miot network
topsworld Dec 28, 2024
0afb145
feat: update miot_client.get_miot_instance_async
topsworld Dec 28, 2024
7839a67
feat: option flow support network detect config
topsworld Dec 29, 2024
c5b2f43
doc: update translations
topsworld Dec 29, 2024
742e84d
feat: update config flow network detect logic
topsworld Dec 29, 2024
c442575
style: change miot client refresh prop log level
topsworld Dec 29, 2024
caedd1f
feat: config flow support network check
topsworld Dec 30, 2024
17fd8bc
doc: update translations
topsworld Dec 30, 2024
a8359a2
refactor: rename func name
topsworld Dec 30, 2024
48e8a61
fix: ignore invalid type error
topsworld Dec 30, 2024
4fa4fca
feat: option flow add check network deps
topsworld Dec 30, 2024
57f2be0
--amend
topsworld Dec 30, 2024
c2b210c
--amend
topsworld Dec 30, 2024
c24f23e
feat: check mqtt broker
topsworld Dec 30, 2024
a64a1d5
feat: config flow support check network deps
topsworld Dec 30, 2024
4834820
feat: update manifest requirements, paho-mqtt<2.0.0
topsworld Dec 30, 2024
e169df4
fix: fix mqtt broker check logic
topsworld Dec 30, 2024
c4bc72d
Merge branch 'main' into feat-entities-filter
topsworld Dec 30, 2024
d24b07b
style: remove unuse params
topsworld Dec 30, 2024
efda70e
feat: show integration instance id
topsworld Dec 30, 2024
9ef5278
feat: update data_schema from required to optional
topsworld Dec 31, 2024
61effe5
fix: translation text error
topsworld Dec 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
741 changes: 547 additions & 194 deletions custom_components/xiaomi_home/config_flow.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion custom_components/xiaomi_home/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
],
"requirements": [
"construct>=2.10.56",
"paho-mqtt<=2.0.0",
"paho-mqtt<2.0.0",
"numpy",
"cryptography",
"psutil"
Expand Down
46 changes: 25 additions & 21 deletions custom_components/xiaomi_home/miot/miot_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1558,7 +1558,7 @@ async def __refresh_props_from_cloud(self, patch_len: int = 150) -> bool:
None)
self.__on_prop_msg(params=result, ctx=None)
if request_list:
_LOGGER.error(
_LOGGER.info(
'refresh props failed, cloud, %s',
list(request_list.keys()))
request_list = None
Expand Down Expand Up @@ -1614,7 +1614,7 @@ async def __refresh_props_from_gw(self) -> bool:
succeed_once = True
if succeed_once:
return True
_LOGGER.error(
_LOGGER.info(
'refresh props failed, gw, %s', list(request_list.keys()))
# Add failed request back to the list
self._refresh_props_list.update(request_list)
Expand Down Expand Up @@ -1657,7 +1657,7 @@ async def __refresh_props_from_lan(self) -> bool:
succeed_once = True
if succeed_once:
return True
_LOGGER.error(
_LOGGER.info(
'refresh props failed, lan, %s', list(request_list.keys()))
# Add failed request back to the list
self._refresh_props_list.update(request_list)
Expand Down Expand Up @@ -1689,10 +1689,10 @@ async def __refresh_props_handler(self) -> None:
if self._refresh_props_timer:
self._refresh_props_timer.cancel()
self._refresh_props_timer = None
_LOGGER.error('refresh props failed, retry count exceed')
_LOGGER.info('refresh props failed, retry count exceed')
return
self._refresh_props_retry_count += 1
_LOGGER.error(
_LOGGER.info(
'refresh props failed, retry, %s', self._refresh_props_retry_count)
self._refresh_props_timer = self._main_loop.call_later(
3, lambda: self._main_loop.create_task(
Expand Down Expand Up @@ -1851,15 +1851,6 @@ async def get_miot_instance_async(
loop: asyncio.AbstractEventLoop = asyncio.get_running_loop()
if loop is None:
raise MIoTClientError('loop is None')
# MIoT network
network: Optional[MIoTNetwork] = hass.data[DOMAIN].get(
'miot_network', None)
if not network:
network = MIoTNetwork(loop=loop)
hass.data[DOMAIN]['miot_network'] = network
await network.init_async(
refresh_interval=NETWORK_REFRESH_INTERVAL)
_LOGGER.info('create miot_network instance')
# MIoT storage
storage: Optional[MIoTStorage] = hass.data[DOMAIN].get(
'miot_storage', None)
Expand All @@ -1868,28 +1859,41 @@ async def get_miot_instance_async(
root_path=entry_data['storage_path'], loop=loop)
hass.data[DOMAIN]['miot_storage'] = storage
_LOGGER.info('create miot_storage instance')
global_config: dict = await storage.load_user_config_async(
uid='global_config', cloud_server='all',
keys=['network_detect_addr', 'net_interfaces', 'enable_subscribe'])
# MIoT network
network_detect_addr: dict = global_config.get(
'network_detect_addr', {})
network: Optional[MIoTNetwork] = hass.data[DOMAIN].get(
'miot_network', None)
if not network:
network = MIoTNetwork(
ip_addr_list=network_detect_addr.get('ip', []),
url_addr_list=network_detect_addr.get('url', []),
refresh_interval=NETWORK_REFRESH_INTERVAL,
loop=loop)
hass.data[DOMAIN]['miot_network'] = network
await network.init_async()
_LOGGER.info('create miot_network instance')
# MIoT service
mips_service: Optional[MipsService] = hass.data[DOMAIN].get(
'mips_service', None)
if not mips_service:
aiozc = await zeroconf.async_get_async_instance(hass)
mips_service: MipsService = MipsService(aiozc=aiozc, loop=loop)
mips_service = MipsService(aiozc=aiozc, loop=loop)
hass.data[DOMAIN]['mips_service'] = mips_service
await mips_service.init_async()
_LOGGER.info('create mips_service instance')
# MIoT lan
miot_lan: Optional[MIoTLan] = hass.data[DOMAIN].get(
'miot_lan', None)
if not miot_lan:
lan_config = (await storage.load_user_config_async(
uid='global_config',
cloud_server='all',
keys=['net_interfaces', 'enable_subscribe'])) or {}
miot_lan = MIoTLan(
net_ifs=lan_config.get('net_interfaces', []),
net_ifs=global_config.get('net_interfaces', []),
network=network,
mips_service=mips_service,
enable_subscribe=lan_config.get('enable_subscribe', False),
enable_subscribe=global_config.get('enable_subscribe', False),
loop=loop)
hass.data[DOMAIN]['miot_lan'] = miot_lan
_LOGGER.info('create miot_lan instance')
Expand Down
50 changes: 26 additions & 24 deletions custom_components/xiaomi_home/miot/miot_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,20 +224,20 @@ class MIoTHttpClient:
_client_id: str
_access_token: str

_get_prop_timer: asyncio.TimerHandle
_get_prop_list: dict[str, dict[str, asyncio.Future | str | bool]]
_get_prop_timer: Optional[asyncio.TimerHandle]
_get_prop_list: dict[str, dict]

def __init__(
self, cloud_server: str, client_id: str, access_token: str,
loop: Optional[asyncio.AbstractEventLoop] = None
) -> None:
self._main_loop = loop or asyncio.get_running_loop()
self._host = None
self._base_url = None
self._client_id = None
self._access_token = None
self._host = DEFAULT_OAUTH2_API_HOST
self._base_url = ''
self._client_id = ''
self._access_token = ''

self._get_prop_timer: asyncio.TimerHandle = None
self._get_prop_timer = None
self._get_prop_list = {}

if (
Expand All @@ -258,8 +258,9 @@ async def deinit_async(self) -> None:
self._get_prop_timer.cancel()
self._get_prop_timer = None
for item in self._get_prop_list.values():
fut: asyncio.Future = item.get('fut')
fut.cancel()
fut: Optional[asyncio.Future] = item.get('fut', None)
if fut:
fut.cancel()
self._get_prop_list.clear()
if self._session and not self._session.closed:
await self._session.close()
Expand All @@ -270,9 +271,7 @@ def update_http_header(
access_token: Optional[str] = None
) -> None:
if isinstance(cloud_server, str):
if cloud_server == 'cn':
self._host = DEFAULT_OAUTH2_API_HOST
else:
if cloud_server != 'cn':
self._host = f'{cloud_server}.{DEFAULT_OAUTH2_API_HOST}'
self._base_url = f'https://{self._host}'
if isinstance(client_id, str):
Expand Down Expand Up @@ -350,8 +349,8 @@ async def __mihome_api_post_async(
async def get_user_info_async(self) -> dict:
http_res = await self._session.get(
url='https://open.account.xiaomi.com/user/profile',
params={'clientId': self._client_id,
'token': self._access_token},
params={
'clientId': self._client_id, 'token': self._access_token},
headers={'content-type': 'application/x-www-form-urlencoded'},
timeout=MIHOME_HTTP_API_TIMEOUT
)
Expand Down Expand Up @@ -386,7 +385,9 @@ async def get_central_cert_async(self, csr: str) -> Optional[str]:

return cert

async def __get_dev_room_page_async(self, max_id: str = None) -> dict:
async def __get_dev_room_page_async(
self, max_id: Optional[str] = None
) -> dict:
res_obj = await self.__mihome_api_post_async(
url_path='/app/v2/homeroom/get_dev_room_page',
data={
Expand Down Expand Up @@ -442,7 +443,7 @@ async def get_homeinfos_async(self) -> dict:
if 'result' not in res_obj:
raise MIoTHttpError('invalid response result')

uid: str = None
uid: Optional[str] = None
home_infos: dict = {}
for device_source in ['homelist', 'share_home_list']:
home_infos.setdefault(device_source, {})
Expand Down Expand Up @@ -507,7 +508,7 @@ async def get_uid_async(self) -> str:
return (await self.get_homeinfos_async()).get('uid', None)

async def __get_device_list_page_async(
self, dids: list[str], start_did: str = None
self, dids: list[str], start_did: Optional[str] = None
) -> dict[str, dict]:
req_data: dict = {
'limit': 200,
Expand Down Expand Up @@ -575,9 +576,9 @@ async def __get_device_list_page_async(

async def get_devices_with_dids_async(
self, dids: list[str]
) -> dict[str, dict]:
) -> Optional[dict[str, dict]]:
results: list[dict[str, dict]] = await asyncio.gather(
*[self.__get_device_list_page_async(dids[index:index+150])
*[self.__get_device_list_page_async(dids=dids[index:index+150])
for index in range(0, len(dids), 150)])
devices = {}
for result in results:
Expand All @@ -587,7 +588,7 @@ async def get_devices_with_dids_async(
return devices

async def get_devices_async(
self, home_ids: list[str] = None
self, home_ids: Optional[list[str]] = None
) -> dict[str, dict]:
homeinfos = await self.get_homeinfos_async()
homes: dict[str, dict[str, Any]] = {}
Expand Down Expand Up @@ -627,8 +628,9 @@ async def get_devices_async(
'group_id': group_id
} for did in room_info.get('dids', [])})
dids = sorted(list(devices.keys()))
results: dict[str, dict] = await self.get_devices_with_dids_async(
dids=dids)
results = await self.get_devices_with_dids_async(dids=dids)
if results is None:
raise MIoTHttpError('get devices failed')
for did in dids:
if did not in results:
devices.pop(did, None)
Expand Down Expand Up @@ -706,7 +708,7 @@ async def __get_prop_handler(self) -> bool:
key = f'{result["did"]}.{result["siid"]}.{result["piid"]}'
prop_obj = self._get_prop_list.pop(key, None)
if prop_obj is None:
_LOGGER.error('get prop error, key not exists, %s', result)
_LOGGER.info('get prop error, key not exists, %s', result)
continue
prop_obj['fut'].set_result(result['value'])
props_req.remove(key)
Expand All @@ -717,7 +719,7 @@ async def __get_prop_handler(self) -> bool:
continue
prop_obj['fut'].set_result(None)
if props_req:
_LOGGER.error(
_LOGGER.info(
'get prop from cloud failed, %s, %s', len(key), props_req)

if self._get_prop_list:
Expand Down
Loading
Loading