Skip to content

Commit 2d48454

Browse files
committed
Updated based on review feedback
1 parent 293ea82 commit 2d48454

File tree

4 files changed

+118
-140
lines changed

4 files changed

+118
-140
lines changed

src/tools/PICS-generator/PICSGenerator.py

+5-50
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright (c) 2023 Project CHIP Authors
2+
# Copyright (c) 2024 Project CHIP Authors
33
# All rights reserved.
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,6 +24,8 @@
2424
import chip.clusters as Clusters
2525
from rich.console import Console
2626

27+
from pics_generator_support import pics_xml_file_list_loader, map_cluster_name_to_pics_xml
28+
2729
# Add the path to python_testing folder, in order to be able to import from matter_testing_support
2830
sys.path.append(os.path.abspath(sys.path[0] + "/../../python_testing"))
2931
from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main # noqa: E402
@@ -40,52 +42,7 @@ def GenerateDevicePicsXmlFiles(clusterName, clusterPicsCode, featurePicsList, at
4042

4143
console.print(f"Handling PICS for {clusterName}")
4244

43-
# Map clusters to common XML template if needed
44-
if "ICDManagement" == clusterName:
45-
picsFileName = "ICD Management"
46-
47-
elif "OTA Software Update Provider" in clusterName or \
48-
"OTA Software Update Requestor" in clusterName:
49-
picsFileName = "OTA Software Update"
50-
51-
elif "On/Off" == clusterName:
52-
picsFileName = clusterName.replace("/", "-")
53-
54-
elif "GroupKeyManagement" == clusterName:
55-
picsFileName = "Group Communication"
56-
57-
elif "Wake on LAN" == clusterName or \
58-
"Low Power" == clusterName or \
59-
"Keypad Input" == clusterName or \
60-
"Audio Output" == clusterName or \
61-
"Media Input" == clusterName or \
62-
"Target Navigator" == clusterName or \
63-
"Content Control" == clusterName or \
64-
"Channel" == clusterName or \
65-
"Media Playback" == clusterName or \
66-
"Account Login" == clusterName or \
67-
"Application Basic" == clusterName or \
68-
"Content Launcher" == clusterName or \
69-
"Content App Observer" == clusterName or \
70-
"Application Launcher" == clusterName:
71-
72-
picsFileName = "Media Cluster"
73-
74-
elif "Operational Credentials" == clusterName:
75-
picsFileName = "Node Operational Credentials"
76-
77-
# Workaround for naming colisions with current logic
78-
elif "Thermostat" == clusterName:
79-
picsFileName = "Thermostat Cluster"
80-
81-
elif "Boolean State" == clusterName:
82-
picsFileName = "Boolean State Cluster"
83-
84-
elif "AccessControl" in clusterName:
85-
picsFileName = "Access Control Cluster"
86-
87-
else:
88-
picsFileName = clusterName
45+
picsFileName = map_cluster_name_to_pics_xml(clusterName, xmlFileList)
8946

9047
# Determine if file has already been handled and use this file
9148
for outputFolderFileName in os.listdir(outputPathStr):
@@ -435,9 +392,7 @@ def cleanDirectory(pathToClean):
435392

436393
# Load PICS XML templates
437394
print("Capture list of PICS XML templates")
438-
xmlFileList = os.listdir(xmlTemplatePathStr)
439-
for PICSXmlFile in xmlFileList:
440-
print(f"{xmlTemplatePathStr}/{PICSXmlFile}")
395+
xmlFileList = pics_xml_file_list_loader(xmlTemplatePathStr, True)
441396

442397
# Setup output path
443398
print(f"Output path: {outputPathStr}")

src/tools/PICS-generator/README.md

+21
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ commissioning information:
5959
python3 PICSGenerator.py --pics-template <pathToPicsTemplateFolder> --pics-output <outputPath> --commissioning-method ble-thread --discriminator <DESCRIMINATOR> --passcode <PASSCODE> --thread-dataset-hex <DATASET_AS_HEX>
6060
```
6161

62+
or in case the device is e.g. an example running on a Linux/macOS system, use the on-network commissioning:
63+
64+
```
65+
python3 PICSGenerator.py --pics-template <pathToPicsTemplateFolder> --pics-output <outputPath> --commissioning-method on-network --discriminator <DESCRIMINATOR> --passcode <PASSCODE>
66+
```
67+
6268
In case the device uses a development PAA, the following parameter should be
6369
added.
6470

@@ -78,3 +84,18 @@ If a device has already been commissioned, the tool can be executed like this:
7884
```
7985
python3 PICSGenerator.py --pics-template <pathToPicsTemplateFolder> --pics-output <outputPath>
8086
```
87+
88+
# Updates for future releases
89+
90+
Given each new release adds PICS files, to ensure the tool is able to map the cluster names to the PICS XML files,
91+
the XMLPICSValidator script can be used to validate the mapping and will inform in case a cluster can not
92+
be mapped to a PICS XML file.
93+
94+
The purpose of this script is mainly to make the update of this tool to future versions of Matter easier
95+
and is not intended as a script for generating the PICS.
96+
97+
To run the XMLPICSValidator, the following command can be used:
98+
99+
```
100+
python3 XMLPICSValidator.py --pics-template <pathToPicsTemplateFolder>
101+
```
+15-90
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright (c) 2023 Project CHIP Authors
2+
# Copyright (c) 2024 Project CHIP Authors
33
# All rights reserved.
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,107 +16,32 @@
1616
#
1717

1818
import argparse
19-
import os
2019
import sys
21-
22-
from rich.console import Console
20+
import os
21+
from pics_generator_support import pics_xml_file_list_loader, map_cluster_name_to_pics_xml
2322

2423
# Add the path to python_testing folder, in order to be able to import from matter_testing_support
2524
sys.path.append(os.path.abspath(sys.path[0] + "/../../python_testing"))
26-
from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main # noqa: E402
2725
from spec_parsing_support import build_xml_clusters # noqa: E402
2826

29-
console = None
30-
xml_clusters = None
3127

3228
parser = argparse.ArgumentParser()
3329
parser.add_argument('--pics-template', required=True)
3430
args, unknown = parser.parse_known_args()
3531

36-
xmlTemplatePathStr = args.pics_template
37-
if not xmlTemplatePathStr.endswith('/'):
38-
xmlTemplatePathStr += '/'
39-
40-
# Load PICS XML templates
41-
print("Capture list of PICS XML templates")
42-
xmlFileList = os.listdir(xmlTemplatePathStr)
43-
for PICSXmlFile in xmlFileList:
44-
print(f"{xmlTemplatePathStr}/{PICSXmlFile}")
45-
46-
47-
class XMLPICSValidator(MatterBaseTest):
48-
@async_test_body
49-
async def test_xml_pics_validator(self):
50-
51-
# Create console to print
52-
global console
53-
console = Console()
54-
55-
global xml_clusters
56-
xml_clusters, problems = build_xml_clusters()
57-
58-
for cluster in xml_clusters:
59-
60-
fileName = ""
61-
clusterName = xml_clusters[cluster].name
62-
63-
if "ICDManagement" == clusterName:
64-
picsFileName = "ICD Management"
65-
66-
elif "OTA Software Update Provider" in clusterName or \
67-
"OTA Software Update Requestor" in clusterName:
68-
picsFileName = "OTA Software Update"
69-
70-
elif "On/Off" == clusterName:
71-
picsFileName = clusterName.replace("/", "-")
72-
73-
elif "GroupKeyManagement" == clusterName:
74-
picsFileName = "Group Communication"
75-
76-
elif "Wake on LAN" == clusterName or \
77-
"Low Power" == clusterName or \
78-
"Keypad Input" == clusterName or \
79-
"Audio Output" == clusterName or \
80-
"Media Input" == clusterName or \
81-
"Target Navigator" == clusterName or \
82-
"Content Control" == clusterName or \
83-
"Channel" == clusterName or \
84-
"Media Playback" == clusterName or \
85-
"Account Login" == clusterName or \
86-
"Application Basic" == clusterName or \
87-
"Content Launcher" == clusterName or \
88-
"Content App Observer" == clusterName or \
89-
"Application Launcher" == clusterName:
90-
91-
picsFileName = "Media Cluster"
92-
93-
elif "Operational Credentials" == clusterName:
94-
picsFileName = "Node Operational Credentials"
95-
96-
# Workaround for naming colisions with current logic
97-
elif "Thermostat" == clusterName:
98-
picsFileName = "Thermostat Cluster"
99-
100-
elif "Boolean State" == clusterName:
101-
picsFileName = "Boolean State Cluster"
102-
103-
elif "AccessControl" in clusterName:
104-
picsFileName = "Access Control Cluster"
105-
106-
else:
107-
picsFileName = clusterName
32+
xml_template_path_str = args.pics_template
10833

109-
for file in xmlFileList:
110-
if file.lower().startswith(picsFileName.lower()):
111-
fileName = file
112-
break
34+
print("Build list of PICS XML")
35+
pics_xml_file_list = pics_xml_file_list_loader(xml_template_path_str, True)
11336

114-
if fileName:
115-
console.print(f"[blue]\"{clusterName}\" - \"{fileName}\" ✅")
116-
else:
117-
console.print(f"[red]Could not find matching file for \"{clusterName}\" ❌")
118-
continue
37+
print("Build list of spec XML")
38+
xml_clusters, problems = build_xml_clusters()
11939

40+
for cluster in xml_clusters:
41+
pics_xml_file_name = map_cluster_name_to_pics_xml(xml_clusters[cluster].name, pics_xml_file_list)
12042

121-
if __name__ == "__main__":
122-
default_matter_test_main()
43+
if pics_xml_file_name:
44+
print(f"{xml_clusters[cluster].name} - {pics_xml_file_name} ✅")
45+
else:
46+
print(
47+
f"Could not find matching PICS XML file for {xml_clusters[cluster].name} - {xml_clusters[cluster].pics} (Provisional: {xml_clusters[cluster].is_provisional}) ❌")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#
2+
# Copyright (c) 2024 Project CHIP Authors
3+
# All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
import os
19+
20+
cluster_to_pics_dict = {
21+
# Name mapping due to inconsistent naming of PICS files
22+
"ICDManagement": "ICD Management",
23+
"OTA Software Update Provider": "OTA Software Update",
24+
"OTA Software Update Requestor": "OTA Software Update",
25+
"On/Off": "On-Off",
26+
"GroupKeyManagement": "Group Communication",
27+
"Wake on LAN": "Media Cluster",
28+
"Low Power": "Media Cluster",
29+
"Keypad Input": "Media Cluster",
30+
"Audio Output": "Media Cluster",
31+
"Media Input": "Media Cluster",
32+
"Target Navigator": "Media Cluster",
33+
"Content Control": "Media Cluster",
34+
"Channel": "Media Cluster",
35+
"Media Playback": "Media Cluster",
36+
"Account Login": "Media Cluster",
37+
"Application Basic": "Media Cluster",
38+
"Content Launcher": "Media Cluster",
39+
"Content App Observer": "Media Cluster",
40+
"Application Launch": "Media Cluster",
41+
"Operational Credentials": "Node Operational Credentials",
42+
43+
# Workaround for naming colisions with current logic
44+
"Thermostat": "Thermostat Cluster",
45+
"Boolean State": "Boolean State Cluster",
46+
"AccessControl": "Access Control Cluster",
47+
}
48+
49+
50+
def pics_xml_file_list_loader(pics_xml_path: str, log_loaded_pics_files: bool) -> list:
51+
52+
pics_xml_file_list = os.listdir(pics_xml_path)
53+
54+
if log_loaded_pics_files:
55+
if not pics_xml_path.endswith('/'):
56+
pics_xml_path += '/'
57+
58+
for pics_xml_file in pics_xml_file_list:
59+
print(f"{pics_xml_path}/{pics_xml_file}")
60+
61+
return pics_xml_file_list
62+
63+
64+
def map_cluster_name_to_pics_xml(cluster_name, pics_xml_file_list) -> str:
65+
file_name = ""
66+
67+
try:
68+
pics_file_name = cluster_to_pics_dict[cluster_name]
69+
except KeyError:
70+
pics_file_name = cluster_name
71+
72+
for file in pics_xml_file_list:
73+
if file.lower().startswith(pics_file_name.lower()):
74+
file_name = file
75+
break
76+
77+
return file_name

0 commit comments

Comments
 (0)