Update PICS Generator to 1.4 (#35619)
* Updated PICS Generator to match 1.4 PICS
* Add script to validate PICS and cluster macthing
* Updated based on review feedback
* Apply suggestions from code review
Co-authored-by: C Freeman <cecille@google.com>
Co-authored-by: Andrei Litvin <andy314@gmail.com>
* Add XMLPICSValidator to wordlist
* Restyled by prettier-markdown
* Restyled by isort
---------
Co-authored-by: C Freeman <cecille@google.com>
Co-authored-by: Andrei Litvin <andy314@gmail.com>
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/.github/.wordlist.txt b/.github/.wordlist.txt
index dc56ae9..5c24cde 100644
--- a/.github/.wordlist.txt
+++ b/.github/.wordlist.txt
@@ -1603,6 +1603,7 @@
xFFFF
xfffff
xFFFFFFEFFFFFFFFF
+XMLPICSValidator
xtensa
xvzf
xwayland
diff --git a/src/tools/PICS-generator/PICSGenerator.py b/src/tools/PICS-generator/PICSGenerator.py
index 4fd5103..acdeb67 100644
--- a/src/tools/PICS-generator/PICSGenerator.py
+++ b/src/tools/PICS-generator/PICSGenerator.py
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 Project CHIP Authors
+# Copyright (c) 2023-2024 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,7 @@
import xml.etree.ElementTree as ET
import chip.clusters as Clusters
+from pics_generator_support import map_cluster_name_to_pics_xml, pics_xml_file_list_loader
from rich.console import Console
# Add the path to python_testing folder, in order to be able to import from matter_testing_support
@@ -40,41 +41,11 @@
console.print(f"Handling PICS for {clusterName}")
- # Map clusters to common XML template if needed
- if "ICDManagement" == clusterName:
- clusterName = "ICD Management"
-
- elif "OTA Software Update Provider" in clusterName or "OTA Software Update Requestor" in clusterName:
- clusterName = "OTA Software Update"
-
- elif "On/Off" == clusterName:
- clusterName = clusterName.replace("/", "-")
-
- elif "GroupKeyManagement" == clusterName:
- clusterName = "Group Communication"
-
- elif "Wake On LAN" == clusterName or "Low Power" == clusterName:
- clusterName = "Media Cluster"
-
- elif "Operational Credentials" == clusterName:
- clusterName = "Node Operational Credentials"
-
- elif "Laundry Washer Controls" == clusterName:
- clusterName = "Washer Controls"
-
- # Workaround for naming colisions with current logic
- elif "Thermostat" == clusterName:
- clusterName = "Thermostat Cluster"
-
- elif "Boolean State" == clusterName:
- clusterName = "Boolean State Cluster"
-
- if "AccessControl" in clusterName:
- clusterName = "Access Control cluster"
+ picsFileName = map_cluster_name_to_pics_xml(clusterName, xmlFileList)
# Determine if file has already been handled and use this file
for outputFolderFileName in os.listdir(outputPathStr):
- if clusterName in outputFolderFileName:
+ if picsFileName in outputFolderFileName:
xmlPath = outputPathStr
fileName = outputFolderFileName
break
@@ -82,7 +53,7 @@
# If no file is found in output folder, determine if there is a match for the cluster name in input folder
if fileName == "":
for file in xmlFileList:
- if file.lower().startswith(clusterName.lower()):
+ if file.lower().startswith(picsFileName.lower()):
fileName = file
break
else:
@@ -420,10 +391,10 @@
# Load PICS XML templates
print("Capture list of PICS XML templates")
-xmlFileList = os.listdir(xmlTemplatePathStr)
+xmlFileList = pics_xml_file_list_loader(xmlTemplatePathStr, True)
# Setup output path
-print(outputPathStr)
+print(f"Output path: {outputPathStr}")
outputPath = pathlib.Path(outputPathStr)
if not outputPath.exists():
diff --git a/src/tools/PICS-generator/README.md b/src/tools/PICS-generator/README.md
index 9c20616..312d790 100644
--- a/src/tools/PICS-generator/README.md
+++ b/src/tools/PICS-generator/README.md
@@ -59,6 +59,13 @@
python3 PICSGenerator.py --pics-template <pathToPicsTemplateFolder> --pics-output <outputPath> --commissioning-method ble-thread --discriminator <DESCRIMINATOR> --passcode <PASSCODE> --thread-dataset-hex <DATASET_AS_HEX>
```
+or in case the device is e.g. an example running on a Linux/macOS system, use
+the on-network commissioning:
+
+```
+python3 PICSGenerator.py --pics-template <pathToPicsTemplateFolder> --pics-output <outputPath> --commissioning-method on-network --discriminator <DESCRIMINATOR> --passcode <PASSCODE>
+```
+
In case the device uses a development PAA, the following parameter should be
added.
@@ -78,3 +85,20 @@
```
python3 PICSGenerator.py --pics-template <pathToPicsTemplateFolder> --pics-output <outputPath>
```
+
+# Updates for future releases
+
+Given each new release adds PICS files, to ensure the tool is able to map the
+cluster names to the PICS XML files, the XMLPICSValidator script can be used to
+validate the mapping and will inform in case a cluster can not be mapped to a
+PICS XML file.
+
+The purpose of this script is mainly to make the update of this tool to future
+versions of Matter easier and is not intended as a script for generating the
+PICS.
+
+To run the XMLPICSValidator, the following command can be used:
+
+```
+python3 XMLPICSValidator.py --pics-template <pathToPicsTemplateFolder>
+```
diff --git a/src/tools/PICS-generator/XMLPICSValidator.py b/src/tools/PICS-generator/XMLPICSValidator.py
new file mode 100644
index 0000000..d728a61
--- /dev/null
+++ b/src/tools/PICS-generator/XMLPICSValidator.py
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2024 Project CHIP Authors
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import argparse
+import os
+import sys
+
+from pics_generator_support import map_cluster_name_to_pics_xml, pics_xml_file_list_loader
+
+# Add the path to python_testing folder, in order to be able to import from matter_testing_support
+sys.path.append(os.path.abspath(sys.path[0] + "/../../python_testing"))
+from spec_parsing_support import build_xml_clusters # noqa: E402
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--pics-template', required=True)
+args, unknown = parser.parse_known_args()
+
+xml_template_path_str = args.pics_template
+
+print("Build list of PICS XML")
+pics_xml_file_list = pics_xml_file_list_loader(xml_template_path_str, True)
+
+print("Build list of spec XML")
+xml_clusters, problems = build_xml_clusters()
+
+for cluster in xml_clusters:
+ pics_xml_file_name = map_cluster_name_to_pics_xml(xml_clusters[cluster].name, pics_xml_file_list)
+
+ if pics_xml_file_name:
+ print(f"{xml_clusters[cluster].name} - {pics_xml_file_name} ✅")
+ else:
+ print(
+ f"Could not find matching PICS XML file for {xml_clusters[cluster].name} - {xml_clusters[cluster].pics} (Provisional: {xml_clusters[cluster].is_provisional}) ❌")
diff --git a/src/tools/PICS-generator/pics_generator_support.py b/src/tools/PICS-generator/pics_generator_support.py
new file mode 100644
index 0000000..53e0248
--- /dev/null
+++ b/src/tools/PICS-generator/pics_generator_support.py
@@ -0,0 +1,74 @@
+#
+# Copyright (c) 2024 Project CHIP Authors
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+
+cluster_to_pics_dict = {
+ # Name mapping due to inconsistent naming of PICS files
+ "ICDManagement": "ICD Management",
+ "OTA Software Update Provider": "OTA Software Update",
+ "OTA Software Update Requestor": "OTA Software Update",
+ "On/Off": "On-Off",
+ "GroupKeyManagement": "Group Communication",
+ "Wake on LAN": "Media Cluster",
+ "Low Power": "Media Cluster",
+ "Keypad Input": "Media Cluster",
+ "Audio Output": "Media Cluster",
+ "Media Input": "Media Cluster",
+ "Target Navigator": "Media Cluster",
+ "Content Control": "Media Cluster",
+ "Channel": "Media Cluster",
+ "Media Playback": "Media Cluster",
+ "Account Login": "Media Cluster",
+ "Application Basic": "Media Cluster",
+ "Content Launcher": "Media Cluster",
+ "Content App Observer": "Media Cluster",
+ "Application Launch": "Media Cluster",
+ "Operational Credentials": "Node Operational Credentials",
+
+ # Workaround for naming colisions with current logic
+ "Thermostat": "Thermostat Cluster",
+ "Boolean State": "Boolean State Cluster",
+ "AccessControl": "Access Control Cluster",
+}
+
+
+def pics_xml_file_list_loader(pics_xml_path: str, log_loaded_pics_files: bool) -> list:
+
+ pics_xml_file_list = os.listdir(pics_xml_path)
+
+ if log_loaded_pics_files:
+ if not pics_xml_path.endswith('/'):
+ pics_xml_path += '/'
+
+ for pics_xml_file in pics_xml_file_list:
+ print(f"{pics_xml_path}/{pics_xml_file}")
+
+ return pics_xml_file_list
+
+
+def map_cluster_name_to_pics_xml(cluster_name, pics_xml_file_list) -> str:
+ file_name = ""
+
+ pics_file_name = cluster_to_pics_dict.get(cluster_name, cluster_name)
+
+ for file in pics_xml_file_list:
+ if file.lower().startswith(pics_file_name.lower()):
+ file_name = file
+ break
+
+ return file_name