feat: custom defined MIoT-Spec-V2 instance

This commit is contained in:
LiShuzhen 2025-03-31 11:39:15 +08:00
parent 64883c2c7e
commit 96b75fc5e6
6 changed files with 500 additions and 494 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
{
"urn:miot-spec-v2:device:airer:0000A00D:hyd-lyjpro:1": [
{
"iid": 3,
"type": "urn:miot-spec-v2:service:light:00007802:hyd-lyjpro:1",
"description": "Moon Light",
"properties": [
{
"iid": 2,
"type": "urn:miot-spec-v2:property:on:00000006:hyd-lyjpro:1",
"description": "Switch Status",
"format": "bool",
"access": [
"read",
"write",
"notify"
]
}
]
}
]
}

View File

@ -1,13 +0,0 @@
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

View File

@ -41,3 +41,6 @@ urn:miot-spec-v2:device:motion-sensor:0000A014:xiaomi-pir1:
urn:miot-spec-v2:device:router:0000A036:xiaomi-rd03: urn:miot-spec-v2:device:router:0000A036:xiaomi-rd03:
services: services:
- '*' - '*'
urn:miot-spec-v2:device:airer:0000A00D:hyd-lyjpro:
properties:
- '3.2'

View File

@ -1,3 +1,48 @@
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:1: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:5: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6:
prop.10.6:
unit: none
urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-s1:1:
prop.2.5:
name: voc-density
urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:1:
prop.2.3:
value-range:
- 0
- 1
- 1
urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:2: urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:1
urn:miot-spec-v2:device:airer:0000A00D:mrbond-m33a:1:
prop.2.3:
name: current-position-a
prop.2.11:
name: current-position-b
urn:miot-spec-v2:device:bath-heater:0000A028:mike-2:1:
prop.3.1:
name: mode-a
prop.3.11:
name: mode-b
prop.3.12:
name: mode-c
urn:miot-spec-v2:device:bath-heater:0000A028:opple-acmoto:1:
prop.5.2:
value-list:
- value: 1
description: low
- value: 128
description: medium
- value: 255
description: high
urn:miot-spec-v2:device:bath-heater:0000A028:xiaomi-s1:1:
prop.4.4:
name: fan-level-ventilation
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1:
prop.2.2:
name: fan-level-a
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1:
prop.2.1: prop.2.1:
name: access-mode name: access-mode
@ -14,16 +59,23 @@ urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1:
- notify - notify
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:2: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1 urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:2: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:3: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1 urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:3: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:1
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:2: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3:
prop.5.1: prop.5.1:
name: power-consumption expr: round(src_value*6/1000000, 3)
expr: round(src_value/1000, 3)
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:2: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp1md:1: urn:miot-spec-v2:device:outlet:0000A002:cuco-cp1md:1:
prop.2.2: prop.2.2:
name: power-consumption name: power-consumption
expr: round(src_value/1000, 3) expr: round(src_value/1000, 3)
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:1: urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2:
prop.2.3:
expr: round(src_value/10, 1)
prop.2.4:
unit: mA
prop.3.2:
expr: round(src_value/10, 1)
urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1: urn:miot-spec-v2:device:outlet:0000A002:cuco-v3:1:
prop.11.1: prop.11.1:
name: power-consumption name: power-consumption
@ -33,6 +85,17 @@ urn:miot-spec-v2:device:outlet:0000A002:zimi-zncz01:2:0000C816:
prop.3.1: prop.3.1:
name: electric-power name: electric-power
expr: round(src_value/100, 2) expr: round(src_value/100, 2)
urn:miot-spec-v2:device:plant-monitor:0000A030:hhcc-v1:1:
prop.2.1:
name: soil-moisture
icon: mdi:watering-can
prop.2.2:
name: soil-ec
icon: mdi:sprout-outline
unit: μS/cm
urn:miot-spec-v2:device:relay:0000A03D:lumi-c2acn01:1:
prop.4.1:
unit: kWh
urn:miot-spec-v2:device:router:0000A036:xiaomi-rd08:1: urn:miot-spec-v2:device:router:0000A036:xiaomi-rd08:1:
prop.2.1: prop.2.1:
name: download-speed name: download-speed
@ -42,45 +105,6 @@ urn:miot-spec-v2:device:router:0000A036:xiaomi-rd08:1:
name: upload-speed name: upload-speed
icon: mdi:upload icon: mdi:upload
unit: B/s unit: B/s
urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:1:
prop.2.3:
value-range:
- 0
- 1
- 1
urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:2: urn:miot-spec-v2:device:airer:0000A00D:hyd-znlyj5:1
urn:miot-spec-v2:device:bath-heater:0000A028:opple-acmoto:1:
prop.5.2:
value-list:
- value: 1
description: low
- value: 128
description: medium
- value: 255
description: high
urn:miot-spec-v2:device:bath-heater:0000A028:mike-2:1:
prop.3.1:
name: mode-a
prop.3.11:
name: mode-b
prop.3.12:
name: mode-c
urn:miot-spec-v2:device:fan:0000A005:xiaomi-p51:1:
prop.2.2:
name: fan-level-a
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6:
prop.10.6:
unit: none
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:1: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:2: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:3: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:4: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:5: urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-m9:6
urn:miot-spec-v2:device:airer:0000A00D:mrbond-m33a:1:
prop.2.3:
name: current-position-a
prop.2.11:
name: current-position-b
urn:miot-spec-v2:device:thermostat:0000A031:suittc-wk168:1: urn:miot-spec-v2:device:thermostat:0000A031:suittc-wk168:1:
prop.2.3: prop.2.3:
value-list: value-list:
@ -116,38 +140,8 @@ urn:miot-spec-v2:device:thermostat:0000A031:suittc-wk168:1:
description: '15' description: '15'
- value: 16 - value: 16
description: '16' description: '16'
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3:
prop.5.1:
expr: round(src_value*6/1000000, 3)
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:1: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3
urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:2: urn:miot-spec-v2:device:outlet:0000A002:chuangmi-212a01:3
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2:
prop.2.3:
expr: round(src_value/10, 1)
prop.2.4:
unit: mA
prop.3.2:
expr: round(src_value/10, 1)
urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:1: urn:miot-spec-v2:device:outlet:0000A002:cuco-cp2:2
urn:miot-spec-v2:device:plant-monitor:0000A030:hhcc-v1:1:
prop.2.1:
name: soil-moisture
icon: mdi:watering-can
prop.2.2:
name: soil-ec
icon: mdi:sprout-outline
unit: μS/cm
urn:miot-spec-v2:device:air-monitor:0000A008:cgllc-s1:1:
prop.2.5:
name: voc-density
urn:miot-spec-v2:device:water-purifier:0000A013:roswan-lte01:1:0000D05A: urn:miot-spec-v2:device:water-purifier:0000A013:roswan-lte01:1:0000D05A:
prop.4.1: prop.4.1:
unit: ppm unit: ppm
prop.4.2: prop.4.2:
unit: ppm unit: ppm
urn:miot-spec-v2:device:relay:0000A03D:lumi-c2acn01:1:
prop.4.1:
unit: kWh
urn:miot-spec-v2:device:bath-heater:0000A028:xiaomi-s1:1:
prop.4.4:
name: fan-level-ventilation

