| /* |
| * 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. |
| * |
| */ |
| |
| #include "FabricSyncCommand.h" |
| #include <commands/common/RemoteDataModelLogger.h> |
| #include <commands/interactive/InteractiveCommands.h> |
| #include <device_manager/DeviceManager.h> |
| #include <setup_payload/ManualSetupPayloadGenerator.h> |
| #include <thread> |
| #include <unistd.h> |
| |
| #if defined(PW_RPC_ENABLED) |
| #include <rpc/RpcClient.h> |
| #endif |
| |
| using namespace ::chip; |
| |
| namespace admin { |
| |
| namespace { |
| |
| constexpr uint32_t kDefaultSetupPinCode = 20202021; |
| constexpr uint16_t kDefaultLocalBridgePort = 5540; |
| |
| } // namespace |
| |
| void FabricSyncAddBridgeCommand::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR err) |
| { |
| if (mBridgeNodeId != deviceId) |
| { |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(NotSpecified, "Failed to pair non-bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| else |
| { |
| ChipLogProgress(NotSpecified, "Commissioning complete for non-bridge device: NodeId: " ChipLogFormatX64, |
| ChipLogValueX64(deviceId)); |
| } |
| return; |
| } |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| DeviceManager::Instance().SetRemoteBridgeNodeId(mBridgeNodeId); |
| ChipLogProgress(NotSpecified, "Successfully paired bridge device: NodeId: " ChipLogFormatX64, |
| ChipLogValueX64(mBridgeNodeId)); |
| |
| DeviceManager::Instance().UpdateLastUsedNodeId(mBridgeNodeId); |
| DeviceManager::Instance().SubscribeRemoteFabricBridge(); |
| DeviceManager::Instance().InitCommissionerControl(); |
| |
| if (DeviceManager::Instance().IsLocalBridgeReady()) |
| { |
| // After successful commissioning of the Commissionee, initiate Reverse Commissioning |
| // via the Commissioner Control Cluster. However, we must first verify that the |
| // remote Fabric-Bridge supports Fabric Synchronization. |
| // |
| // Note: The Fabric-Admin MUST NOT send the RequestCommissioningApproval command |
| // if the remote Fabric-Bridge lacks Fabric Synchronization support. |
| DeviceLayer::SystemLayer().ScheduleLambda([]() { DeviceManager::Instance().ReadSupportedDeviceCategories(); }); |
| } |
| } |
| else |
| { |
| ChipLogError(NotSpecified, "Failed to pair bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| |
| mBridgeNodeId = kUndefinedNodeId; |
| } |
| |
| CHIP_ERROR FabricSyncAddBridgeCommand::RunCommand(NodeId remoteId) |
| { |
| if (DeviceManager::Instance().IsFabricSyncReady()) |
| { |
| // print to console |
| fprintf(stderr, "Remote Fabric Bridge has already been configured.\n"); |
| return CHIP_NO_ERROR; |
| } |
| |
| PairingManager::Instance().SetPairingDelegate(this); |
| |
| mBridgeNodeId = remoteId; |
| |
| return PairingManager::Instance().PairDevice(remoteId, mSetupPINCode, reinterpret_cast<const char *>(mRemoteAddr.data()), |
| mRemotePort); |
| } |
| |
| void FabricSyncRemoveBridgeCommand::OnDeviceRemoved(NodeId deviceId, CHIP_ERROR err) |
| { |
| if (mBridgeNodeId != deviceId) |
| { |
| ChipLogProgress(NotSpecified, "An non-bridge device: NodeId: " ChipLogFormatX64 " is removed.", ChipLogValueX64(deviceId)); |
| return; |
| } |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| DeviceManager::Instance().SetRemoteBridgeNodeId(kUndefinedNodeId); |
| ChipLogProgress(NotSpecified, "Successfully removed bridge device: NodeId: " ChipLogFormatX64, |
| ChipLogValueX64(mBridgeNodeId)); |
| } |
| else |
| { |
| ChipLogError(NotSpecified, "Failed to remove bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| |
| mBridgeNodeId = kUndefinedNodeId; |
| } |
| |
| CHIP_ERROR FabricSyncRemoveBridgeCommand::RunCommand() |
| { |
| NodeId bridgeNodeId = DeviceManager::Instance().GetRemoteBridgeNodeId(); |
| |
| if (bridgeNodeId == kUndefinedNodeId) |
| { |
| // print to console |
| fprintf(stderr, "Remote Fabric Bridge is not configured yet, nothing to remove.\n"); |
| return CHIP_NO_ERROR; |
| } |
| |
| mBridgeNodeId = bridgeNodeId; |
| |
| PairingManager::Instance().SetPairingDelegate(this); |
| |
| return PairingManager::Instance().UnpairDevice(bridgeNodeId); |
| } |
| |
| void FabricSyncAddLocalBridgeCommand::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR err) |
| { |
| if (mLocalBridgeNodeId != deviceId) |
| { |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(NotSpecified, "Failed to pair non-bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| else |
| { |
| ChipLogProgress(NotSpecified, "Commissioning complete for non-bridge device: NodeId: " ChipLogFormatX64, |
| ChipLogValueX64(deviceId)); |
| } |
| return; |
| } |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| DeviceManager::Instance().SetLocalBridgeNodeId(mLocalBridgeNodeId); |
| DeviceManager::Instance().UpdateLastUsedNodeId(mLocalBridgeNodeId); |
| ChipLogProgress(NotSpecified, "Successfully paired local bridge device: NodeId: " ChipLogFormatX64, |
| ChipLogValueX64(mLocalBridgeNodeId)); |
| } |
| else |
| { |
| ChipLogError(NotSpecified, "Failed to pair local bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| |
| mLocalBridgeNodeId = kUndefinedNodeId; |
| } |
| |
| CHIP_ERROR FabricSyncAddLocalBridgeCommand::RunCommand(NodeId deviceId) |
| { |
| if (DeviceManager::Instance().IsLocalBridgeReady()) |
| { |
| // print to console |
| fprintf(stderr, "Local Fabric Bridge has already been configured.\n"); |
| return CHIP_NO_ERROR; |
| } |
| |
| PairingManager::Instance().SetPairingDelegate(this); |
| mLocalBridgeNodeId = deviceId; |
| |
| uint16_t localBridgePort = mLocalPort.ValueOr(kDefaultLocalBridgePort); |
| uint32_t localBridgeSetupPinCode = mSetupPINCode.ValueOr(kDefaultSetupPinCode); |
| |
| return PairingManager::Instance().PairDevice(deviceId, localBridgeSetupPinCode, "::1", localBridgePort); |
| } |
| |
| void FabricSyncRemoveLocalBridgeCommand::OnDeviceRemoved(NodeId deviceId, CHIP_ERROR err) |
| { |
| if (mLocalBridgeNodeId != deviceId) |
| { |
| ChipLogProgress(NotSpecified, "A non-bridge device: NodeId: " ChipLogFormatX64 " is removed.", ChipLogValueX64(deviceId)); |
| return; |
| } |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| DeviceManager::Instance().SetLocalBridgeNodeId(kUndefinedNodeId); |
| ChipLogProgress(NotSpecified, "Successfully removed local bridge device: NodeId: " ChipLogFormatX64, |
| ChipLogValueX64(mLocalBridgeNodeId)); |
| } |
| else |
| { |
| ChipLogError(NotSpecified, "Failed to remove local bridge device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| |
| mLocalBridgeNodeId = kUndefinedNodeId; |
| } |
| |
| CHIP_ERROR FabricSyncRemoveLocalBridgeCommand::RunCommand() |
| { |
| NodeId bridgeNodeId = DeviceManager::Instance().GetLocalBridgeNodeId(); |
| |
| if (bridgeNodeId == kUndefinedNodeId) |
| { |
| // print to console |
| fprintf(stderr, "Local Fabric Bridge is not configured yet, nothing to remove.\n"); |
| return CHIP_NO_ERROR; |
| } |
| |
| mLocalBridgeNodeId = bridgeNodeId; |
| |
| PairingManager::Instance().SetPairingDelegate(this); |
| |
| return PairingManager::Instance().UnpairDevice(mLocalBridgeNodeId); |
| } |
| |
| void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR err, SetupPayload payload) |
| { |
| ChipLogProgress(NotSpecified, "FabricSyncDeviceCommand::OnCommissioningWindowOpened"); |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| char payloadBuffer[kMaxManualCodeLength + 1]; |
| MutableCharSpan manualCode(payloadBuffer); |
| CHIP_ERROR error = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualCode); |
| if (error == CHIP_NO_ERROR) |
| { |
| NodeId nodeId = DeviceManager::Instance().GetNextAvailableNodeId(); |
| |
| PairingManager::Instance().SetPairingDelegate(this); |
| mAssignedNodeId = nodeId; |
| |
| usleep(kCommissionPrepareTimeMs * 1000); |
| |
| if (PairingManager::Instance().PairDeviceWithCode(nodeId, payloadBuffer) != CHIP_NO_ERROR) |
| { |
| ChipLogError(NotSpecified, "Failed to sync device " ChipLogFormatX64, ChipLogValueX64(nodeId)); |
| } |
| } |
| else |
| { |
| ChipLogError(NotSpecified, "Unable to generate manual code for setup payload: %" CHIP_ERROR_FORMAT, error.Format()); |
| } |
| } |
| else |
| { |
| ChipLogError(NotSpecified, |
| "Failed to open synced device (0x:" ChipLogFormatX64 ") commissioning window: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| } |
| |
| void FabricSyncDeviceCommand::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR err) |
| { |
| if (mAssignedNodeId != deviceId) |
| { |
| // Ignore if the deviceId does not match the mAssignedNodeId. |
| // This scenario should not occur because no other device should be commissioned during the fabric sync process. |
| return; |
| } |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| DeviceManager::Instance().AddSyncedDevice(SyncedDevice(mAssignedNodeId, mRemoteEndpointId)); |
| } |
| else |
| { |
| ChipLogError(NotSpecified, "Failed to pair synced device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT, |
| ChipLogValueX64(deviceId), err.Format()); |
| } |
| } |
| |
| CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteEndpointId) |
| { |
| if (!DeviceManager::Instance().IsFabricSyncReady()) |
| { |
| // print to console |
| fprintf(stderr, "Remote Fabric Bridge is not configured yet.\n"); |
| return CHIP_NO_ERROR; |
| } |
| |
| PairingManager::Instance().SetOpenCommissioningWindowDelegate(this); |
| |
| DeviceManager::Instance().OpenRemoteDeviceCommissioningWindow(remoteEndpointId); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace admin |