From 20a2fcc34242e6024e6e9f689f99b501f0ffb182 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 18 Dec 2024 17:52:04 +0100 Subject: [PATCH] Set fabric label on interview Instead of setting the label on commissioning only, set it during the interview process but only if necessary. This avoids the commissioning process to fail in a incomplete state when setting the label fails. The interview process has more resilience to recover from a failure during the commissioning since we have a retry mechanism in place. This also allows to set the label on devices that are already commissioned. Also (try to) unpair the device in case interviewing fails. This allows user to commission a second time without running into "Trying to add a NOC for a fabric that already exists" errors. --- matter_server/server/device_controller.py | 48 ++++++++++++++++++----- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/matter_server/server/device_controller.py b/matter_server/server/device_controller.py index fbe540d3..c9bf97ce 100644 --- a/matter_server/server/device_controller.py +++ b/matter_server/server/device_controller.py @@ -321,15 +321,6 @@ async def commission_with_code( LOGGER.info("Commissioned Node ID: %s vs %s", commissioned_node_id, node_id) if commissioned_node_id != node_id: raise RuntimeError("Returned Node ID must match requested Node ID") - - if self._default_fabric_label: - await self._chip_device_controller.send_command( - node_id, - 0, - Clusters.OperationalCredentials.Commands.UpdateFabricLabel( - self._default_fabric_label - ), - ) except ChipStackError as err: raise NodeCommissionFailed( f"Commission with code failed for node {node_id}." @@ -348,6 +339,12 @@ async def commission_with_code( await self._interview_node(node_id) except (NodeNotResolving, NodeInterviewFailed) as err: if retries <= 0: + try: + await self._chip_device_controller.unpair_device(node_id) + except ChipStackError as err_unpair: + LOGGER.warning( + "Removing current fabric from device failed: %s", err_unpair + ) raise err retries -= 1 LOGGER.warning("Unable to interview Node %s: %s", node_id, err) @@ -580,6 +577,39 @@ async def _interview_node(self, node_id: int) -> None: except ChipStackError as err: raise NodeInterviewFailed(f"Failed to interview node {node_id}") from err + # Set label if specified and needed + if self._default_fabric_label: + cluster = read_response.attributes[0][Clusters.OperationalCredentials] + fabrics: list[ + Clusters.OperationalCredentials.Structs.FabricDescriptorStruct + ] = cluster[Clusters.OperationalCredentials.Attributes.Fabrics] + fabric_index = cluster[ + Clusters.OperationalCredentials.Attributes.CurrentFabricIndex + ] + + local_fabric = next( + (fabric for fabric in fabrics if fabric.fabricIndex == fabric_index), + None, + ) + if local_fabric and local_fabric.label != self._default_fabric_label: + try: + LOGGER.debug( + "Setting fabric label for node %s to '%s'", + node_id, + self._default_fabric_label, + ) + await self._chip_device_controller.send_command( + node_id, + 0, + Clusters.OperationalCredentials.Commands.UpdateFabricLabel( + self._default_fabric_label + ), + ) + except ChipStackError as err: + LOGGER.warning( + "Failed to set fabric label for node %s: %s", node_id, err + ) + is_new_node = node_id not in self._nodes existing_info = self._nodes.get(node_id) node = MatterNodeData(