diff --git a/custom_components/xiaomi_home/miot/miot_spec.py b/custom_components/xiaomi_home/miot/miot_spec.py index 5424fab..6901271 100644 --- a/custom_components/xiaomi_home/miot/miot_spec.py +++ b/custom_components/xiaomi_home/miot/miot_spec.py @@ -1156,6 +1156,59 @@ class _SpecFilter: return False +class _SpecAdd: + """MIoT-Spec-V2 add for entity conversion.""" + _SPEC_ADD_FILE = 'specs/spec_add.yaml' + _main_loop: asyncio.AbstractEventLoop + _data: Optional[dict] + _selected: Optional[dict] + + def __init__( + self, loop: Optional[asyncio.AbstractEventLoop] = None + ) -> None: + self._main_loop = loop or asyncio.get_running_loop() + self._data = None + + async def init_async(self) -> None: + if isinstance(self._data, dict): + return + add_data = None + self._data = {} + self._selected = None + try: + add_data = await self._main_loop.run_in_executor( + None, load_yaml_file, + os.path.join( + os.path.dirname(os.path.abspath(__file__)), + self._SPEC_ADD_FILE)) + except Exception as err: # pylint: disable=broad-exception-caught + _LOGGER.error('spec add, load file error, %s', err) + return + if not isinstance(add_data, dict): + _LOGGER.error('spec add, invalid spec add content') + return + for key, value in add_data.items(): + if not isinstance(key, str) or not isinstance(value, (dict, str)): + _LOGGER.error('spec add, invalid spec modify data') + return + + self._data = add_data + + async def deinit_async(self) -> None: + self._data = None + self._selected = None + + async def set_spec_async(self, urn: str) -> None: + if not self._data: + return + self._selected = self._data.get(urn, None) + if isinstance(self._selected, str): + return await self.set_spec_async(urn=self._selected) + + def get_spec_add(self) -> Optional[dict]: + return self._selected + + class _SpecModify: """MIoT-Spec-V2 modify for entity conversion.""" _SPEC_MODIFY_FILE = 'specs/spec_modify.yaml' @@ -1259,6 +1312,7 @@ class MIoTSpecParser: _multi_lang: _MIoTSpecMultiLang _bool_trans: _SpecBoolTranslation _spec_filter: _SpecFilter + _spec_add: _SpecAdd _spec_modify: _SpecModify _init_done: bool @@ -1434,6 +1488,8 @@ class MIoTSpecParser: # Filter spec service spec_service.need_filter = self._spec_filter.filter_service( siid=service['iid']) + if spec_service.need_filter: + continue if type_strs[1] != 'miot-spec-v2': spec_service.proprietary = True spec_service.description_trans = ( @@ -1467,6 +1523,8 @@ class MIoTSpecParser: spec_service.need_filter or self._spec_filter.filter_property( siid=service['iid'], piid=property_['iid'])) + if spec_prop.need_filter: + continue if p_type_strs[1] != 'miot-spec-v2': spec_prop.proprietary = spec_service.proprietary or True spec_prop.description_trans = ( @@ -1543,6 +1601,8 @@ class MIoTSpecParser: spec_service.need_filter or self._spec_filter.filter_event( siid=service['iid'], eiid=event['iid'])) + if spec_event.need_filter: + continue if e_type_strs[1] != 'miot-spec-v2': spec_event.proprietary = spec_service.proprietary or True spec_event.description_trans = ( @@ -1579,6 +1639,8 @@ class MIoTSpecParser: spec_service.need_filter or self._spec_filter.filter_action( siid=service['iid'], aiid=action['iid'])) + if spec_action.need_filter: + continue if a_type_strs[1] != 'miot-spec-v2': spec_action.proprietary = spec_service.proprietary or True spec_action.description_trans = ( diff --git a/custom_components/xiaomi_home/miot/specs/spec_add.yaml b/custom_components/xiaomi_home/miot/specs/spec_add.yaml new file mode 100644 index 0000000..8bb3ad0 --- /dev/null +++ b/custom_components/xiaomi_home/miot/specs/spec_add.yaml @@ -0,0 +1,13 @@ +urn:miot-spec-v2:device:airer:0000A00D:hyd-lyjpro:1: + 3: + name: light + description: Moon Light + properties: + 2: + name: true + description: Switch Status + format: bool + access: + - read + - write + - notify diff --git a/test/check_rule_format.py b/test/check_rule_format.py index 79fcc43..bbf9af1 100644 --- a/test/check_rule_format.py +++ b/test/check_rule_format.py @@ -20,6 +20,9 @@ SPEC_BOOL_TRANS_FILE = path.join( SPEC_FILTER_FILE = path.join( ROOT_PATH, '../custom_components/xiaomi_home/miot/specs/spec_filter.yaml') +SPEC_ADD_FILE = path.join( + ROOT_PATH, + '../custom_components/xiaomi_home/miot/specs/spec_add.yaml') SPEC_MODIFY_FILE = path.join( ROOT_PATH, '../custom_components/xiaomi_home/miot/specs/spec_modify.yaml') @@ -139,6 +142,29 @@ def bool_trans(d: dict) -> bool: return True +def spec_add(data: dict) -> bool: + """dict[str, str | dict[str, dict[str, str | dict]]]""" + if not isinstance(data, dict): + return False + for urn, content in data.items(): + if not isinstance(urn, str) or not isinstance(content, (dict, str)): + return False + if isinstance(content, str): + continue + for key, value in content.items(): + if not isinstance(key, int) or not isinstance(value, dict): + return False + for k, v in value.items(): + if not isinstance(k, str): + return False + if k in ['properties', 'actions', 'events']: + if not isinstance(v, dict): + return False + elif not isinstance(v, str): + return False + return True + + def spec_modify(data: dict) -> bool: """dict[str, str | dict[str, dict]]""" if not isinstance(data, dict): @@ -200,6 +226,12 @@ def sort_spec_filter(file_path: str): return filter_data +def sort_spec_add(file_path: str): + filter_data = load_yaml_file(file_path=file_path) + assert isinstance(filter_data, dict), f'{file_path} format error' + return dict(sorted(filter_data.items())) + + def sort_spec_modify(file_path: str): filter_data = load_yaml_file(file_path=file_path) assert isinstance(filter_data, dict), f'{file_path} format error' @@ -222,6 +254,14 @@ def test_spec_filter(): assert spec_filter(data), f'{SPEC_FILTER_FILE} format error' +@pytest.mark.github +def test_spec_add(): + data = load_yaml_file(SPEC_ADD_FILE) + assert isinstance(data, dict) + assert data, f'load {SPEC_ADD_FILE} failed' + assert spec_add(data), f'{SPEC_ADD_FILE} format error' + + @pytest.mark.github def test_spec_modify(): data = load_yaml_file(SPEC_MODIFY_FILE) @@ -319,6 +359,9 @@ def test_sort_spec_data(): sort_data = sort_spec_filter(file_path=SPEC_FILTER_FILE) save_yaml_file(file_path=SPEC_FILTER_FILE, data=sort_data) _LOGGER.info('%s formatted.', SPEC_FILTER_FILE) + sort_data = sort_spec_add(file_path=SPEC_ADD_FILE) + save_yaml_file(file_path=SPEC_ADD_FILE, data=sort_data) + _LOGGER.info('%s formatted.', SPEC_ADD_FILE) sort_data = sort_spec_modify(file_path=SPEC_MODIFY_FILE) save_yaml_file(file_path=SPEC_MODIFY_FILE, data=sort_data) _LOGGER.info('%s formatted.', SPEC_MODIFY_FILE)