| /* |
| * |
| * Copyright (c) 2023-2024 Project CHIP Authors |
| * |
| * 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 "simple-app-helper.h" |
| |
| #include "../tv-casting-common/core/ConnectionCallbacks.h" |
| #include "clusters/Clusters.h" |
| |
| #include "app/clusters/bindings/BindingManager.h" |
| #include <inttypes.h> |
| #include <lib/core/CHIPCore.h> |
| #include <lib/shell/Commands.h> |
| #include <lib/shell/Engine.h> |
| #include <lib/shell/commands/Help.h> |
| #include <lib/support/CHIPArgParser.hpp> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <platform/CHIPDeviceLayer.h> |
| #include <platform/TestOnlyCommissionableDataProvider.h> |
| |
| // VendorId of the Endpoint on the CastingPlayer that the CastingApp desires to interact with after connection |
| const uint16_t kDesiredEndpointVendorId = 65521; |
| // EndpointId of the Endpoint on the CastingPlayer that the CastingApp desires to interact with after connection using the |
| // Commissioner-Generated passcode commissioning flow |
| const uint8_t kDesiredEndpointId = 1; |
| // Indicates that the Commissioner-Generated passcode commissioning flow is in progress. |
| bool gCommissionerGeneratedPasscodeFlowRunning = false; |
| |
| DiscoveryDelegateImpl * DiscoveryDelegateImpl::_discoveryDelegateImpl = nullptr; |
| bool gAwaitingCommissionerPasscodeInput = false; |
| LinuxCommissionableDataProvider gSimpleAppCommissionableDataProvider; |
| std::shared_ptr<matter::casting::core::CastingPlayer> targetCastingPlayer; |
| |
| DiscoveryDelegateImpl * DiscoveryDelegateImpl::GetInstance() |
| { |
| if (_discoveryDelegateImpl == nullptr) |
| { |
| _discoveryDelegateImpl = new DiscoveryDelegateImpl(); |
| } |
| return _discoveryDelegateImpl; |
| } |
| |
| void DiscoveryDelegateImpl::HandleOnAdded(matter::casting::memory::Strong<matter::casting::core::CastingPlayer> player) |
| { |
| ChipLogProgress(AppServer, "DiscoveryDelegateImpl::HandleOnAdded()"); |
| if (commissionersCount == 0) |
| { |
| ChipLogProgress(AppServer, "---- Awaiting user input ----"); |
| ChipLogProgress(AppServer, "Select a discovered Casting Player (start index = 0) to request commissioning."); |
| ChipLogProgress( |
| AppServer, |
| "Include the commissioner-generated-passcode flag to attempt the Commissioner-Generated passcode commissioning flow."); |
| ChipLogProgress(AppServer, "Example 1 Commissionee Passcode: cast request 0"); |
| ChipLogProgress(AppServer, "Example 2 Commissioner Passcode: cast request 0 commissioner-generated-passcode"); |
| ChipLogProgress(AppServer, "---- Awaiting user input ----"); |
| } |
| ChipLogProgress(AppServer, "Discovered CastingPlayer #%d", commissionersCount); |
| ++commissionersCount; |
| player->LogDetail(); |
| } |
| |
| void DiscoveryDelegateImpl::HandleOnUpdated(matter::casting::memory::Strong<matter::casting::core::CastingPlayer> player) |
| { |
| ChipLogProgress(AppServer, "DiscoveryDelegateImpl::HandleOnUpdated() Updated CastingPlayer with ID: %s", player->GetId()); |
| } |
| |
| void InvokeContentLauncherLaunchURL(matter::casting::memory::Strong<matter::casting::core::Endpoint> endpoint) |
| { |
| // get contentLauncherCluster from the endpoint |
| matter::casting::memory::Strong<matter::casting::clusters::content_launcher::ContentLauncherCluster> contentLauncherCluster = |
| endpoint->GetCluster<matter::casting::clusters::content_launcher::ContentLauncherCluster>(); |
| VerifyOrReturn(contentLauncherCluster != nullptr); |
| |
| // get the launchURLCommand from the contentLauncherCluster |
| matter::casting::core::Command<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type> * launchURLCommand = |
| static_cast<matter::casting::core::Command<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type> *>( |
| contentLauncherCluster->GetCommand(chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Id)); |
| VerifyOrReturn(launchURLCommand != nullptr, ChipLogError(AppServer, "LaunchURL command not found on ContentLauncherCluster")); |
| |
| // create the LaunchURL request |
| chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type request; |
| request.contentURL = chip::CharSpan::fromCharString(kContentURL); |
| request.displayString = chip::Optional<chip::CharSpan>(chip::CharSpan::fromCharString(kContentDisplayStr)); |
| request.brandingInformation = |
| chip::MakeOptional(chip::app::Clusters::ContentLauncher::Structs::BrandingInformationStruct::Type()); |
| |
| // call Invoke on launchURLCommand while passing in success/failure callbacks |
| launchURLCommand->Invoke( |
| request, nullptr, |
| [](void * context, const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType & response) { |
| ChipLogProgress(AppServer, "LaunchURL Success with response.data: %.*s", static_cast<int>(response.data.Value().size()), |
| response.data.Value().data()); |
| }, |
| [](void * context, CHIP_ERROR error) { |
| ChipLogError(AppServer, "LaunchURL Failure with err %" CHIP_ERROR_FORMAT, error.Format()); |
| }, |
| chip::MakeOptional(kTimedInvokeCommandTimeoutMs)); // time out after kTimedInvokeCommandTimeoutMs |
| } |
| |
| void ReadApplicationBasicVendorID(matter::casting::memory::Strong<matter::casting::core::Endpoint> endpoint) |
| { |
| // get applicationBasicCluster from the endpoint |
| matter::casting::memory::Strong<matter::casting::clusters::application_basic::ApplicationBasicCluster> applicationBasicCluster = |
| endpoint->GetCluster<matter::casting::clusters::application_basic::ApplicationBasicCluster>(); |
| VerifyOrReturn(applicationBasicCluster != nullptr); |
| |
| // get the vendorIDAttribute from the applicationBasicCluster |
| matter::casting::core::Attribute<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo> * vendorIDAttribute = |
| static_cast<matter::casting::core::Attribute<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo> *>( |
| applicationBasicCluster->GetAttribute(chip::app::Clusters::ApplicationBasic::Attributes::VendorID::Id)); |
| VerifyOrReturn(vendorIDAttribute != nullptr, |
| ChipLogError(AppServer, "VendorID attribute not found on ApplicationBasicCluster")); |
| |
| // call Read on vendorIDAttribute while passing in success/failure callbacks |
| vendorIDAttribute->Read( |
| nullptr, |
| [](void * context, |
| chip::Optional<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType> before, |
| chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType after) { |
| if (before.HasValue()) |
| { |
| ChipLogProgress(AppServer, "Read VendorID value: %d [Before reading value: %d]", after, before.Value()); |
| } |
| else |
| { |
| ChipLogProgress(AppServer, "Read VendorID value: %d", after); |
| } |
| }, |
| [](void * context, CHIP_ERROR error) { |
| ChipLogError(AppServer, "VendorID Read failure with err %" CHIP_ERROR_FORMAT, error.Format()); |
| }); |
| } |
| |
| void SubscribeToMediaPlaybackCurrentState(matter::casting::memory::Strong<matter::casting::core::Endpoint> endpoint) |
| { |
| // get mediaPlaybackCluster from the endpoint |
| matter::casting::memory::Strong<matter::casting::clusters::media_playback::MediaPlaybackCluster> mediaPlaybackCluster = |
| endpoint->GetCluster<matter::casting::clusters::media_playback::MediaPlaybackCluster>(); |
| VerifyOrReturn(mediaPlaybackCluster != nullptr); |
| |
| // get the currentStateAttribute from the applicationBasicCluster |
| matter::casting::core::Attribute<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo> * |
| currentStateAttribute = |
| static_cast<matter::casting::core::Attribute<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo> *>( |
| mediaPlaybackCluster->GetAttribute(chip::app::Clusters::MediaPlayback::Attributes::CurrentState::Id)); |
| VerifyOrReturn(currentStateAttribute != nullptr, |
| ChipLogError(AppServer, "CurrentState attribute not found on MediaPlaybackCluster")); |
| |
| // call Subscribe on currentStateAttribute while passing in success/failure callbacks |
| currentStateAttribute->Subscribe( |
| nullptr, |
| [](void * context, |
| chip::Optional<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType> before, |
| chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType after) { |
| if (before.HasValue()) |
| { |
| ChipLogProgress(AppServer, "Read CurrentState value: %d [Before reading value: %d]", static_cast<int>(after), |
| static_cast<int>(before.Value())); |
| } |
| else |
| { |
| ChipLogProgress(AppServer, "Read CurrentState value: %d", static_cast<int>(after)); |
| } |
| }, |
| [](void * context, CHIP_ERROR error) { |
| ChipLogError(AppServer, "CurrentState Read failure with err %" CHIP_ERROR_FORMAT, error.Format()); |
| }, |
| kMinIntervalFloorSeconds, kMaxIntervalCeilingSeconds); |
| } |
| |
| CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & provider, LinuxDeviceOptions & options) |
| { |
| ChipLogProgress(Discovery, "InitCommissionableDataProvider()"); |
| chip::Optional<uint32_t> setupPasscode; |
| |
| if (options.payload.setUpPINCode != 0) |
| { |
| setupPasscode.SetValue(options.payload.setUpPINCode); |
| ChipLogProgress(Discovery, "InitCommissionableDataProvider() using setupPasscode: %d", setupPasscode.Value()); |
| } |
| else if (!options.spake2pVerifier.HasValue()) |
| { |
| uint32_t defaultTestPasscode = 0; |
| chip::DeviceLayer::TestOnlyCommissionableDataProvider TestOnlyCommissionableDataProvider; |
| VerifyOrDie(TestOnlyCommissionableDataProvider.GetSetupPasscode(defaultTestPasscode) == CHIP_NO_ERROR); |
| |
| ChipLogError(Support, |
| "InitCommissionableDataProvider() *** WARNING: Using temporary passcode %u due to no neither --passcode or " |
| "--spake2p-verifier-base64 " |
| "given on command line. This is temporary and will be deprecated. Please update your scripts " |
| "to explicitly configure onboarding credentials. ***", |
| static_cast<unsigned>(defaultTestPasscode)); |
| setupPasscode.SetValue(defaultTestPasscode); |
| options.payload.setUpPINCode = defaultTestPasscode; |
| } |
| else |
| { |
| ChipLogError(Support, |
| "InitCommissionableDataProvider() *** WARNING: Passcode is 0, so will be ignored, and verifier will take " |
| "over. Onboarding payload printed for debug will be invalid, but if the onboarding payload had been given " |
| "properly to the commissioner later, PASE will succeed. ***"); |
| } |
| |
| // Default to the minimum PBKDF iterations (1,000) for this example implementation. For TV devices and TV casting app production |
| // implementations, you should use a higher number of PBKDF iterations to enhance security. The default minimum iterations are |
| // not sufficient against brute-force and rainbow table attacks. Increasing the number of iterations will increase the |
| // computational time required to derive the key. This can slow down the authentication process, especially on devices with |
| // limited processing power like a Raspberry Pi 4. For a production implementation, you should measure the actual performance on |
| // the target device. |
| uint32_t spake2pIterationCount = |
| chip::Crypto::kSpake2p_Min_PBKDF_Iterations; // 1,000 - Hypothetical key derivation time: ~20 milliseconds (ms). |
| // uint32_t spake2pIterationCount = chip::Crypto::kSpake2p_Max_PBKDF_Iterations; // 100,000 - Hypothetical key derivation time: |
| // ~2 seconds. |
| if (options.spake2pIterations == 1000) |
| { |
| spake2pIterationCount = options.spake2pIterations; |
| ChipLogError(Support, |
| "InitCommissionableDataProvider() *** WARNING: PASE PBKDF iterations provided are the minimum allowable: %u. " |
| "Increase for production use to enhance security. ***", |
| static_cast<unsigned>(spake2pIterationCount)); |
| } |
| else if ((options.spake2pIterations > 1000)) |
| { |
| spake2pIterationCount = options.spake2pIterations; |
| ChipLogProgress(Support, "InitCommissionableDataProvider() PASE PBKDF iterations set to: %u.", |
| static_cast<unsigned>(spake2pIterationCount)); |
| } |
| else |
| { |
| ChipLogError(Support, |
| "InitCommissionableDataProvider() *** WARNING: PASE PBKDF iterations set to the minimum allowable: %u. " |
| "Increase for production use to enhance security. ***", |
| static_cast<unsigned>(spake2pIterationCount)); |
| } |
| |
| return provider.Init(options.spake2pVerifier, options.spake2pSalt, spake2pIterationCount, setupPasscode, |
| options.payload.discriminator.GetLongValue()); |
| } |
| |
| void LogEndpointsDetails(const std::vector<matter::casting::memory::Strong<matter::casting::core::Endpoint>> & endpoints) |
| { |
| ChipLogProgress(AppServer, "simple-app-helper.cpp::LogEndpointsDetails() Number of Endpoints: %d", |
| static_cast<int>(endpoints.size())); |
| for (const auto & endpoint : endpoints) |
| { |
| endpoint->LogDetail(); |
| } |
| } |
| |
| void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer) |
| { |
| ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler()"); |
| |
| // For a connection failure, called back with an error and nullptr. |
| VerifyOrReturn( |
| err == CHIP_NO_ERROR, |
| ChipLogError( |
| AppServer, |
| "simple-app-helper.cpp::ConnectionHandler(): Failed to connect to CastingPlayer (ID: %s) with err %" CHIP_ERROR_FORMAT, |
| targetCastingPlayer->GetId(), err.Format())); |
| |
| if (gCommissionerGeneratedPasscodeFlowRunning) |
| { |
| ChipLogProgress(AppServer, |
| "simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s) using " |
| "Commissioner-Generated passcode", |
| castingPlayer->GetId()); |
| ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Desired Endpoint ID for demo interactions: 1"); |
| } |
| else |
| { |
| ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Successfully connected to CastingPlayer (ID: %s)", |
| castingPlayer->GetId()); |
| ChipLogProgress(AppServer, |
| "simple-app-helper.cpp::ConnectionHandler(): Desired Endpoint Vendor ID for demo interactions: %d", |
| kDesiredEndpointVendorId); |
| } |
| |
| ChipLogProgress(AppServer, "simple-app-helper.cpp::ConnectionHandler(): Getting endpoints avaiable for demo interactions"); |
| std::vector<matter::casting::memory::Strong<matter::casting::core::Endpoint>> endpoints = castingPlayer->GetEndpoints(); |
| LogEndpointsDetails(endpoints); |
| |
| // Find the desired Endpoint and auto-trigger some Matter Casting demo interactions |
| auto it = std::find_if(endpoints.begin(), endpoints.end(), |
| [](const matter::casting::memory::Strong<matter::casting::core::Endpoint> & endpoint) { |
| if (gCommissionerGeneratedPasscodeFlowRunning) |
| { |
| // For the example Commissioner-Generated passcode commissioning flow, run demo interactions with |
| // the Endpoint with ID 1. For this flow, we commissioned with the Target Content Application |
| // with Vendor ID 1111. Since this target content application does not report its Endpoint's |
| // Vendor IDs, we find the desired endpoint based on the Endpoint ID. See |
| // connectedhomeip/examples/tv-app/tv-common/include/AppTv.h. |
| return endpoint->GetId() == kDesiredEndpointId; |
| } |
| return endpoint->GetVendorId() == kDesiredEndpointVendorId; |
| }); |
| if (it != endpoints.end()) |
| { |
| // The desired endpoint is endpoints[index] |
| unsigned index = (unsigned int) std::distance(endpoints.begin(), it); |
| |
| ChipLogProgress( |
| AppServer, |
| "simple-app-helper.cpp::ConnectionHandler(): Triggering demo interactions with CastingPlayer (ID: %s). Endpoint ID: %d", |
| castingPlayer->GetId(), endpoints[index]->GetId()); |
| |
| // demonstrate invoking a command |
| InvokeContentLauncherLaunchURL(endpoints[index]); |
| |
| // demonstrate reading an attribute |
| ReadApplicationBasicVendorID(endpoints[index]); |
| |
| // demonstrate subscribing to an attribute |
| SubscribeToMediaPlaybackCurrentState(endpoints[index]); |
| } |
| else |
| { |
| ChipLogError( |
| AppServer, |
| "simple-app-helper.cpp::ConnectionHandler():Desired Endpoint Vendor ID not found on the CastingPlayer (ID: %s)", |
| castingPlayer->GetId()); |
| } |
| } |
| |
| void CommissionerDeclarationCallback(const chip::Transport::PeerAddress & source, |
| chip::Protocols::UserDirectedCommissioning::CommissionerDeclaration cd) |
| { |
| ChipLogProgress(AppServer, |
| "simple-app-helper.cpp::CommissionerDeclarationCallback() called with CommissionerDeclaration message:"); |
| cd.DebugLog(); |
| if (cd.GetCommissionerPasscode()) |
| { |
| ChipLogProgress(AppServer, "---- Awaiting user input ----"); |
| ChipLogProgress(AppServer, "Input the Commissioner-Generated passcode displayed on the CastingPlayer UX."); |
| ChipLogProgress(AppServer, "Input 12345678 to use the default passcode."); |
| ChipLogProgress(AppServer, "Example: cast setcommissionerpasscode 12345678"); |
| ChipLogProgress(AppServer, "---- Awaiting user input ----"); |
| gAwaitingCommissionerPasscodeInput = true; |
| } |
| } |
| |
| #if defined(ENABLE_CHIP_SHELL) |
| void RegisterCommands() |
| { |
| static const chip::Shell::shell_command_t sDeviceComand = { &CommandHandler, "cast", |
| "Casting commands. Usage: cast [command_name]" }; |
| |
| // Register the root `device` command with the top-level shell. |
| chip::Shell::Engine::Root().RegisterCommands(&sDeviceComand, 1); |
| } |
| |
| CHIP_ERROR CommandHandler(int argc, char ** argv) |
| { |
| if (argc == 0 || strcmp(argv[0], "help") == 0) |
| { |
| return PrintAllCommands(); |
| } |
| if (strcmp(argv[0], "discover") == 0) |
| { |
| ChipLogProgress(AppServer, "CommandHandler() discover"); |
| |
| return matter::casting::core::CastingPlayerDiscovery::GetInstance()->StartDiscovery(kTargetPlayerDeviceType); |
| } |
| if (strcmp(argv[0], "stop-discovery") == 0) |
| { |
| ChipLogProgress(AppServer, "CommandHandler() stop-discovery"); |
| return matter::casting::core::CastingPlayerDiscovery::GetInstance()->StopDiscovery(); |
| } |
| if (strcmp(argv[0], "request") == 0) |
| { |
| ChipLogProgress(AppServer, "CommandHandler() request"); |
| if (argc < 2) |
| { |
| return PrintAllCommands(); |
| } |
| char * eptr; |
| unsigned long index = static_cast<unsigned long>(strtol(argv[1], &eptr, 10)); |
| std::vector<matter::casting::memory::Strong<matter::casting::core::CastingPlayer>> castingPlayers = |
| matter::casting::core::CastingPlayerDiscovery::GetInstance()->GetCastingPlayers(); |
| VerifyOrReturnValue(index < castingPlayers.size(), CHIP_ERROR_INVALID_ARGUMENT, |
| ChipLogError(AppServer, "Invalid casting player index provided: %lu", index)); |
| targetCastingPlayer = castingPlayers.at(index); |
| |
| gCommissionerGeneratedPasscodeFlowRunning = false; |
| |
| // Specify the TargetApp that the client wants to interact with after commissioning. If this value is passed in, |
| // VerifyOrEstablishConnection() will force UDC, in case the desired TargetApp is not found in the on-device |
| // CastingStore |
| matter::casting::core::IdentificationDeclarationOptions idOptions; |
| chip::Protocols::UserDirectedCommissioning::TargetAppInfo targetAppInfo; |
| targetAppInfo.vendorId = kDesiredEndpointVendorId; |
| |
| if (argc == 3) |
| { |
| |
| if (strcmp(argv[2], "commissioner-generated-passcode") == 0) |
| { |
| // Attempt Commissioner-Generated Passcode (commissioner-generated-passcode) commissioning flow only if the |
| // CastingPlayer indicates support for it. |
| if (targetCastingPlayer->GetSupportsCommissionerGeneratedPasscode()) |
| { |
| ChipLogProgress(AppServer, |
| "CommandHandler() request %lu commissioner-generated-passcode. Attempting the " |
| "Commissioner-Generated Passcode commissioning flow", |
| index); |
| idOptions.mCommissionerPasscode = true; |
| |
| // For the example Commissioner-Generated passcode commissioning flow, override the default Target Content |
| // Application Vendor ID, which is configured on the tv-app. This Target Content Application Vendor ID (1111), |
| // does not implement the AccountLogin cluster, which would otherwise auto commission using the |
| // Commissionee-Generated passcode upon recieving the IdentificationDeclaration Message. See |
| // connectedhomeip/examples/tv-app/tv-common/include/AppTv.h. |
| targetAppInfo.vendorId = 1111; |
| gCommissionerGeneratedPasscodeFlowRunning = true; |
| } |
| else |
| { |
| ChipLogError(AppServer, |
| "CommandHandler() request %lu commissioner-generated-passcode. Selected CastingPLayer does not " |
| "support the Commissioner-Generated Passcode commissioning flow", |
| index); |
| } |
| } |
| } |
| |
| CHIP_ERROR result = idOptions.addTargetAppInfo(targetAppInfo); |
| if (result != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "CommandHandler() request, failed to add targetAppInfo: %" CHIP_ERROR_FORMAT, result.Format()); |
| } |
| |
| matter::casting::core::ConnectionCallbacks connectionCallbacks; |
| connectionCallbacks.mOnConnectionComplete = ConnectionHandler; |
| // Provide an handler (Optional) for Commissioner's CommissionerDeclaration messages. The CommissionerDeclaration messages |
| // provide information indicating the Commissioner's pre-commissioning state. |
| connectionCallbacks.mCommissionerDeclarationCallback = CommissionerDeclarationCallback; |
| |
| targetCastingPlayer->VerifyOrEstablishConnection(connectionCallbacks, matter::casting::core::kCommissioningWindowTimeoutSec, |
| idOptions); |
| return CHIP_NO_ERROR; |
| } |
| if (strcmp(argv[0], "setcommissionerpasscode") == 0) |
| { |
| ChipLogProgress(AppServer, "CommandHandler() setcommissionerpasscode"); |
| if (argc < 2) |
| { |
| return PrintAllCommands(); |
| } |
| char * eptr; |
| uint32_t userEnteredPasscode = (uint32_t) strtol(argv[1], &eptr, 10); |
| if (gAwaitingCommissionerPasscodeInput) |
| { |
| ChipLogProgress(AppServer, "CommandHandler() setcommissionerpasscode user-entered passcode: %d", userEnteredPasscode); |
| gAwaitingCommissionerPasscodeInput = false; |
| |
| // Per connectedhomeip/examples/platform/linux/LinuxCommissionableDataProvider.h: We don't support overriding the |
| // passcode post-init (it is deprecated!). Therefore we need to initiate a new provider with the user-entered |
| // Commissioner-generated passcode, and then update the CastigApp's AppParameters to update the commissioning session's |
| // passcode. |
| LinuxDeviceOptions::GetInstance().payload.setUpPINCode = userEnteredPasscode; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| err = InitCommissionableDataProvider(gSimpleAppCommissionableDataProvider, LinuxDeviceOptions::GetInstance()); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, |
| "CommandHandler() setcommissionerpasscode InitCommissionableDataProvider() err %" CHIP_ERROR_FORMAT, |
| err.Format()); |
| return err; |
| } |
| // Update the CommissionableDataProvider stored in this CastingApp's AppParameters and the CommissionableDataProvider to |
| // be used for the commissioning session. |
| err = matter::casting::core::CastingApp::GetInstance()->UpdateCommissionableDataProvider( |
| &gSimpleAppCommissionableDataProvider); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, |
| "CommandHandler() setcommissionerpasscode InitCommissionableDataProvider() err %" CHIP_ERROR_FORMAT, |
| err.Format()); |
| return err; |
| } |
| |
| // Continue Connecting to the target CastingPlayer with the user entered Commissioner-generated Passcode. |
| err = targetCastingPlayer->ContinueConnecting(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, |
| "CommandHandler() setcommissionerpasscode ContinueConnecting() failed due to err %" CHIP_ERROR_FORMAT, |
| err.Format()); |
| // Since continueConnecting() failed, Attempt to cancel the connection attempt with |
| // the CastingPlayer/Commissioner by calling StopConnecting(). |
| err = targetCastingPlayer->StopConnecting(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, |
| "CommandHandler() setcommissionerpasscode, ContinueConnecting() failed and then StopConnecting " |
| "failed due to err %" CHIP_ERROR_FORMAT, |
| err.Format()); |
| } |
| return err; |
| } |
| } |
| else |
| { |
| ChipLogError( |
| AppServer, |
| "CommandHandler() setcommissionerpasscode, no Commissioner-Generated passcode input expected at this time."); |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| } |
| if (strcmp(argv[0], "stop-connecting") == 0) |
| { |
| ChipLogProgress(AppServer, "CommandHandler() stop-connecting"); |
| CHIP_ERROR err = targetCastingPlayer->StopConnecting(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "CommandHandler() stop-connecting, err %" CHIP_ERROR_FORMAT, err.Format()); |
| return err; |
| } |
| } |
| if (strcmp(argv[0], "print-bindings") == 0) |
| { |
| PrintBindings(); |
| return CHIP_NO_ERROR; |
| } |
| if (strcmp(argv[0], "print-fabrics") == 0) |
| { |
| PrintFabrics(); |
| return CHIP_NO_ERROR; |
| } |
| if (strcmp(argv[0], "delete-fabric") == 0) |
| { |
| char * eptr; |
| chip::FabricIndex fabricIndex = (chip::FabricIndex) strtol(argv[1], &eptr, 10); |
| chip::Server::GetInstance().GetFabricTable().Delete(fabricIndex); |
| return CHIP_NO_ERROR; |
| } |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| |
| CHIP_ERROR PrintAllCommands() |
| { |
| chip::Shell::streamer_t * sout = chip::Shell::streamer_get(); |
| streamer_printf(sout, " help Usage: cast <subcommand>\r\n"); |
| streamer_printf(sout, " print-bindings Usage: cast print-bindings\r\n"); |
| streamer_printf(sout, " print-fabrics Usage: cast print-fabrics\r\n"); |
| streamer_printf( |
| sout, |
| " delete-fabric <index> Delete a fabric from the casting client's fabric store. Usage: cast delete-fabric 1\r\n"); |
| streamer_printf(sout, " discover Discover Casting Players. Usage: cast discover\r\n"); |
| streamer_printf(sout, " stop-discovery Stop Discovery of Casting Players. Usage: cast stop-discovery\r\n"); |
| streamer_printf(sout, |
| " request <index> Request connecting to discovered Casting Player with " |
| "[index] using the Commissionee-Generated passcode commissioning flow. Usage: cast request 0\r\n"); |
| streamer_printf(sout, |
| " request <index> commissioner-generated-passcode Request connecting to discovered Casting Player with " |
| "[index] using the Commissioner-Generated passcode commissioning flow. Usage: cast request 0 cgp\r\n"); |
| streamer_printf(sout, |
| " setcommissionerpasscode <passcode> Set the commissioning session's passcode to the " |
| "Commissioner-Generated passcode. Used for the the Commissioner-Generated passcode commissioning flow. Usage: " |
| "cast setcommissionerpasscode 12345678\r\n"); |
| streamer_printf(sout, |
| " stop-connecting Stop connecting to Casting Player upon " |
| "Commissioner-Generated passcode commissioning flow passcode input request. Usage: cast stop-connecting\r\n"); |
| streamer_printf(sout, "\r\n"); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void PrintBindings() |
| { |
| for (const auto & binding : chip::BindingTable::GetInstance()) |
| { |
| ChipLogProgress(AppServer, |
| "Binding type=%d fab=%d nodeId=0x" ChipLogFormatX64 |
| " groupId=%d local endpoint=%d remote endpoint=%d cluster=" ChipLogFormatMEI, |
| binding.type, binding.fabricIndex, ChipLogValueX64(binding.nodeId), binding.groupId, binding.local, |
| binding.remote, ChipLogValueMEI(binding.clusterId.value_or(0))); |
| } |
| } |
| |
| void PrintFabrics() |
| { |
| // set fabric to be the first in the list |
| for (const auto & fb : chip::Server::GetInstance().GetFabricTable()) |
| { |
| chip::FabricIndex fabricIndex = fb.GetFabricIndex(); |
| ChipLogError(AppServer, "Next Fabric index=%d", fabricIndex); |
| if (!fb.IsInitialized()) |
| { |
| ChipLogError(AppServer, " -- Not initialized"); |
| continue; |
| } |
| chip::NodeId myNodeId = fb.GetNodeId(); |
| ChipLogProgress(AppServer, |
| "---- Current Fabric nodeId=0x" ChipLogFormatX64 " fabricId=0x" ChipLogFormatX64 " fabricIndex=%d", |
| ChipLogValueX64(myNodeId), ChipLogValueX64(fb.GetFabricId()), fabricIndex); |
| } |
| } |
| |
| #endif // ENABLE_CHIP_SHELL |