/* * * Copyright (c) 2022 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 "CastingUtils.h" #include "CommissioningCallbacks.h" using namespace chip; using namespace chip::System; using namespace chip::DeviceLayer; using namespace chip::Dnssd; // TODO: Accept these values over CLI const char kContentUrl[] = "https://www.test.com/videoid"; const char kContentDisplayStr[] = "Test video"; int gInitialContextVal = 121212; CHIP_ERROR DiscoverCommissioners() { // Send discover commissioners request ReturnErrorOnFailure(CastingServer::GetInstance()->DiscoverCommissioners()); // Give commissioners some time to respond and then ScheduleWork to initiate commissioning return DeviceLayer::SystemLayer().StartTimer( chip::System::Clock::Milliseconds32(kCommissionerDiscoveryTimeoutInMs), [](System::Layer *, void *) { chip::DeviceLayer::PlatformMgr().ScheduleWork(InitCommissioningFlow); }, nullptr); } CHIP_ERROR RequestCommissioning(int index) { chip::Optional associatedConnectableVideoPlayer; const Dnssd::CommissionNodeData * selectedCommissioner = CastingServer::GetInstance()->GetDiscoveredCommissioner(index, associatedConnectableVideoPlayer); if (selectedCommissioner == nullptr) { ChipLogError(AppServer, "No such commissioner with index %d exists", index); return CHIP_ERROR_INVALID_ARGUMENT; } PrepareForCommissioning(selectedCommissioner); return CHIP_NO_ERROR; } /** * Enters commissioning mode, opens commissioning window, logs onboarding payload. * If non-null selectedCommissioner is provided, sends user directed commissioning * request to the selectedCommissioner and advertises self as commissionable node over DNS-SD */ void PrepareForCommissioning(const Dnssd::CommissionNodeData * selectedCommissioner) { CastingServer::GetInstance()->Init(); CommissioningCallbacks commissioningCallbacks; commissioningCallbacks.commissioningComplete = HandleCommissioningCompleteCallback; CastingServer::GetInstance()->OpenBasicCommissioningWindow(commissioningCallbacks, OnConnectionSuccess, OnConnectionFailure, OnNewOrUpdatedEndpoint); // Display onboarding payload chip::DeviceLayer::ConfigurationMgr().LogDeviceConfig(); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT if (selectedCommissioner != nullptr) { // Send User Directed commissioning request // Wait 1 second to allow our commissionee DNS records to publish (needed on Mac) int32_t expiration = 1; ReturnOnFailure(DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(expiration), HandleUDCSendExpiration, (void *) selectedCommissioner)); } else { ChipLogProgress(AppServer, "To run discovery again, enter: cast discover"); } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT } void InitCommissioningFlow(intptr_t commandArg) { int commissionerCount = 0; // Display discovered commissioner TVs to ask user to select one for (int i = 0; i < CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES; i++) { chip::Optional associatedConnectableVideoPlayer; const Dnssd::CommissionNodeData * commissioner = CastingServer::GetInstance()->GetDiscoveredCommissioner(i, associatedConnectableVideoPlayer); if (commissioner != nullptr) { ChipLogProgress(AppServer, "Discovered Commissioner #%d", commissionerCount); commissionerCount++; commissioner->LogDetail(); if (associatedConnectableVideoPlayer.HasValue()) { [[maybe_unused]] TargetVideoPlayerInfo * targetVideoPlayerInfo = associatedConnectableVideoPlayer.Value(); ChipLogProgress(AppServer, "Previously connected with nodeId 0x" ChipLogFormatX64 " fabricIndex: %d", ChipLogValueX64(targetVideoPlayerInfo->GetNodeId()), targetVideoPlayerInfo->GetFabricIndex()); } } } if (commissionerCount > 0) { ChipLogProgress(AppServer, "%d commissioner(s) discovered. Select one (by number# above) to request commissioning from: ", commissionerCount); ChipLogProgress(AppServer, "Example: cast request 0"); } else { ChipLogError(AppServer, "No commissioner discovered, commissioning must be initiated manually!"); PrepareForCommissioning(); } } void LaunchURLResponseCallback(CHIP_ERROR err) { ChipLogProgress(AppServer, "LaunchURLResponseCallback called with %" CHIP_ERROR_FORMAT, err.Format()); } void OnCurrentStateReadResponseSuccess( void * context, chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType responseData) { ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess called with responseData: %d", static_cast(responseData)); switch (responseData) { case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kPlaying: ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Playing"); break; case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kPaused: ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Paused"); break; case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kNotPlaying: ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Not Playing"); break; case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kBuffering: ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Buffering"); break; default: ChipLogError(AppServer, "OnCurrentStateReadResponseSuccess Invalid CurrentState!"); break; } if (context != nullptr) { ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess context value: %d", *(static_cast(context))); } } void OnCurrentStateReadResponseFailure(void * context, CHIP_ERROR err) { ChipLogProgress(AppServer, "OnCurrentStateReadResponseFailure called with %" CHIP_ERROR_FORMAT, err.Format()); } void OnCurrentStateSubscriptionEstablished(void * context, SubscriptionId aSubscriptionId) { ChipLogProgress(AppServer, "OnCurrentStateSubscriptionEstablished called"); if (context != nullptr) { ChipLogProgress(AppServer, "OnCurrentStateSubscriptionEstablished context value: %d", *(static_cast(context))); } } void doCastingDemoActions(TargetEndpointInfo * endpoint) { if (endpoint != nullptr && endpoint->IsInitialized()) { if (endpoint->HasCluster(chip::app::Clusters::MediaPlayback::Id)) { // Subscribe to MediaPlayback::CurrentState ChipLogProgress(AppServer, "doCastingDemoActions requesting subscription on MediaPlayback:CurrentState on endpoint ID: %d", endpoint->GetEndpointId()); CHIP_ERROR err = CastingServer::GetInstance()->MediaPlayback_SubscribeToCurrentState( endpoint, static_cast(&gInitialContextVal), OnCurrentStateReadResponseSuccess, OnCurrentStateReadResponseFailure, 0, 4000, OnCurrentStateSubscriptionEstablished); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "MediaPlayback_SubscribeToCurrentState call failed!"); } } else { ChipLogProgress(AppServer, "doCastingDemoActions: Not subscribing to MediaPlayback:CurrentState on endpoint ID %d as it does not " "support the MediaPlayback cluster", endpoint->GetEndpointId()); } if (endpoint->HasCluster(chip::app::Clusters::ContentLauncher::Id)) { // Send a ContentLauncher::LaunchURL command ChipLogProgress(AppServer, "doCastingDemoActions sending ContentLauncher:LaunchURL on endpoint ID: %d", endpoint->GetEndpointId()); CHIP_ERROR err = CastingServer::GetInstance()->ContentLauncherLaunchURL(endpoint, kContentUrl, kContentDisplayStr, LaunchURLResponseCallback); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "ContentLauncherLaunchURL call failed!"); } } else { ChipLogProgress(AppServer, "doCastingDemoActions: Not sending ContentLauncher:LaunchURL on endpoint ID %d as it does not support " "the ContentLauncher cluster", endpoint->GetEndpointId()); } } } void OnConnectionSuccess(TargetVideoPlayerInfo * videoPlayer) { ChipLogProgress(AppServer, "OnConnectionSuccess with Video Player(nodeId: 0x" ChipLogFormatX64 ", fabricIndex: %d, deviceName: %s, vendorId: %d, productId: " "%d, deviceType: %d)", ChipLogValueX64(videoPlayer->GetNodeId()), videoPlayer->GetFabricIndex(), videoPlayer->GetDeviceName(), videoPlayer->GetVendorId(), videoPlayer->GetProductId(), videoPlayer->GetDeviceType()); TargetEndpointInfo * endpoints = videoPlayer->GetEndpoints(); if (endpoints != nullptr) { for (size_t i = 0; i < kMaxNumberOfEndpoints && endpoints[i].IsInitialized(); i++) { doCastingDemoActions(&endpoints[i]); // LaunchURL and Subscribe to CurrentState } } } void OnConnectionFailure(CHIP_ERROR err) { ChipLogError(AppServer, "OnConnectionFailure error: %" CHIP_ERROR_FORMAT, err.AsString()); } void OnNewOrUpdatedEndpoint(TargetEndpointInfo * endpoint) { ChipLogProgress(AppServer, "OnNewOrUpdatedEndpoint called"); doCastingDemoActions(endpoint); // LaunchURL and Subscribe to CurrentState } CHIP_ERROR ConnectToCachedVideoPlayer() { TargetVideoPlayerInfo * cachedVideoPlayers = CastingServer::GetInstance()->ReadCachedTargetVideoPlayerInfos(); if (cachedVideoPlayers != nullptr) { for (size_t i = 0; i < kMaxCachedVideoPlayers; i++) { if (cachedVideoPlayers[i].IsInitialized()) { ChipLogProgress(AppServer, "Found a Cached video player with nodeId: 0x" ChipLogFormatX64 ", fabricIndex: %d", ChipLogValueX64(cachedVideoPlayers[i].GetNodeId()), cachedVideoPlayers[i].GetFabricIndex()); if (CastingServer::GetInstance()->VerifyOrEstablishConnection( cachedVideoPlayers[i], OnConnectionSuccess, OnConnectionFailure, OnNewOrUpdatedEndpoint) == CHIP_NO_ERROR) { ChipLogProgress(AppServer, "FindOrEstablish CASESession attempted for cached video player with nodeId: 0x" ChipLogFormatX64 ", fabricIndex: %d", ChipLogValueX64(cachedVideoPlayers[i].GetNodeId()), cachedVideoPlayers[i].GetFabricIndex()); return CHIP_NO_ERROR; } } } } return CHIP_ERROR_INVALID_CASE_PARAMETER; } void HandleCommissioningCompleteCallback(CHIP_ERROR err) { ChipLogProgress(AppServer, "HandleCommissioningCompleteCallback called with %" CHIP_ERROR_FORMAT, err.Format()); } #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT void HandleUDCSendExpiration(System::Layer * aSystemLayer, void * context) { Dnssd::CommissionNodeData * selectedCommissioner = (Dnssd::CommissionNodeData *) context; // Send User Directed commissioning request ReturnOnFailure(CastingServer::GetInstance()->SendUserDirectedCommissioningRequest(selectedCommissioner)); } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT void PrintFabrics() { // set fabric to be the first in the list for (const auto & fb : chip::Server::GetInstance().GetFabricTable()) { FabricIndex fabricIndex = fb.GetFabricIndex(); ChipLogError(AppServer, "Next Fabric index=%d", fabricIndex); if (!fb.IsInitialized()) { ChipLogError(AppServer, " -- Not initialized"); continue; } [[maybe_unused]] NodeId myNodeId = fb.GetNodeId(); ChipLogProgress(NotSpecified, "---- Current Fabric nodeId=0x" ChipLogFormatX64 " fabricId=0x" ChipLogFormatX64 " fabricIndex=%d", ChipLogValueX64(myNodeId), ChipLogValueX64(fb.GetFabricId()), fabricIndex); } }