Compare commits

..

1 Commits

Author SHA1 Message Date
Ikko Eltociear Ashimine
d3d3cfd815
Merge 5f41b15b7e into 3b89536bda 2025-01-13 13:46:52 +09:00
4 changed files with 62 additions and 230 deletions

View File

@ -308,43 +308,3 @@ async def async_remove_entry(
await miot_cert.remove_user_cert_async()
await miot_cert.remove_user_key_async()
return True
async def async_remove_config_entry_device(
hass: HomeAssistant,
config_entry: ConfigEntry,
device_entry: device_registry.DeviceEntry
) -> bool:
"""Remove the device."""
miot_client: MIoTClient = await get_miot_instance_async(
hass=hass, entry_id=config_entry.entry_id)
if len(device_entry.identifiers) != 1:
_LOGGER.error(
'remove device failed, invalid identifiers, %s, %s',
device_entry.id, device_entry.identifiers)
return False
identifiers = list(device_entry.identifiers)[0]
if identifiers[0] != DOMAIN:
_LOGGER.error(
'remove device failed, invalid domain, %s, %s',
device_entry.id, device_entry.identifiers)
return False
device_info = identifiers[1].split('_')
if len(device_info) != 2:
_LOGGER.error(
'remove device failed, invalid device info, %s, %s',
device_entry.id, device_entry.identifiers)
return False
did = device_info[1]
if did not in miot_client.device_list:
_LOGGER.error(
'remove device failed, device not found, %s, %s',
device_entry.id, device_entry.identifiers)
return False
# Remove device
await miot_client.remove_device_async(did)
device_registry.async_get(hass).async_remove_device(device_entry.id)
_LOGGER.info(
'remove device, %s, %s, %s', device_info[0], did, device_entry.id)
return True

View File

@ -848,30 +848,6 @@ class MIoTClient:
_LOGGER.debug('client unsub device state, %s', did)
return True
async def remove_device_async(self, did: str) -> None:
if did not in self._device_list_cache:
return
sub_from = self._sub_source_list.pop(did, None)
# Unsub
if sub_from:
if sub_from == 'cloud':
self._mips_cloud.unsub_prop(did=did)
self._mips_cloud.unsub_event(did=did)
elif sub_from == 'lan':
self._miot_lan.unsub_prop(did=did)
self._miot_lan.unsub_event(did=did)
elif sub_from in self._mips_local:
mips = self._mips_local[sub_from]
mips.unsub_prop(did=did)
mips.unsub_event(did=did)
# Storage
await self._storage.save_async(
domain='miot_devices',
name=f'{self._uid}_{self._cloud_server}',
data=self._device_list_cache)
# Update notify
self.__request_show_devices_changed_notify()
def __get_exec_error_with_rc(self, rc: int) -> str:
err_msg: str = self._i18n.translate(key=f'error.common.{rc}')
if not err_msg:

View File

@ -15,7 +15,8 @@ TEST_LANG: str = 'zh-Hans'
TEST_UID: str = '123456789'
TEST_CLOUD_SERVER: str = 'cn'
DOMAIN_CLOUD_CACHE: str = 'cloud_cache'
DOMAIN_OAUTH2: str = 'oauth2_info'
DOMAIN_USER_INFO: str = 'user_info'
_LOGGER = logging.getLogger(__name__)
@ -138,18 +139,8 @@ def test_cloud_server() -> str:
@pytest.fixture(scope='session')
def test_domain_cloud_cache() -> str:
return DOMAIN_CLOUD_CACHE
@pytest.fixture(scope='session')
def test_name_oauth2_info() -> str:
return f'{TEST_CLOUD_SERVER}_oauth2_info'
@pytest.fixture(scope='session')
def test_name_uid() -> str:
return f'{TEST_CLOUD_SERVER}_uid'
def test_domain_oauth2() -> str:
return DOMAIN_OAUTH2
@pytest.fixture(scope='session')
@ -158,15 +149,5 @@ def test_name_uuid() -> str:
@pytest.fixture(scope='session')
def test_name_rd_did() -> str:
return f'{TEST_CLOUD_SERVER}_rd_did'
@pytest.fixture(scope='session')
def test_name_homes() -> str:
return f'{TEST_CLOUD_SERVER}_homes'
@pytest.fixture(scope='session')
def test_name_devices() -> str:
return f'{TEST_CLOUD_SERVER}_devices'
def test_domain_user_info() -> str:
return DOMAIN_USER_INFO

View File

@ -16,9 +16,8 @@ async def test_miot_oauth_async(
test_cache_path: str,
test_cloud_server: str,
test_oauth2_redirect_url: str,
test_domain_oauth2: str,
test_uuid: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_uuid: str
) -> dict:
from miot.const import OAUTH2_CLIENT_ID
@ -27,7 +26,7 @@ async def test_miot_oauth_async(
miot_storage = MIoTStorage(test_cache_path)
local_uuid = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
domain=test_domain_oauth2, name=test_name_uuid, type_=str)
uuid = str(local_uuid or test_uuid)
_LOGGER.info('uuid: %s', uuid)
miot_oauth = MIoTOauthClient(
@ -38,7 +37,7 @@ async def test_miot_oauth_async(
oauth_info = None
load_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
if (
isinstance(load_info, dict)
and 'access_token' in load_info
@ -62,11 +61,11 @@ async def test_miot_oauth_async(
oauth_info = res_obj
_LOGGER.info('get_access_token result: %s', res_obj)
rc = await miot_storage.save_async(
test_domain_cloud_cache, test_name_oauth2_info, oauth_info)
test_domain_oauth2, test_cloud_server, oauth_info)
assert rc
_LOGGER.info('save oauth info')
rc = await miot_storage.save_async(
test_domain_cloud_cache, test_name_uuid, uuid)
test_domain_oauth2, test_name_uuid, uuid)
assert rc
_LOGGER.info('save uuid')
@ -87,8 +86,7 @@ async def test_miot_oauth_refresh_token(
test_cache_path: str,
test_cloud_server: str,
test_oauth2_redirect_url: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_domain_oauth2: str,
test_name_uuid: str
):
from miot.const import OAUTH2_CLIENT_ID
@ -97,10 +95,10 @@ async def test_miot_oauth_refresh_token(
miot_storage = MIoTStorage(test_cache_path)
uuid = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_uuid, type_=str)
domain=test_domain_oauth2, name=test_name_uuid, type_=str)
assert isinstance(uuid, str)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict)
assert 'access_token' in oauth_info
assert 'refresh_token' in oauth_info
@ -124,9 +122,9 @@ async def test_miot_oauth_refresh_token(
remaining_time = update_info['expires_ts'] - int(time.time())
assert remaining_time > 0
_LOGGER.info('refresh token, remaining valid time: %ss', remaining_time)
# Save oauth2 info
# Save token
rc = await miot_storage.save_async(
test_domain_cloud_cache, test_name_oauth2_info, update_info)
test_domain_oauth2, test_cloud_server, update_info)
assert rc
_LOGGER.info('refresh token success, %s', update_info)
@ -138,8 +136,7 @@ async def test_miot_oauth_refresh_token(
async def test_miot_cloud_get_nickname_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str
test_domain_oauth2: str
):
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
@ -147,7 +144,7 @@ async def test_miot_cloud_get_nickname_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -167,9 +164,8 @@ async def test_miot_cloud_get_nickname_async(
async def test_miot_cloud_get_uid_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_uid: str
test_domain_oauth2: str,
test_domain_user_info: str
):
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
@ -177,7 +173,7 @@ async def test_miot_cloud_get_uid_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -188,7 +184,8 @@ async def test_miot_cloud_get_uid_async(
_LOGGER.info('your uid: %s', uid)
# Save uid
rc = await miot_storage.save_async(
domain=test_domain_cloud_cache, name=test_name_uid, data=uid)
domain=test_domain_user_info,
name=f'uid_{test_cloud_server}', data=uid)
assert rc
await miot_http.deinit_async()
@ -199,9 +196,8 @@ async def test_miot_cloud_get_uid_async(
async def test_miot_cloud_get_homeinfos_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_uid: str
test_domain_oauth2: str,
test_domain_user_info: str
):
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
@ -209,7 +205,7 @@ async def test_miot_cloud_get_homeinfos_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -227,7 +223,8 @@ async def test_miot_cloud_get_homeinfos_async(
uid = homeinfos.get('uid', '')
# Compare uid with uid in storage
uid2 = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
domain=test_domain_user_info,
name=f'uid_{test_cloud_server}', type_=str)
assert uid == uid2
_LOGGER.info('your uid: %s', uid)
# Get homes
@ -245,11 +242,8 @@ async def test_miot_cloud_get_homeinfos_async(
async def test_miot_cloud_get_devices_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_uid: str,
test_name_homes: str,
test_name_devices: str
test_domain_oauth2: str,
test_domain_user_info: str
):
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
@ -257,7 +251,7 @@ async def test_miot_cloud_get_devices_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -272,7 +266,8 @@ async def test_miot_cloud_get_devices_async(
# Compare uid with uid in storage
uid = devices.get('uid', '')
uid2 = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
domain=test_domain_user_info,
name=f'uid_{test_cloud_server}', type_=str)
assert uid == uid2
_LOGGER.info('your uid: %s', uid)
# Get homes
@ -283,10 +278,12 @@ async def test_miot_cloud_get_devices_async(
_LOGGER.info('your devices count: %s', len(devices))
# Storage homes and devices
rc = await miot_storage.save_async(
domain=test_domain_cloud_cache, name=test_name_homes, data=homes)
domain=test_domain_user_info,
name=f'homes_{test_cloud_server}', data=homes)
assert rc
rc = await miot_storage.save_async(
domain=test_domain_cloud_cache, name=test_name_devices, data=devices)
domain=test_domain_user_info,
name=f'devices_{test_cloud_server}', data=devices)
assert rc
await miot_http.deinit_async()
@ -297,9 +294,8 @@ async def test_miot_cloud_get_devices_async(
async def test_miot_cloud_get_devices_with_dids_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_devices: str
test_domain_oauth2: str,
test_domain_user_info: str
):
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
@ -307,7 +303,7 @@ async def test_miot_cloud_get_devices_with_dids_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -315,7 +311,8 @@ async def test_miot_cloud_get_devices_with_dids_async(
# Load devices
local_devices = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
domain=test_domain_user_info,
name=f'devices_{test_cloud_server}', type_=dict)
assert isinstance(local_devices, dict)
did_list = list(local_devices.keys())
assert len(did_list) > 0
@ -331,96 +328,13 @@ async def test_miot_cloud_get_devices_with_dids_async(
await miot_http.deinit_async()
@pytest.mark.asyncio
async def test_miot_cloud_get_cert(
test_cache_path: str,
test_cloud_server: str,
test_random_did: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_uid: str,
test_name_rd_did: str
):
"""
NOTICE: Currently, only certificate acquisition in the CN region is
supported.
"""
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
from miot.miot_storage import MIoTCert, MIoTStorage
if test_cloud_server.lower() != 'cn':
_LOGGER.info('only support CN region')
return
miot_storage = MIoTStorage(test_cache_path)
uid = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_uid, type_=str)
assert isinstance(uid, str)
_LOGGER.info('your uid: %s', uid)
random_did = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_rd_did, type_=str)
if not random_did:
random_did = test_random_did
rc = await miot_storage.save_async(
domain=test_domain_cloud_cache, name=test_name_rd_did,
data=random_did)
assert rc
assert isinstance(random_did, str)
_LOGGER.info('your random_did: %s', random_did)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
assert isinstance(oauth_info, dict)
assert 'access_token' in oauth_info
access_token = oauth_info['access_token']
# Get certificates
miot_cert = MIoTCert(storage=miot_storage, uid=uid, cloud_server='CN')
assert await miot_cert.verify_ca_cert_async(), 'invalid ca cert'
remaining_time: int = await miot_cert.user_cert_remaining_time_async()
if remaining_time > 0:
_LOGGER.info(
'user cert is valid, remaining time, %ss', remaining_time)
_LOGGER.info((
'if you want to obtain it again, please delete the '
'key, csr, and cert files in %s.'), test_cache_path)
return
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server,
client_id=OAUTH2_CLIENT_ID,
access_token=access_token)
user_key = miot_cert.gen_user_key()
assert isinstance(user_key, str)
_LOGGER.info('user_key str, %s', user_key)
user_csr = miot_cert.gen_user_csr(user_key=user_key, did=random_did)
assert isinstance(user_csr, str)
_LOGGER.info('user_csr str, %s', user_csr)
cert_str = await miot_http.get_central_cert_async(csr=user_csr)
assert isinstance(cert_str, str)
_LOGGER.info('user_cert str, %s', cert_str)
rc = await miot_cert.update_user_key_async(key=user_key)
assert rc
rc = await miot_cert.update_user_cert_async(cert=cert_str)
assert rc
# verify user certificates
remaining_time = await miot_cert.user_cert_remaining_time_async(
cert_data=cert_str.encode('utf-8'), did=random_did)
assert remaining_time > 0
_LOGGER.info('user cert remaining time, %ss', remaining_time)
await miot_http.deinit_async()
@pytest.mark.asyncio
@pytest.mark.dependency()
async def test_miot_cloud_get_prop_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_devices: str
test_domain_oauth2: str,
test_domain_user_info: str
):
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
@ -428,7 +342,7 @@ async def test_miot_cloud_get_prop_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -436,7 +350,8 @@ async def test_miot_cloud_get_prop_async(
# Load devices
local_devices = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
domain=test_domain_user_info,
name=f'devices_{test_cloud_server}', type_=dict)
assert isinstance(local_devices, dict)
did_list = list(local_devices.keys())
assert len(did_list) > 0
@ -455,9 +370,8 @@ async def test_miot_cloud_get_prop_async(
async def test_miot_cloud_get_props_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_devices: str
test_domain_oauth2: str,
test_domain_user_info: str
):
from miot.const import OAUTH2_CLIENT_ID
from miot.miot_cloud import MIoTHttpClient
@ -465,7 +379,7 @@ async def test_miot_cloud_get_props_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -473,7 +387,8 @@ async def test_miot_cloud_get_props_async(
# Load devices
local_devices = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
domain=test_domain_user_info,
name=f'devices_{test_cloud_server}', type_=dict)
assert isinstance(local_devices, dict)
did_list = list(local_devices.keys())
assert len(did_list) > 0
@ -494,9 +409,8 @@ async def test_miot_cloud_get_props_async(
async def test_miot_cloud_set_prop_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_devices: str
test_domain_oauth2: str,
test_domain_user_info: str
):
"""
WARNING: This test case will control the actual device and is not enabled
@ -508,7 +422,7 @@ async def test_miot_cloud_set_prop_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -516,7 +430,8 @@ async def test_miot_cloud_set_prop_async(
# Load devices
local_devices = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
domain=test_domain_user_info,
name=f'devices_{test_cloud_server}', type_=dict)
assert isinstance(local_devices, dict)
assert len(local_devices) > 0
# Set prop
@ -545,9 +460,8 @@ async def test_miot_cloud_set_prop_async(
async def test_miot_cloud_action_async(
test_cache_path: str,
test_cloud_server: str,
test_domain_cloud_cache: str,
test_name_oauth2_info: str,
test_name_devices: str
test_domain_oauth2: str,
test_domain_user_info: str
):
"""
WARNING: This test case will control the actual device and is not enabled
@ -559,7 +473,7 @@ async def test_miot_cloud_action_async(
miot_storage = MIoTStorage(test_cache_path)
oauth_info = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_oauth2_info, type_=dict)
domain=test_domain_oauth2, name=test_cloud_server, type_=dict)
assert isinstance(oauth_info, dict) and 'access_token' in oauth_info
miot_http = MIoTHttpClient(
cloud_server=test_cloud_server, client_id=OAUTH2_CLIENT_ID,
@ -567,7 +481,8 @@ async def test_miot_cloud_action_async(
# Load devices
local_devices = await miot_storage.load_async(
domain=test_domain_cloud_cache, name=test_name_devices, type_=dict)
domain=test_domain_user_info,
name=f'devices_{test_cloud_server}', type_=dict)
assert isinstance(local_devices, dict)
assert len(local_devices) > 0
# Action