diff --git a/.github/.wordlist.txt b/.github/.wordlist.txt index dc56ae93a28e1d..5c24cdec825ac1 100644 --- a/.github/.wordlist.txt +++ b/.github/.wordlist.txt @@ -1603,6 +1603,7 @@ xFFF xFFFF xfffff xFFFFFFEFFFFFFFFF +XMLPICSValidator xtensa xvzf xwayland diff --git a/src/tools/PICS-generator/PICSGenerator.py b/src/tools/PICS-generator/PICSGenerator.py index 4fd51033cc1eba..acdeb676ed8aa5 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 @@ def GenerateDevicePicsXmlFiles(clusterName, clusterPicsCode, featurePicsList, at 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 @@ def GenerateDevicePicsXmlFiles(clusterName, clusterPicsCode, featurePicsList, at # 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 @@ def cleanDirectory(pathToClean): # 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 9c20616bb5bc0a..312d790ca3109b 100644 --- a/src/tools/PICS-generator/README.md +++ b/src/tools/PICS-generator/README.md @@ -59,6 +59,13 @@ commissioning information: python3 PICSGenerator.py --pics-template --pics-output --commissioning-method ble-thread --discriminator --passcode --thread-dataset-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 --pics-output --commissioning-method on-network --discriminator --passcode +``` + In case the device uses a development PAA, the following parameter should be added. @@ -78,3 +85,20 @@ If a device has already been commissioned, the tool can be executed like this: ``` python3 PICSGenerator.py --pics-template --pics-output ``` + +# 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 +``` diff --git a/src/tools/PICS-generator/XMLPICSValidator.py b/src/tools/PICS-generator/XMLPICSValidator.py new file mode 100644 index 00000000000000..d728a61991edf0 --- /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 00000000000000..53e0248842ff64 --- /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