View File

@ -15,17 +15,13 @@ TRANS_RELATIVE_PATH: str = path.join(
MIOT_I18N_RELATIVE_PATH: str = path.join( MIOT_I18N_RELATIVE_PATH: str = path.join(
ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n') ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n')
SPEC_BOOL_TRANS_FILE = path.join( SPEC_BOOL_TRANS_FILE = path.join(
ROOT_PATH, ROOT_PATH, '../custom_components/xiaomi_home/miot/specs/bool_trans.yaml')
'../custom_components/xiaomi_home/miot/specs/bool_trans.yaml')
SPEC_FILTER_FILE = path.join( SPEC_FILTER_FILE = path.join(
ROOT_PATH, ROOT_PATH, '../custom_components/xiaomi_home/miot/specs/spec_filter.yaml')
'../custom_components/xiaomi_home/miot/specs/spec_filter.yaml')
SPEC_ADD_FILE = path.join( SPEC_ADD_FILE = path.join(
ROOT_PATH, ROOT_PATH, '../custom_components/xiaomi_home/miot/specs/spec_add.json')
'../custom_components/xiaomi_home/miot/specs/spec_add.yaml')
SPEC_MODIFY_FILE = path.join( SPEC_MODIFY_FILE = path.join(
ROOT_PATH, ROOT_PATH, '../custom_components/xiaomi_home/miot/specs/spec_modify.yaml')
'../custom_components/xiaomi_home/miot/specs/spec_modify.yaml')
def load_json_file(file_path: str) -> Optional[dict]: def load_json_file(file_path: str) -> Optional[dict]:
@ -33,7 +29,7 @@ def load_json_file(file_path: str) -> Optional[dict]:
with open(file_path, 'r', encoding='utf-8') as file: with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file) return json.load(file)
except FileNotFoundError: except FileNotFoundError:
_LOGGER.info('%s is not found.', file_path,) _LOGGER.info('%s is not found.', file_path)
return None return None
except json.JSONDecodeError: except json.JSONDecodeError:
_LOGGER.info('%s is not a valid JSON file.', file_path) _LOGGER.info('%s is not a valid JSON file.', file_path)
@ -59,9 +55,12 @@ def load_yaml_file(file_path: str) -> Optional[dict]:
def save_yaml_file(file_path: str, data: dict) -> None: def save_yaml_file(file_path: str, data: dict) -> None:
with open(file_path, 'w', encoding='utf-8') as file: with open(file_path, 'w', encoding='utf-8') as file:
yaml.safe_dump( yaml.safe_dump(data,
data, file, default_flow_style=False, file,
allow_unicode=True, indent=2, sort_keys=False) default_flow_style=False,
allow_unicode=True,
indent=2,
sort_keys=False)
def dict_str_str(d: dict) -> bool: def dict_str_str(d: dict) -> bool:
@ -135,33 +134,109 @@ def bool_trans(d: dict) -> bool:
for key, trans in d['translate'].items(): for key, trans in d['translate'].items():
trans_keys: set[str] = set(trans.keys()) trans_keys: set[str] = set(trans.keys())
if set(trans.keys()) != default_keys: if set(trans.keys()) != default_keys:
_LOGGER.info( _LOGGER.info('bool trans inconsistent, %s, %s, %s', key,
'bool trans inconsistent, %s, %s, %s', default_keys, trans_keys)
key, default_keys, trans_keys)
return False return False
return True return True
def spec_add(data: dict) -> bool: def spec_add(data: dict) -> bool:
"""dict[str, str | dict[str, dict[str, str | dict]]]""" """dict[str, list[dict[str, int| str | list]]]"""
if not isinstance(data, dict): if not isinstance(data, dict):
return False return False
for urn, content in data.items(): for urn, content in data.items():
if not isinstance(urn, str) or not isinstance(content, (dict, str)): if not isinstance(urn, str) or not isinstance(content, (list, str)):
return False return False
if isinstance(content, str): if isinstance(content, str):
continue continue
for key, value in content.items(): for service in content:
if not isinstance(key, int) or not isinstance(value, dict): if ('iid' not in service) or ('type' not in service) or (
'description'
not in service) or (('properties' not in service) and
('actions' not in service) and
('events' not in service)):
return False return False
for k, v in value.items(): type_strs: list[str] = service['type'].split(':')
if not isinstance(k, str): if type_strs[1] != 'miot-spec-v2':
return False
if 'properties' in service:
if not isinstance(service['properties'], list):
return False return False
if k in ['properties', 'actions', 'events']: for prop in service['properties']:
if not isinstance(v, dict): if ('iid' not in prop) or ('type' not in prop) or (
'description' not in prop) or (
'format' not in prop) or ('access' not in prop):
return False return False
elif not isinstance(v, str): if not isinstance(prop['iid'], int) or not isinstance(
prop['type'], str) or not isinstance(
prop['description'], str) or not isinstance(
prop['format'], str) or not isinstance(
prop['access'], list):
return False
type_strs = prop['type'].split(':')
if type_strs[1] != 'miot-spec-v2':
return False
for access in prop['access']:
if access not in ['read', 'write', 'notify']:
return False
if 'value-range' in prop:
if not isinstance(prop['value-range'], list):
return False
for value in prop['value-range']:
if not isinstance(value, (int, float)):
return False
if 'value-list' in prop:
if not isinstance(prop['value-list'], list):
return False
for item in prop['value-list']:
if 'value' not in item or 'description' not in item:
return False
if not isinstance(item['value'],
int) or not isinstance(
item['description'], str):
return False
if 'actions' in service:
if not isinstance(service['actions'], list):
return False return False
for action in service['actions']:
if ('iid' not in action) or ('type' not in action) or (
'description' not in action) or (
'in' not in action) or ('out' not in action):
return False
if not isinstance(action['iid'], int) or not isinstance(
action['type'], str) or not isinstance(
action['description'], str) or not isinstance(
action['in'], list) or not isinstance(
action['out'], list):
return False
type_strs = action['type'].split(':')
if type_strs[1] != 'miot-spec-v2':
return False
for param in action['in']:
if not isinstance(param, int):
return False
for param in action['out']:
if not isinstance(param, int):
return False
if 'events' in service:
if not isinstance(service['events'], list):
return False
for event in service['events']:
if ('iid' not in event) or ('type' not in event) or (
'description' not in event) or ('arguments'
not in event):
return False
if not isinstance(event['iid'], int) or not isinstance(
event['type'], str) or not isinstance(
event['description'], str) or not isinstance(
event['arguments'], list):
return False
type_strs = event['type'].split(':')
if type_strs[1] != 'miot-spec-v2':
return False
for param in event['arguments']:
if not isinstance(param, int):
return False
return True return True
@ -185,25 +260,22 @@ def compare_dict_structure(dict1: dict, dict2: dict) -> bool:
_LOGGER.info('invalid type') _LOGGER.info('invalid type')
return False return False
if dict1.keys() != dict2.keys(): if dict1.keys() != dict2.keys():
_LOGGER.info( _LOGGER.info('inconsistent key values, %s, %s', dict1.keys(),
'inconsistent key values, %s, %s', dict1.keys(), dict2.keys()) dict2.keys())
return False return False
for key in dict1: for key in dict1:
if isinstance(dict1[key], dict) and isinstance(dict2[key], dict): if isinstance(dict1[key], dict) and isinstance(dict2[key], dict):
if not compare_dict_structure(dict1[key], dict2[key]): if not compare_dict_structure(dict1[key], dict2[key]):
_LOGGER.info( _LOGGER.info('inconsistent key values, dict, %s', key)
'inconsistent key values, dict, %s', key)
return False return False
elif isinstance(dict1[key], list) and isinstance(dict2[key], list): elif isinstance(dict1[key], list) and isinstance(dict2[key], list):
if not all( if not all(
isinstance(i, type(j)) isinstance(i, type(j))
for i, j in zip(dict1[key], dict2[key])): for i, j in zip(dict1[key], dict2[key])):
_LOGGER.info( _LOGGER.info('inconsistent key values, list, %s', key)
'inconsistent key values, list, %s', key)
return False return False
elif not isinstance(dict1[key], type(dict2[key])): elif not isinstance(dict1[key], type(dict2[key])):
_LOGGER.info( _LOGGER.info('inconsistent key values, type, %s', key)
'inconsistent key values, type, %s', key)
return False return False
return True return True
@ -227,7 +299,7 @@ def sort_spec_filter(file_path: str):
def sort_spec_add(file_path: str): def sort_spec_add(file_path: str):
filter_data = load_yaml_file(file_path=file_path) filter_data = load_json_file(file_path=file_path)
assert isinstance(filter_data, dict), f'{file_path} format error' assert isinstance(filter_data, dict), f'{file_path} format error'
return dict(sorted(filter_data.items())) return dict(sorted(filter_data.items()))
@ -256,7 +328,7 @@ def test_spec_filter():
@pytest.mark.github @pytest.mark.github
def test_spec_add(): def test_spec_add():
data = load_yaml_file(SPEC_ADD_FILE) data = load_json_file(SPEC_ADD_FILE)
assert isinstance(data, dict) assert isinstance(data, dict)
assert data, f'load {SPEC_ADD_FILE} failed' assert data, f'load {SPEC_ADD_FILE} failed'
assert spec_add(data), f'{SPEC_ADD_FILE} format error' assert spec_add(data), f'{SPEC_ADD_FILE} format error'
@ -295,7 +367,8 @@ def test_miot_lang_integrity():
# pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel
from miot.const import INTEGRATION_LANGUAGES from miot.const import INTEGRATION_LANGUAGES
integration_lang_list: list[str] = [ integration_lang_list: list[str] = [
f'{key}.json' for key in list(INTEGRATION_LANGUAGES.keys())] f'{key}.json' for key in list(INTEGRATION_LANGUAGES.keys())
]
translations_names: set[str] = set(listdir(TRANS_RELATIVE_PATH)) translations_names: set[str] = set(listdir(TRANS_RELATIVE_PATH))
assert len(translations_names) == len(integration_lang_list) assert len(translations_names) == len(integration_lang_list)
assert translations_names == set(integration_lang_list) assert translations_names == set(integration_lang_list)
@ -311,21 +384,18 @@ def test_miot_lang_integrity():
default_dict = load_json_file( default_dict = load_json_file(
path.join(TRANS_RELATIVE_PATH, integration_lang_list[0])) path.join(TRANS_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]: for name in list(integration_lang_list)[1:]:
compare_dict = load_json_file( compare_dict = load_json_file(path.join(TRANS_RELATIVE_PATH, name))
path.join(TRANS_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict): if not compare_dict_structure(default_dict, compare_dict):
_LOGGER.info( _LOGGER.info('compare_dict_structure failed /translations, %s',
'compare_dict_structure failed /translations, %s', name) name)
assert False assert False
# Check i18n files structure # Check i18n files structure
default_dict = load_json_file( default_dict = load_json_file(
path.join(MIOT_I18N_RELATIVE_PATH, integration_lang_list[0])) path.join(MIOT_I18N_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]: for name in list(integration_lang_list)[1:]:
compare_dict = load_json_file( compare_dict = load_json_file(path.join(MIOT_I18N_RELATIVE_PATH, name))
path.join(MIOT_I18N_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict): if not compare_dict_structure(default_dict, compare_dict):
_LOGGER.info( _LOGGER.info('compare_dict_structure failed /miot/i18n, %s', name)
'compare_dict_structure failed /miot/i18n, %s', name)
assert False assert False
@ -343,12 +413,11 @@ def test_miot_data_sort():
f'{SPEC_BOOL_TRANS_FILE} not sorted, goto project root path' f'{SPEC_BOOL_TRANS_FILE} not sorted, goto project root path'
' and run the following command sorting, ', ' and run the following command sorting, ',
'pytest -s -v -m update ./test/check_rule_format.py') 'pytest -s -v -m update ./test/check_rule_format.py')
assert json.dumps( assert json.dumps(load_yaml_file(file_path=SPEC_FILTER_FILE)) == json.dumps(
load_yaml_file(file_path=SPEC_FILTER_FILE)) == json.dumps( sort_spec_filter(file_path=SPEC_FILTER_FILE)), (
sort_spec_filter(file_path=SPEC_FILTER_FILE)), ( f'{SPEC_FILTER_FILE} not sorted, goto project root path'
f'{SPEC_FILTER_FILE} not sorted, goto project root path' ' and run the following command sorting, ',
' and run the following command sorting, ', 'pytest -s -v -m update ./test/check_rule_format.py')
'pytest -s -v -m update ./test/check_rule_format.py')
@pytest.mark.update @pytest.mark.update
@ -360,7 +429,7 @@ def test_sort_spec_data():
save_yaml_file(file_path=SPEC_FILTER_FILE, data=sort_data) save_yaml_file(file_path=SPEC_FILTER_FILE, data=sort_data)
_LOGGER.info('%s formatted.', SPEC_FILTER_FILE) _LOGGER.info('%s formatted.', SPEC_FILTER_FILE)
sort_data = sort_spec_add(file_path=SPEC_ADD_FILE) sort_data = sort_spec_add(file_path=SPEC_ADD_FILE)
save_yaml_file(file_path=SPEC_ADD_FILE, data=sort_data) save_json_file(file_path=SPEC_ADD_FILE, data=sort_data)
_LOGGER.info('%s formatted.', SPEC_ADD_FILE) _LOGGER.info('%s formatted.', SPEC_ADD_FILE)
sort_data = sort_spec_modify(file_path=SPEC_MODIFY_FILE) sort_data = sort_spec_modify(file_path=SPEC_MODIFY_FILE)
save_yaml_file(file_path=SPEC_MODIFY_FILE, data=sort_data) save_yaml_file(file_path=SPEC_MODIFY_FILE, data=sort_data)