diff --git a/test/resources/stubs/module-definition-test.json b/test/resources/stubs/module-definition-test.json index a943a7c..decc8f4 100644 --- a/test/resources/stubs/module-definition-test.json +++ b/test/resources/stubs/module-definition-test.json @@ -829,15 +829,50 @@ "channels": { "MD-2_M-1_MI-1_CH-1": { "identifier": "MD-2_M-1_MI-1_CH-1", - "name": "Kanal A: Wohnzimmer" + "name": "Kanal A: Wohnzimmer", + "communication_object_ids": [ + "1.1.1/MD-2_M-1_MI-1_O-2-35_R-65", + "1.1.1/MD-2_M-1_MI-1_O-2-1_R-1", + "1.1.1/MD-2_M-1_MI-1_O-2-2_R-3", + "1.1.1/MD-2_M-1_MI-1_O-2-9_R-4", + "1.1.1/MD-2_M-1_MI-1_O-2-17_R-5", + "1.1.1/MD-2_M-1_MI-1_O-2-19_R-6", + "1.1.1/MD-2_M-1_MI-1_O-2-20_R-7", + "1.1.1/MD-2_M-1_MI-1_O-2-21_R-8", + "1.1.1/MD-2_M-1_MI-1_O-2-3_R-14", + "1.1.1/MD-2_M-1_MI-1_O-2-10_R-15", + "1.1.1/MD-2_M-1_MI-1_O-2-8_R-18", + "1.1.1/MD-2_M-1_MI-1_O-2-22_R-21", + "1.1.1/MD-2_M-1_MI-1_O-2-15_R-68", + "1.1.1/MD-2_M-1_MI-1_O-2-12_R-55" + ] }, "MD-2_M-2_MI-1_CH-1": { "identifier": "MD-2_M-2_MI-1_CH-1", - "name": "Kanal B: Küche" + "name": "Kanal B: Küche", + "communication_object_ids": [ + "1.1.1/MD-2_M-2_MI-1_O-2-35_R-65", + "1.1.1/MD-2_M-2_MI-1_O-2-1_R-1", + "1.1.1/MD-2_M-2_MI-1_O-2-2_R-3", + "1.1.1/MD-2_M-2_MI-1_O-2-9_R-4", + "1.1.1/MD-2_M-2_MI-1_O-2-17_R-5", + "1.1.1/MD-2_M-2_MI-1_O-2-19_R-6", + "1.1.1/MD-2_M-2_MI-1_O-2-20_R-7", + "1.1.1/MD-2_M-2_MI-1_O-2-21_R-8", + "1.1.1/MD-2_M-2_MI-1_O-2-3_R-14", + "1.1.1/MD-2_M-2_MI-1_O-2-10_R-15", + "1.1.1/MD-2_M-2_MI-1_O-2-8_R-18", + "1.1.1/MD-2_M-2_MI-1_O-2-22_R-21", + "1.1.1/MD-2_M-2_MI-1_O-2-15_R-68", + "1.1.1/MD-2_M-2_MI-1_O-2-12_R-55" + ] }, "CH-9": { "identifier": "CH-9", - "name": "Szenen" + "name": "Szenen", + "communication_object_ids": [ + "1.1.1/O-332_R-19" + ] } } }, @@ -860,19 +895,70 @@ "channels": { "MD-2_M-1_MI-1_CH-1": { "identifier": "MD-2_M-1_MI-1_CH-1", - "name": "Kanal A: Bad" + "name": "Kanal A: Bad", + "communication_object_ids": [ + "1.1.2/MD-2_M-1_MI-1_O-2-35_R-65", + "1.1.2/MD-2_M-1_MI-1_O-2-1_R-1", + "1.1.2/MD-2_M-1_MI-1_O-2-2_R-3", + "1.1.2/MD-2_M-1_MI-1_O-2-9_R-4", + "1.1.2/MD-2_M-1_MI-1_O-2-17_R-5", + "1.1.2/MD-2_M-1_MI-1_O-2-19_R-6", + "1.1.2/MD-2_M-1_MI-1_O-2-20_R-7", + "1.1.2/MD-2_M-1_MI-1_O-2-21_R-8", + "1.1.2/MD-2_M-1_MI-1_O-2-3_R-14", + "1.1.2/MD-2_M-1_MI-1_O-2-10_R-15", + "1.1.2/MD-2_M-1_MI-1_O-2-8_R-18", + "1.1.2/MD-2_M-1_MI-1_O-2-22_R-21", + "1.1.2/MD-2_M-1_MI-1_O-2-15_R-68", + "1.1.2/MD-2_M-1_MI-1_O-2-12_R-55" + ] }, "MD-2_M-5_MI-1_CH-1": { "identifier": "MD-2_M-5_MI-1_CH-1", - "name": "Kanal E: " + "name": "Kanal E: ", + "communication_object_ids": [ + "1.1.2/MD-2_M-5_MI-1_O-2-35_R-65", + "1.1.2/MD-2_M-5_MI-1_O-2-1_R-1", + "1.1.2/MD-2_M-5_MI-1_O-2-2_R-3", + "1.1.2/MD-2_M-5_MI-1_O-2-9_R-4", + "1.1.2/MD-2_M-5_MI-1_O-2-17_R-5", + "1.1.2/MD-2_M-5_MI-1_O-2-19_R-6", + "1.1.2/MD-2_M-5_MI-1_O-2-20_R-7", + "1.1.2/MD-2_M-5_MI-1_O-2-21_R-8", + "1.1.2/MD-2_M-5_MI-1_O-2-3_R-14", + "1.1.2/MD-2_M-5_MI-1_O-2-10_R-15", + "1.1.2/MD-2_M-5_MI-1_O-2-8_R-18", + "1.1.2/MD-2_M-5_MI-1_O-2-22_R-21", + "1.1.2/MD-2_M-5_MI-1_O-2-15_R-68", + "1.1.2/MD-2_M-5_MI-1_O-2-12_R-55" + ] }, "MD-2_M-6_MI-1_CH-1": { "identifier": "MD-2_M-6_MI-1_CH-1", - "name": "Kanal F: " + "name": "Kanal F: ", + "communication_object_ids": [ + "1.1.2/MD-2_M-6_MI-1_O-2-35_R-65", + "1.1.2/MD-2_M-6_MI-1_O-2-1_R-1", + "1.1.2/MD-2_M-6_MI-1_O-2-2_R-3", + "1.1.2/MD-2_M-6_MI-1_O-2-9_R-4", + "1.1.2/MD-2_M-6_MI-1_O-2-17_R-5", + "1.1.2/MD-2_M-6_MI-1_O-2-19_R-6", + "1.1.2/MD-2_M-6_MI-1_O-2-20_R-7", + "1.1.2/MD-2_M-6_MI-1_O-2-21_R-8", + "1.1.2/MD-2_M-6_MI-1_O-2-3_R-14", + "1.1.2/MD-2_M-6_MI-1_O-2-10_R-15", + "1.1.2/MD-2_M-6_MI-1_O-2-8_R-18", + "1.1.2/MD-2_M-6_MI-1_O-2-22_R-21", + "1.1.2/MD-2_M-6_MI-1_O-2-15_R-68", + "1.1.2/MD-2_M-6_MI-1_O-2-12_R-55" + ] }, "CH-9": { "identifier": "CH-9", - "name": "Szenen" + "name": "Szenen", + "communication_object_ids": [ + "1.1.2/O-332_R-19" + ] } } }, @@ -894,84 +980,7 @@ "1.1.3/MD-3_M-2_MI-2_O-2-4_R-5", "1.1.3/MD-3_M-2_MI-8_O-2-5_R-6" ], - "channels": { - "CH-0": { - "identifier": "CH-0", - "name": "ALLGEMEIN" - }, - "MD-2_M-3_MI-1_CH-1": { - "identifier": "MD-2_M-3_MI-1_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-2_CH-1": { - "identifier": "MD-2_M-3_MI-2_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-3_CH-1": { - "identifier": "MD-2_M-3_MI-3_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-4_CH-1": { - "identifier": "MD-2_M-3_MI-4_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-5_CH-1": { - "identifier": "MD-2_M-3_MI-5_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-6_CH-1": { - "identifier": "MD-2_M-3_MI-6_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-7_CH-1": { - "identifier": "MD-2_M-3_MI-7_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-8_CH-1": { - "identifier": "MD-2_M-3_MI-8_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-9_CH-1": { - "identifier": "MD-2_M-3_MI-9_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-10_CH-1": { - "identifier": "MD-2_M-3_MI-10_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-11_CH-1": { - "identifier": "MD-2_M-3_MI-11_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-12_CH-1": { - "identifier": "MD-2_M-3_MI-12_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-13_CH-1": { - "identifier": "MD-2_M-3_MI-13_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-14_CH-1": { - "identifier": "MD-2_M-3_MI-14_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-15_CH-1": { - "identifier": "MD-2_M-3_MI-15_CH-1", - "name": "GL-4, " - }, - "MD-2_M-3_MI-16_CH-1": { - "identifier": "MD-2_M-3_MI-16_CH-1", - "name": "GL-4, " - }, - "CH-17": { - "identifier": "CH-17", - "name": "EVG" - }, - "CH-84": { - "identifier": "CH-84", - "name": "Bewegungsmelder" - } - } + "channels": {} }, "1.1.4": { "name": "Z70 v2", @@ -994,11 +1003,32 @@ "channels": { "CH-1": { "identifier": "CH-1", - "name": "Allgemein" - }, - "CH-2": { - "identifier": "CH-2", - "name": "Visualisierung" + "name": "Allgemein", + "communication_object_ids": [ + "1.1.4/O-1_R-2", + "1.1.4/O-2_R-3", + "1.1.4/O-1474_R-653", + "1.1.4/O-1475_R-654", + "1.1.4/O-1473_R-652", + "1.1.4/O-7_R-8", + "1.1.4/O-8_R-688", + "1.1.4/O-1470_R-44", + "1.1.4/O-256_R-36", + "1.1.4/O-260_R-40", + "1.1.4/O-56_R-20", + "1.1.4/O-3_R-4", + "1.1.4/O-4_R-5", + "1.1.4/O-5_R-6", + "1.1.4/O-55_R-19", + "1.1.4/MD-4_M-15_MI-1_SM-1_M-1_MI-1-1-2_SM-1_O-3-1_R-2", + "1.1.4/MD-4_M-15_MI-1_SM-1_M-1_MI-1-1-2_SM-1_O-3-0_R-1", + "1.1.4/MD-4_M-15_MI-1_SM-1_M-1_MI-1-1-1_SM-1_O-3-1_R-2", + "1.1.4/MD-4_M-15_MI-1_SM-1_M-1_MI-1-1-1_SM-1_O-3-0_R-1", + "1.1.4/MD-4_M-15_MI-2_SM-1_M-1_MI-2-1-2_SM-1_O-3-1_R-2", + "1.1.4/MD-4_M-15_MI-2_SM-1_M-1_MI-2-1-2_SM-1_O-3-0_R-1", + "1.1.4/MD-4_M-15_MI-2_SM-1_M-1_MI-2-1-1_SM-1_O-3-1_R-2", + "1.1.4/MD-4_M-15_MI-2_SM-1_M-1_MI-2-1-1_SM-1_O-3-0_R-1" + ] } } } diff --git a/test/resources/stubs/xknx_test_project.json b/test/resources/stubs/xknx_test_project.json index 0328b74..949abbd 100644 --- a/test/resources/stubs/xknx_test_project.json +++ b/test/resources/stubs/xknx_test_project.json @@ -718,51 +718,123 @@ "channels": { "MD-1_M-13_MI-1_CH-80": { "identifier": "MD-1_M-13_MI-1_CH-80", - "name": "Ventilausgang 1 (...)" + "name": "Ventilausgang 1 (...)", + "communication_object_ids": [ + "1.1.7/MD-1_M-13_MI-1_O-2-1_R-2" + ] }, "MD-1_M-8_MI-1_CH-80": { "identifier": "MD-1_M-8_MI-1_CH-80", - "name": "Ventilausgang 2 (...)" + "name": "Ventilausgang 2 (...)", + "communication_object_ids": [ + "1.1.7/MD-1_M-8_MI-1_O-2-1_R-2" + ] }, "MD-1_M-1_MI-1_CH-80": { "identifier": "MD-1_M-1_MI-1_CH-80", - "name": "Ventilausgang 3 (...)" + "name": "Ventilausgang 3 (...)", + "communication_object_ids": [ + "1.1.7/MD-1_M-1_MI-1_O-2-1_R-2" + ] }, "MD-1_M-2_MI-1_CH-80": { "identifier": "MD-1_M-2_MI-1_CH-80", - "name": "Ventilausgang 4 (...)" + "name": "Ventilausgang 4 (...)", + "communication_object_ids": [ + "1.1.7/MD-1_M-2_MI-1_O-2-1_R-2" + ] }, "MD-1_M-6_MI-1_CH-80": { "identifier": "MD-1_M-6_MI-1_CH-80", - "name": "Ventilausgang 5 (...)" + "name": "Ventilausgang 5 (...)", + "communication_object_ids": [ + "1.1.7/MD-1_M-6_MI-1_O-2-1_R-2" + ] }, "MD-1_M-3_MI-1_CH-80": { "identifier": "MD-1_M-3_MI-1_CH-80", - "name": "Ventilausgang 6 (...)" + "name": "Ventilausgang 6 (...)", + "communication_object_ids": [ + "1.1.7/MD-1_M-3_MI-1_O-2-1_R-2" + ] }, "MD-2_M-20_MI-1_CH-81": { "identifier": "MD-2_M-20_MI-1_CH-81", - "name": "Raumtemperaturregler 1 (name in application)" + "name": "Raumtemperaturregler 1 (name in application)", + "communication_object_ids": [ + "1.1.7/MD-2_M-20_MI-1_O-2-2_R-3", + "1.1.7/MD-2_M-20_MI-1_O-5-1_R-9", + "1.1.7/MD-2_M-20_MI-1_O-5-2_R-10", + "1.1.7/MD-2_M-20_MI-1_O-5-0_R-17", + "1.1.7/MD-2_M-20_MI-1_O-5-10_R-18", + "1.1.7/MD-2_M-20_MI-1_O-7-1_R-45", + "1.1.7/MD-2_M-20_MI-1_O-7-0_R-21" + ] }, "MD-2_M-4_MI-1_CH-81": { "identifier": "MD-2_M-4_MI-1_CH-81", - "name": "channel name in properties" + "name": "channel name in properties", + "communication_object_ids": [ + "1.1.7/MD-2_M-4_MI-1_O-2-2_R-3", + "1.1.7/MD-2_M-4_MI-1_O-5-1_R-9", + "1.1.7/MD-2_M-4_MI-1_O-5-2_R-10", + "1.1.7/MD-2_M-4_MI-1_O-5-0_R-17", + "1.1.7/MD-2_M-4_MI-1_O-5-10_R-18", + "1.1.7/MD-2_M-4_MI-1_O-7-1_R-45", + "1.1.7/MD-2_M-4_MI-1_O-7-0_R-21" + ] }, "MD-2_M-9_MI-1_CH-81": { "identifier": "MD-2_M-9_MI-1_CH-81", - "name": "in application and properties" + "name": "in application and properties", + "communication_object_ids": [ + "1.1.7/MD-2_M-9_MI-1_O-2-2_R-3", + "1.1.7/MD-2_M-9_MI-1_O-5-1_R-9", + "1.1.7/MD-2_M-9_MI-1_O-5-2_R-10", + "1.1.7/MD-2_M-9_MI-1_O-5-0_R-17", + "1.1.7/MD-2_M-9_MI-1_O-5-10_R-18", + "1.1.7/MD-2_M-9_MI-1_O-7-1_R-45", + "1.1.7/MD-2_M-9_MI-1_O-7-0_R-21" + ] }, "MD-2_M-10_MI-1_CH-81": { "identifier": "MD-2_M-10_MI-1_CH-81", - "name": "Raumtemperaturregler 4 (...)" + "name": "Raumtemperaturregler 4 (...)", + "communication_object_ids": [ + "1.1.7/MD-2_M-10_MI-1_O-2-2_R-3", + "1.1.7/MD-2_M-10_MI-1_O-5-1_R-9", + "1.1.7/MD-2_M-10_MI-1_O-5-2_R-10", + "1.1.7/MD-2_M-10_MI-1_O-5-0_R-17", + "1.1.7/MD-2_M-10_MI-1_O-5-10_R-18", + "1.1.7/MD-2_M-10_MI-1_O-7-1_R-45", + "1.1.7/MD-2_M-10_MI-1_O-7-0_R-21" + ] }, "MD-2_M-11_MI-1_CH-81": { "identifier": "MD-2_M-11_MI-1_CH-81", - "name": "Raumtemperaturregler 5 (...)" + "name": "Raumtemperaturregler 5 (...)", + "communication_object_ids": [ + "1.1.7/MD-2_M-11_MI-1_O-2-2_R-3", + "1.1.7/MD-2_M-11_MI-1_O-5-1_R-9", + "1.1.7/MD-2_M-11_MI-1_O-5-2_R-10", + "1.1.7/MD-2_M-11_MI-1_O-5-0_R-17", + "1.1.7/MD-2_M-11_MI-1_O-5-10_R-18", + "1.1.7/MD-2_M-11_MI-1_O-7-1_R-45", + "1.1.7/MD-2_M-11_MI-1_O-7-0_R-21" + ] }, "MD-2_M-12_MI-1_CH-81": { "identifier": "MD-2_M-12_MI-1_CH-81", - "name": "Raumtemperaturregler 6 (...)" + "name": "Raumtemperaturregler 6 (...)", + "communication_object_ids": [ + "1.1.7/MD-2_M-12_MI-1_O-2-2_R-3", + "1.1.7/MD-2_M-12_MI-1_O-5-1_R-9", + "1.1.7/MD-2_M-12_MI-1_O-5-2_R-10", + "1.1.7/MD-2_M-12_MI-1_O-5-0_R-17", + "1.1.7/MD-2_M-12_MI-1_O-5-10_R-18", + "1.1.7/MD-2_M-12_MI-1_O-7-1_R-45", + "1.1.7/MD-2_M-12_MI-1_O-7-0_R-21" + ] } } } diff --git a/xknxproject/loader/project_loader.py b/xknxproject/loader/project_loader.py index 5935543..bb8c6c4 100644 --- a/xknxproject/loader/project_loader.py +++ b/xknxproject/loader/project_loader.py @@ -237,6 +237,7 @@ def _create_device( project_uid = device_element.get("Puid") product_ref = device_element.get("ProductRefId", "") + additional_addresses = [ add_addr for address_elem in device_element.findall( @@ -244,6 +245,7 @@ def _create_device( ) if (add_addr := address_elem.get("Address")) is not None ] + com_obj_inst_refs = [ com_obj_inst_ref for elem in device_element.findall( @@ -251,6 +253,7 @@ def _create_device( ) if (com_obj_inst_ref := self._create_com_object_instance(elem)) is not None ] + module_instances = [ module_instance for mi_elem in device_element.findall( @@ -258,15 +261,22 @@ def _create_device( ) if (module_instance := self._create_module_instance(mi_elem)) is not None ] - channels = [ - ChannelNode( - ref_id=channel_node_elem.get("RefId"), # type: ignore[arg-type] - name=channel_node_elem.get("Text", ""), - ) - for channel_node_elem in device_element.findall( - "{*}GroupObjectTree//{*}Nodes/{*}Node[@Type='Channel']" + + channels = [] + for channel_node_elem in device_element.findall( + "{*}GroupObjectTree//{*}Nodes/{*}Node[@Type='Channel']" + ): + if not (_gos := channel_node_elem.get("GroupObjectInstances")): + # parse only used channels + continue + channels.append( + ChannelNode( + ref_id=channel_node_elem.get("RefId"), # type: ignore[arg-type] + name=channel_node_elem.get("Text", ""), + group_object_instances=_gos.split(" "), + ) ) - ] + return DeviceInstance( identifier=device_element.get("Id", ""), address=int(address), diff --git a/xknxproject/models/knxproject.py b/xknxproject/models/knxproject.py index 1c6e3c8..9ae68ef 100644 --- a/xknxproject/models/knxproject.py +++ b/xknxproject/models/knxproject.py @@ -70,6 +70,7 @@ class Channel(TypedDict): identifier: str name: str + communication_object_ids: list[str] class Line(TypedDict): diff --git a/xknxproject/models/models.py b/xknxproject/models/models.py index f75d3fd..5b3b2bb 100644 --- a/xknxproject/models/models.py +++ b/xknxproject/models/models.py @@ -224,8 +224,11 @@ def _complete_channel_placeholders(self) -> None: class ChannelNode: """Class that represents a Node with Type Channel.""" - ref_id: str - name: str + ref_id: str # name="RefId" type="knx:RELIDREF" use="required" + name: str # name="Text" type="xs:string" use="optional" + group_object_instances: list[ + str + ] # name="GroupObjectInstances" type="knx:RELIDREFS" use="optional" @dataclass diff --git a/xknxproject/xml/parser.py b/xknxproject/xml/parser.py index 813acde..b7ac210 100644 --- a/xknxproject/xml/parser.py +++ b/xknxproject/xml/parser.py @@ -321,6 +321,18 @@ def _transform(self) -> KNXProject: ) device_com_objects.append(com_object_key) + channels = { + channel.ref_id: Channel( + identifier=channel.ref_id, + name=channel.name, + communication_object_ids=[ + f"{device.individual_address}/{go_instance_id}" + for go_instance_id in channel.group_object_instances + ], + ) + for channel in device.channels + } + devices_dict[device.individual_address] = Device( name=device.name or device.product_name, hardware_name=device.product_name, @@ -331,12 +343,7 @@ def _transform(self) -> KNXProject: application=device.application_program_ref, project_uid=device.project_uid, communication_object_ids=device_com_objects, - channels={ - channel.ref_id: Channel( - identifier=channel.ref_id, name=channel.name - ) - for channel in device.channels - }, + channels=channels, ) topology_dict: dict[str, Area] = {}