From 54327291c81b2b6ad54a203bd12da7cb06a41b7f Mon Sep 17 00:00:00 2001 From: chrisdecenzo <61757564+chrisdecenzo@users.noreply.github.com> Date: Thu, 2 Dec 2021 17:47:43 -0800 Subject: [PATCH] dynamic tv apps (#12125) * draft - dynamic apps * sync to TOT * centralize commissioning logic in AppMain, add all clusters to the content apps * temp fix for integration tests * cleanup and prepare for moving app platform to common code * move ContentApp and ContentAppPlatform to common code * build fix * build fix * build fix * move DYNAMIC_ENDPOINT_COUNT to device config * fix CI test cases which assume dynamic endpoints * fix CI test cases * fix CI compiles * fix infineon compile * fix infineon compile * fix restyle --- config/standalone/CHIPProjectConfig.h | 4 +- .../include/CHIPProjectAppConfig.h | 34 +++ examples/bridge-app/esp32/CMakeLists.txt | 4 +- examples/bridge-app/esp32/main/main.cpp | 10 +- examples/bridge-app/linux/BUILD.gn | 4 +- examples/bridge-app/linux/args.gni | 8 + examples/bridge-app/linux/main.cpp | 10 +- examples/platform/linux/AppMain.cpp | 118 +++++++- examples/platform/linux/AppMain.h | 21 ++ .../linux/ControllerShellCommands.cpp | 129 +-------- .../platform/linux/ControllerShellCommands.h | 4 +- examples/tv-app/linux/AppImpl.cpp | 219 +++++++++++++++ examples/tv-app/linux/AppImpl.h | 120 ++++++++ .../tv-app/linux/AppPlatformShellCommands.cpp | 202 ++++++++++++++ .../tv-app/linux/AppPlatformShellCommands.h | 33 +++ examples/tv-app/linux/BUILD.gn | 8 + .../ApplicationBasicManager.cpp | 19 +- examples/tv-app/linux/main.cpp | 24 ++ .../tv-common/include/CHIPProjectAppConfig.h | 11 +- src/app/chip_data_model.gni | 6 + src/app/util/ContentApp.cpp | 164 +++++++++++ src/app/util/ContentApp.h | 83 ++++++ src/app/util/ContentAppPlatform.cpp | 258 ++++++++++++++++++ src/app/util/ContentAppPlatform.h | 78 ++++++ src/app/util/attribute-storage.cpp | 2 +- src/app/util/attribute-storage.h | 7 +- src/include/platform/CHIPDeviceConfig.h | 20 ++ .../UDCClientState.h | 12 + .../UserDirectedCommissioningServer.cpp | 14 +- 29 files changed, 1476 insertions(+), 150 deletions(-) create mode 100644 examples/bridge-app/bridge-common/include/CHIPProjectAppConfig.h create mode 100644 examples/tv-app/linux/AppImpl.cpp create mode 100644 examples/tv-app/linux/AppImpl.h create mode 100644 examples/tv-app/linux/AppPlatformShellCommands.cpp create mode 100644 examples/tv-app/linux/AppPlatformShellCommands.h create mode 100644 src/app/util/ContentApp.cpp create mode 100644 src/app/util/ContentApp.h create mode 100644 src/app/util/ContentAppPlatform.cpp create mode 100644 src/app/util/ContentAppPlatform.h diff --git a/config/standalone/CHIPProjectConfig.h b/config/standalone/CHIPProjectConfig.h index f9a8a80026d852..90873026e001ce 100644 --- a/config/standalone/CHIPProjectConfig.h +++ b/config/standalone/CHIPProjectConfig.h @@ -69,8 +69,8 @@ #define CHIP_CONFIG_DATA_MANAGEMENT_CLIENT_EXPERIMENTAL 1 -#ifndef DYNAMIC_ENDPOINT_COUNT -#define DYNAMIC_ENDPOINT_COUNT 4 +#ifndef CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT +#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT 4 #endif #define CONFIG_IM_BUILD_FOR_UNIT_TEST 1 diff --git a/examples/bridge-app/bridge-common/include/CHIPProjectAppConfig.h b/examples/bridge-app/bridge-common/include/CHIPProjectAppConfig.h new file mode 100644 index 00000000000000..036cbda81ad736 --- /dev/null +++ b/examples/bridge-app/bridge-common/include/CHIPProjectAppConfig.h @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2020 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. + */ + +/** + * @file + * Example project configuration file for CHIP. + * + * This is a place to put application or project-specific overrides + * to the default configuration values for general CHIP features. + * + */ + +#pragma once + +// overrides CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT in CHIPProjectConfig +#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT 16 + +// include the CHIPProjectConfig from config/standalone +#include diff --git a/examples/bridge-app/esp32/CMakeLists.txt b/examples/bridge-app/esp32/CMakeLists.txt index 0a0bc8b1131b65..dd174e3e7ec1c8 100644 --- a/examples/bridge-app/esp32/CMakeLists.txt +++ b/examples/bridge-app/esp32/CMakeLists.txt @@ -23,9 +23,9 @@ set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../common/QRCode" ) +# TODO: add CHIPProjectAppConfig.h to esp32 project(chip-bridge-app) -idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++14;-Os;-DLWIP_IPV6_SCOPES=0;-DCHIP_HAVE_CONFIG_H;-DDYNAMIC_ENDPOINT_COUNT=16" APPEND) +idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++14;-Os;-DLWIP_IPV6_SCOPES=0;-DCHIP_HAVE_CONFIG_H;-DCHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT=16" APPEND) idf_build_set_property(C_COMPILE_OPTIONS "-Os;-DLWIP_IPV6_SCOPES=0" APPEND) flashing_script() - diff --git a/examples/bridge-app/esp32/main/main.cpp b/examples/bridge-app/esp32/main/main.cpp index cf5b5fe88ba5f8..411ff15c5bec8d 100644 --- a/examples/bridge-app/esp32/main/main.cpp +++ b/examples/bridge-app/esp32/main/main.cpp @@ -50,7 +50,7 @@ static const int kFixedLabelAttributeArraySize = 254; static EndpointId gCurrentEndpointId; static EndpointId gFirstDynamicEndpointId; -static Device * gDevices[DYNAMIC_ENDPOINT_COUNT]; // number of dynamic endpoints count +static Device * gDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; // number of dynamic endpoints count // 4 Bridged devices static Device gLight1("Light 1", "Office"); @@ -116,7 +116,7 @@ DECLARE_DYNAMIC_ENDPOINT(bridgedLightEndpoint, bridgedLightClusters); CHIP_ERROR AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceType) { uint8_t index = 0; - while (index < DYNAMIC_ENDPOINT_COUNT) + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { if (NULL == gDevices[index]) { @@ -144,7 +144,7 @@ CHIP_ERROR AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t de CHIP_ERROR RemoveDeviceEndpoint(Device * dev) { - for (uint8_t index = 0; index < DYNAMIC_ENDPOINT_COUNT; index++) + for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; index++) { if (gDevices[index] == dev) { @@ -264,7 +264,7 @@ EmberAfStatus emberAfExternalAttributeReadCallback(EndpointId endpoint, ClusterI { uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); - if ((endpointIndex < DYNAMIC_ENDPOINT_COUNT) && (gDevices[endpointIndex] != NULL)) + if ((endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) && (gDevices[endpointIndex] != NULL)) { Device * dev = gDevices[endpointIndex]; @@ -291,7 +291,7 @@ EmberAfStatus emberAfExternalAttributeWriteCallback(EndpointId endpoint, Cluster { uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); - if (endpointIndex < DYNAMIC_ENDPOINT_COUNT) + if (endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { Device * dev = gDevices[endpointIndex]; diff --git a/examples/bridge-app/linux/BUILD.gn b/examples/bridge-app/linux/BUILD.gn index 572142d2357782..0f27218e003c55 100644 --- a/examples/bridge-app/linux/BUILD.gn +++ b/examples/bridge-app/linux/BUILD.gn @@ -20,6 +20,7 @@ assert(chip_build_tools) executable("chip-bridge-app") { sources = [ + "${chip_root}/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h", "Device.cpp", "Options.cpp", "include/Device.h", @@ -33,9 +34,6 @@ executable("chip-bridge-app") { cflags = [ "-Wconversion" ] - # TODO: the definition of DYNAMIC_ENDPOINT_COUNT needs find a common home! - cflags += [ "-DDYNAMIC_ENDPOINT_COUNT=16" ] - include_dirs = [ "include" ] output_dir = root_out_dir diff --git a/examples/bridge-app/linux/args.gni b/examples/bridge-app/linux/args.gni index a6463ca2c05fae..7039d20e181994 100644 --- a/examples/bridge-app/linux/args.gni +++ b/examples/bridge-app/linux/args.gni @@ -15,3 +15,11 @@ import("//build_overrides/chip.gni") import("${chip_root}/config/standalone/args.gni") + +chip_device_project_config_include = "" +chip_project_config_include = "" +chip_system_project_config_include = "" + +chip_project_config_include_dirs = + [ "${chip_root}/examples/bridge-app/bridge-common/include" ] +chip_project_config_include_dirs += [ "${chip_root}/config/standalone" ] diff --git a/examples/bridge-app/linux/main.cpp b/examples/bridge-app/linux/main.cpp index 16b2a54da783d7..3d7deecc5649d1 100644 --- a/examples/bridge-app/linux/main.cpp +++ b/examples/bridge-app/linux/main.cpp @@ -59,7 +59,7 @@ static const int kFixedLabelElementsOctetStringSize = 16; static EndpointId gCurrentEndpointId; static EndpointId gFirstDynamicEndpointId; -static Device * gDevices[DYNAMIC_ENDPOINT_COUNT]; +static Device * gDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; // ENDPOINT DEFINITIONS: // ================================================================================= @@ -184,7 +184,7 @@ DECLARE_DYNAMIC_ENDPOINT(bridgedSwitchEndpoint, bridgedSwitchClusters); int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceType) { uint8_t index = 0; - while (index < DYNAMIC_ENDPOINT_COUNT) + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { if (NULL == gDevices[index]) { @@ -219,7 +219,7 @@ int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, uint16_t deviceTyp int RemoveDeviceEndpoint(Device * dev) { uint8_t index = 0; - while (index < DYNAMIC_ENDPOINT_COUNT) + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { if (gDevices[index] == dev) { @@ -460,7 +460,7 @@ EmberAfStatus emberAfExternalAttributeReadCallback(EndpointId endpoint, ClusterI EmberAfStatus ret = EMBER_ZCL_STATUS_FAILURE; - if ((endpointIndex < DYNAMIC_ENDPOINT_COUNT) && (gDevices[endpointIndex] != NULL)) + if ((endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) && (gDevices[endpointIndex] != NULL)) { Device * dev = gDevices[endpointIndex]; @@ -496,7 +496,7 @@ EmberAfStatus emberAfExternalAttributeWriteCallback(EndpointId endpoint, Cluster // ChipLogProgress(DeviceLayer, "emberAfExternalAttributeWriteCallback: ep=%d", endpoint); - if (endpointIndex < DYNAMIC_ENDPOINT_COUNT) + if (endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { Device * dev = gDevices[endpointIndex]; diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index 44f9917c92e70d..f99cb4cf902219 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -274,6 +274,122 @@ CHIP_ERROR ShutdownCommissioner() return CHIP_NO_ERROR; } +class PairingCommand : public chip::Controller::DevicePairingDelegate, public chip::Controller::DeviceAddressUpdateDelegate +{ + /////////// DevicePairingDelegate Interface ///////// + void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override; + void OnPairingComplete(CHIP_ERROR error) override; + void OnPairingDeleted(CHIP_ERROR error) override; + void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) override; + + /////////// DeviceAddressUpdateDelegate Interface ///////// + void OnAddressUpdateComplete(NodeId nodeId, CHIP_ERROR error) override; + + CHIP_ERROR UpdateNetworkAddress(); +}; + +PairingCommand gPairingCommand; +NodeId gRemoteId = kTestDeviceNodeId; + +CHIP_ERROR PairingCommand::UpdateNetworkAddress() +{ + ChipLogProgress(AppServer, "Mdns: Updating NodeId: %" PRIx64 " ...", gRemoteId); + return gCommissioner.UpdateDevice(gRemoteId); +} + +void PairingCommand::OnAddressUpdateComplete(NodeId nodeId, CHIP_ERROR err) +{ + ChipLogProgress(AppServer, "OnAddressUpdateComplete: %s", ErrorStr(err)); +} + +void PairingCommand::OnStatusUpdate(DevicePairingDelegate::Status status) +{ + switch (status) + { + case DevicePairingDelegate::Status::SecurePairingSuccess: + ChipLogProgress(AppServer, "Secure Pairing Success"); + break; + case DevicePairingDelegate::Status::SecurePairingFailed: + ChipLogError(AppServer, "Secure Pairing Failed"); + break; + } +} + +void PairingCommand::OnPairingComplete(CHIP_ERROR err) +{ + if (err == CHIP_NO_ERROR) + { + ChipLogProgress(AppServer, "Pairing Success"); + } + else + { + ChipLogProgress(AppServer, "Pairing Failure: %s", ErrorStr(err)); + // For some devices, it may take more time to appear on the network and become discoverable + // over DNS-SD, so don't give up on failure and restart the address update. Note that this + // will not be repeated endlessly as each chip-tool command has a timeout (in the case of + // the `pairing` command it equals 120s). + // UpdateNetworkAddress(); + } +} + +void PairingCommand::OnPairingDeleted(CHIP_ERROR err) +{ + if (err == CHIP_NO_ERROR) + { + ChipLogProgress(AppServer, "Pairing Deleted Success"); + } + else + { + ChipLogProgress(AppServer, "Pairing Deleted Failure: %s", ErrorStr(err)); + } +} + +void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err) +{ + if (err == CHIP_NO_ERROR) + { + ChipLogProgress(AppServer, "Device commissioning completed with success"); + } + else + { + ChipLogProgress(AppServer, "Device commissioning Failure: %s", ErrorStr(err)); + } +} + +CHIP_ERROR CommissionerPairOnNetwork(uint32_t pincode, uint16_t disc, chip::Transport::PeerAddress address) +{ + RendezvousParameters params = RendezvousParameters().SetSetupPINCode(pincode).SetDiscriminator(disc).SetPeerAddress(address); + + gCommissioner.RegisterDeviceAddressUpdateDelegate(&gPairingCommand); + gCommissioner.RegisterPairingDelegate(&gPairingCommand); + gCommissioner.PairDevice(gRemoteId, params); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR CommissionerPairUDC(uint32_t pincode, size_t index) +{ + UDCClientState * state = gCommissioner.GetUserDirectedCommissioningServer()->GetUDCClients().GetUDCClientState(index); + if (state == nullptr) + { + ChipLogProgress(AppServer, "udc client[%ld] null \r\n", index); + return CHIP_ERROR_KEY_NOT_FOUND; + } + else + { + Transport::PeerAddress peerAddress = state->GetPeerAddress(); + + state->SetUDCClientProcessingState(UDCClientProcessingState::kCommissioningNode); + + return CommissionerPairOnNetwork(pincode, state->GetLongDiscriminator(), peerAddress); + } +} + +DeviceCommissioner * GetDeviceCommissioner() +{ + return &gCommissioner; +} + #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE void ChipLinuxAppMainLoop() @@ -307,7 +423,7 @@ void ChipLinuxAppMainLoop() #if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE InitCommissioner(); #if defined(ENABLE_CHIP_SHELL) - chip::Shell::RegisterControllerCommands(&gCommissioner); + chip::Shell::RegisterControllerCommands(); #endif // defined(ENABLE_CHIP_SHELL) #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE diff --git a/examples/platform/linux/AppMain.h b/examples/platform/linux/AppMain.h index 53a5674c161cf1..520cd3af514147 100644 --- a/examples/platform/linux/AppMain.h +++ b/examples/platform/linux/AppMain.h @@ -18,5 +18,26 @@ #pragma once +#include +#include + +#include +#include +#include +#include +#include + int ChipLinuxAppInit(int argc, char ** argv); void ChipLinuxAppMainLoop(); + +#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE + +using namespace chip::Transport; +using namespace ::chip::Controller; + +CHIP_ERROR CommissionerPairOnNetwork(uint32_t pincode, uint16_t disc, chip::Transport::PeerAddress address); +CHIP_ERROR CommissionerPairUDC(uint32_t pincode, size_t index); + +DeviceCommissioner * GetDeviceCommissioner(); + +#endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE diff --git a/examples/platform/linux/ControllerShellCommands.cpp b/examples/platform/linux/ControllerShellCommands.cpp index 5fe7db4ab303c2..ff231eb5341c50 100644 --- a/examples/platform/linux/ControllerShellCommands.cpp +++ b/examples/platform/linux/ControllerShellCommands.cpp @@ -19,6 +19,7 @@ * @file Contains shell commands for for performing discovery (eg. of commissionable nodes) related to commissioning. */ +#include #include #include #include @@ -37,8 +38,6 @@ namespace Shell { using namespace ::chip::Controller; -DeviceCommissioner * gCommissioner; - #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY static CHIP_ERROR ResetUDC(bool printHeader) { @@ -49,7 +48,7 @@ static CHIP_ERROR ResetUDC(bool printHeader) streamer_printf(sout, "udc-reset: "); } - gCommissioner->GetUserDirectedCommissioningServer()->ResetUDCClientProcessingStates(); + GetDeviceCommissioner()->GetUserDirectedCommissioningServer()->ResetUDCClientProcessingStates(); streamer_printf(sout, "done\r\n"); @@ -65,7 +64,7 @@ static CHIP_ERROR PrintUDC(bool printHeader) streamer_printf(sout, "udc-print: \r\n"); } - gCommissioner->GetUserDirectedCommissioningServer()->PrintUDCClients(); + GetDeviceCommissioner()->GetUserDirectedCommissioningServer()->PrintUDCClients(); streamer_printf(sout, "done\r\n"); @@ -82,7 +81,7 @@ static CHIP_ERROR discover(bool printHeader) } Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone, (uint64_t) 0); - gCommissioner->DiscoverCommissionableNodes(filter); + GetDeviceCommissioner()->DiscoverCommissionableNodes(filter); streamer_printf(sout, "done\r\n"); @@ -99,7 +98,7 @@ static CHIP_ERROR discover(bool printHeader, char * instance) } Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kInstanceName, instance); - gCommissioner->DiscoverCommissionableNodes(filter); + GetDeviceCommissioner()->DiscoverCommissionableNodes(filter); streamer_printf(sout, "done\r\n"); @@ -117,7 +116,7 @@ static CHIP_ERROR display(bool printHeader) for (int i = 0; i < 10; i++) { - const chip::Dnssd::DiscoveredNodeData * next = gCommissioner->GetDiscoveredDevice(i); + const chip::Dnssd::DiscoveredNodeData * next = GetDeviceCommissioner()->GetDiscoveredDevice(i); if (next == nullptr) { streamer_printf(sout, " Entry %d null\r\n", i); @@ -134,84 +133,6 @@ static CHIP_ERROR display(bool printHeader) return CHIP_NO_ERROR; } -class PairingCommand : public chip::Controller::DevicePairingDelegate, public chip::Controller::DeviceAddressUpdateDelegate -{ - /////////// DevicePairingDelegate Interface ///////// - void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override; - void OnPairingComplete(CHIP_ERROR error) override; - void OnPairingDeleted(CHIP_ERROR error) override; - void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) override; - - /////////// DeviceAddressUpdateDelegate Interface ///////// - void OnAddressUpdateComplete(NodeId nodeId, CHIP_ERROR error) override; - - CHIP_ERROR UpdateNetworkAddress(); -}; - -NodeId gRemoteId = kTestDeviceNodeId; -PairingCommand gPairingCommand; - -CHIP_ERROR PairingCommand::UpdateNetworkAddress() -{ - ChipLogProgress(chipTool, "Mdns: Updating NodeId: %" PRIx64 " ...", gRemoteId); - return gCommissioner->UpdateDevice(gRemoteId); -} - -void PairingCommand::OnAddressUpdateComplete(NodeId nodeId, CHIP_ERROR err) -{ - ChipLogProgress(chipTool, "OnAddressUpdateComplete: %s", ErrorStr(err)); -} - -void PairingCommand::OnStatusUpdate(DevicePairingDelegate::Status status) -{ - switch (status) - { - case DevicePairingDelegate::Status::SecurePairingSuccess: - ChipLogProgress(chipTool, "Secure Pairing Success"); - break; - case DevicePairingDelegate::Status::SecurePairingFailed: - ChipLogError(chipTool, "Secure Pairing Failed"); - break; - } -} - -void PairingCommand::OnPairingComplete(CHIP_ERROR err) -{ - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(chipTool, "Pairing Success"); - UpdateNetworkAddress(); - } - else - { - ChipLogProgress(chipTool, "Pairing Failure: %s", ErrorStr(err)); - } -} - -void PairingCommand::OnPairingDeleted(CHIP_ERROR err) -{ - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(chipTool, "Pairing Deleted Success"); - } - else - { - ChipLogProgress(chipTool, "Pairing Deleted Failure: %s", ErrorStr(err)); - } -} - -void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err) -{ - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(chipTool, "Device commissioning completed with success"); - } - else - { - ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err)); - } -} - static CHIP_ERROR pairOnNetwork(bool printHeader, uint32_t pincode, uint16_t disc, chip::Transport::PeerAddress address) { streamer_t * sout = streamer_get(); @@ -221,18 +142,14 @@ static CHIP_ERROR pairOnNetwork(bool printHeader, uint32_t pincode, uint16_t dis streamer_printf(sout, "onnetwork \r\n"); } - RendezvousParameters params = RendezvousParameters().SetSetupPINCode(pincode).SetDiscriminator(disc).SetPeerAddress(address); - - gCommissioner->RegisterDeviceAddressUpdateDelegate(&gPairingCommand); - gCommissioner->RegisterPairingDelegate(&gPairingCommand); - gCommissioner->PairDevice(gRemoteId, params); + CHIP_ERROR err = CommissionerPairOnNetwork(pincode, disc, address); streamer_printf(sout, "done\r\n"); - return CHIP_NO_ERROR; + return err; } -static CHIP_ERROR pairUDC(bool printHeader, uint32_t pincode, size_t index) +CHIP_ERROR pairUDC(bool printHeader, uint32_t pincode, size_t index) { streamer_t * sout = streamer_get(); @@ -241,29 +158,10 @@ static CHIP_ERROR pairUDC(bool printHeader, uint32_t pincode, size_t index) streamer_printf(sout, "udc-commission %ld %ld\r\n", pincode, index); } - UDCClientState * state = gCommissioner->GetUserDirectedCommissioningServer()->GetUDCClients().GetUDCClientState(index); - if (state == nullptr) - { - streamer_printf(sout, "udc client[%d] null \r\n", index); - } - else - { - Transport::PeerAddress peerAddress = state->GetPeerAddress(); - - state->SetUDCClientProcessingState(UDCClientProcessingState::kCommissioningNode); - - RendezvousParameters params = RendezvousParameters() - .SetSetupPINCode(pincode) - .SetDiscriminator(state->GetLongDiscriminator()) - .SetPeerAddress(peerAddress); - - gCommissioner->RegisterDeviceAddressUpdateDelegate(&gPairingCommand); - gCommissioner->RegisterPairingDelegate(&gPairingCommand); - gCommissioner->PairDevice(gRemoteId, params); + CHIP_ERROR err = CommissionerPairUDC(pincode, index); + streamer_printf(sout, "done\r\n"); - streamer_printf(sout, "done\r\n"); - } - return CHIP_NO_ERROR; + return err; } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY @@ -369,9 +267,8 @@ static CHIP_ERROR ControllerHandler(int argc, char ** argv) return error; } -void RegisterControllerCommands(chip::Controller::DeviceCommissioner * commissioner) +void RegisterControllerCommands() { - gCommissioner = commissioner; static const shell_command_t sDeviceComand = { &ControllerHandler, "controller", "Controller commands. Usage: controller [command_name]" }; diff --git a/examples/platform/linux/ControllerShellCommands.h b/examples/platform/linux/ControllerShellCommands.h index 064910ce24cf45..6ef5c2deee3005 100644 --- a/examples/platform/linux/ControllerShellCommands.h +++ b/examples/platform/linux/ControllerShellCommands.h @@ -20,12 +20,10 @@ * @brief Registers shell commands for performing discovery (eg. of commissionable nodes) related to commissioning. */ -#include - namespace chip { namespace Shell { -void RegisterControllerCommands(chip::Controller::DeviceCommissioner * commissioner); +void RegisterControllerCommands(); } // namespace Shell } // namespace chip diff --git a/examples/tv-app/linux/AppImpl.cpp b/examples/tv-app/linux/AppImpl.cpp new file mode 100644 index 00000000000000..b652642fe1af13 --- /dev/null +++ b/examples/tv-app/linux/AppImpl.cpp @@ -0,0 +1,219 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @file Contains shell commands for a commissionee (eg. end device) related to commissioning. + */ + +#include "AppImpl.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::AppPlatform; + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + +namespace chip { +namespace AppPlatform { + +// BEGIN DYNAMIC ENDPOINTS +// ================================================================================= + +static const int kNameSize = 32; + +// Current ZCL implementation of Struct uses a max-size array of 254 bytes +static const int kDescriptorAttributeArraySize = 254; +static const int kFixedLabelAttributeArraySize = 254; + +// Device types for dynamic endpoints: TODO Need a generated file from ZAP to define these! +// (taken from chip-devices.xml) +#define DEVICE_TYPE_CONTENT_APP 0x0024 + +// --------------------------------------------------------------------------- +// +// CONTENT APP ENDPOINT: contains the following clusters: +// - Descriptor +// - Application Basic + +// Declare Descriptor cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(descriptorAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(ZCL_DEVICE_LIST_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, 0), /* device list */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_SERVER_LIST_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, 0), /* server list */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_CLIENT_LIST_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, 0), /* client list */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_PARTS_LIST_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, 0), /* parts list */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Application Basic information cluster attributes +// TODO: add missing attributes once schema is updated +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(applicationBasicAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(ZCL_APPLICATION_VENDOR_NAME_ATTRIBUTE_ID, CHAR_STRING, kNameSize, 0), /* VendorName */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_APPLICATION_VENDOR_ID_ATTRIBUTE_ID, INT16U, 1, 0), /* VendorID */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_APPLICATION_NAME_ATTRIBUTE_ID, CHAR_STRING, kNameSize, 0), /* ApplicationName */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_APPLICATION_PRODUCT_ID_ATTRIBUTE_ID, INT16U, 1, 0), /* ProductID */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_APPLICATION_STATUS_ATTRIBUTE_ID, INT8U, 1, 0), /* ApplicationStatus */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Keypad Input cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(keypadInputAttrs) +DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Application Launcher cluster attributes +// TODO: add missing attributes once schema is updated +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(applicationLauncherAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(ZCL_APPLICATION_LAUNCHER_LIST_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, 0), /* catalog list */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Account Login cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(accountLoginAttrs) +DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Content Launcher cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(contentLauncherAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(ZCL_CONTENT_LAUNCHER_ACCEPTS_HEADER_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, + 0), /* accept header list */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_CONTENT_LAUNCHER_SUPPORTED_STREAMING_TYPES_ATTRIBUTE_ID, BITMAP32, 1, + 0), /* streaming protocols */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Media Playback cluster attributes +// TODO: add missing attributes once schema is updated +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(mediaPlaybackAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(ZCL_MEDIA_PLAYBACK_STATE_ATTRIBUTE_ID, ENUM8, 1, 0), /* current state */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_MEDIA_PLAYBACK_START_TIME_ATTRIBUTE_ID, EPOCH_US, 1, 0), /* start time */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_MEDIA_PLAYBACK_DURATION_ATTRIBUTE_ID, INT64U, 1, 0), /* duration */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_MEDIA_PLAYBACK_PLAYBACK_SPEED_ATTRIBUTE_ID, SINGLE, 1, 0), /* playback speed */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_MEDIA_PLAYBACK_PLAYBACK_SEEK_RANGE_END_ATTRIBUTE_ID, INT64U, 1, 0), /* seek range end */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_MEDIA_PLAYBACK_PLAYBACK_SEEK_RANGE_START_ATTRIBUTE_ID, INT64U, 1, 0), /* seek range start */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Target Navigator cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(targetNavigatorAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(ZCL_TARGET_NAVIGATOR_LIST_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, 0), /* target list */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_TARGET_NAVIGATOR_CURRENT_TARGET_ATTRIBUTE_ID, INT8U, 1, 0), /* current target */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Channel cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(channelAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(ZCL_TV_CHANNEL_LIST_ATTRIBUTE_ID, ARRAY, kDescriptorAttributeArraySize, 0), /* channel list */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_TV_CHANNEL_LINEUP_ATTRIBUTE_ID, STRUCT, 1, 0), /* lineup */ + DECLARE_DYNAMIC_ATTRIBUTE(ZCL_TV_CHANNEL_CURRENT_CHANNEL_ATTRIBUTE_ID, STRUCT, 1, 0), /* current channel */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Cluster List for Content App endpoint +DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(contentAppClusters) +DECLARE_DYNAMIC_CLUSTER(ZCL_DESCRIPTOR_CLUSTER_ID, descriptorAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, applicationBasicAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, keypadInputAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, applicationLauncherAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, accountLoginAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, contentLauncherAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, mediaPlaybackAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, targetNavigatorAttrs), + DECLARE_DYNAMIC_CLUSTER(ZCL_APPLICATION_BASIC_CLUSTER_ID, channelAttrs) DECLARE_DYNAMIC_CLUSTER_LIST_END; + +// Declare Content App endpoint +DECLARE_DYNAMIC_ENDPOINT(contentAppEndpoint, contentAppClusters); + +ContentAppImpl::ContentAppImpl(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId, + const char * szApplicationVersion) +{ + mApplicationBasic.SetApplicationName(szApplicationName); + mApplicationBasic.SetVendorName(szApplicationName); + mApplicationBasic.SetVendorId(vendorId); + mApplicationBasic.SetProductId(productId); + mApplicationBasic.SetApplicationVersion(szApplicationVersion); +} + +void ApplicationBasicImpl::SetApplicationName(const char * szApplicationName) +{ + ChipLogProgress(DeviceLayer, "ApplicationBasic[%s]: Application Name=\"%s\"", szApplicationName, szApplicationName); + + strncpy(mApplicationName, szApplicationName, sizeof(mApplicationName)); +} + +void ApplicationBasicImpl::SetVendorName(const char * szVendorName) +{ + ChipLogProgress(DeviceLayer, "ApplicationBasic[%s]: Vendor Name=\"%s\"", mApplicationName, szVendorName); + + strncpy(mVendorName, szVendorName, sizeof(mVendorName)); +} + +void ApplicationBasicImpl::SetApplicationVersion(const char * szApplicationVersion) +{ + ChipLogProgress(DeviceLayer, "ApplicationBasic[%s]: Application Version=\"%s\"", mApplicationName, szApplicationVersion); + + strncpy(mApplicationVersion, szApplicationVersion, sizeof(mApplicationVersion)); +} + +bool AccountLoginImpl::Login(const char * tempAccountId, uint32_t setupPin) +{ + ChipLogProgress(DeviceLayer, "AccountLogin: Login TempAccountId=\"%s\" SetupPIN=\"%d\"", tempAccountId, setupPin); + return (setupPin == mSetupPIN); +} + +uint32_t AccountLoginImpl::GetSetupPIN(const char * tempAccountId) +{ + ChipLogProgress(DeviceLayer, "AccountLogin: GetSetupPIN TempAccountId=\"%s\" returning setuppin=%d", tempAccountId, mSetupPIN); + return mSetupPIN; +} + +ContentAppFactoryImpl::ContentAppFactoryImpl() +{ + mContentApps[1].GetAccountLogin()->SetSetupPIN(34567890); + mContentApps[2].GetAccountLogin()->SetSetupPIN(20202021); +} + +ContentApp * ContentAppFactoryImpl::LoadContentAppByVendorId(uint16_t vendorId) +{ + for (unsigned int i = 0; i < sizeof(mContentApps); i++) + { + ContentAppImpl app = mContentApps[i]; + if (app.GetApplicationBasic()->GetVendorId() == vendorId) + { + AppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, DEVICE_TYPE_CONTENT_APP); + return &mContentApps[i]; + } + } + ChipLogProgress(DeviceLayer, "LoadContentAppByVendorId() - vendor %d not found ", vendorId); + + return nullptr; +} + +} // namespace AppPlatform +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/examples/tv-app/linux/AppImpl.h b/examples/tv-app/linux/AppImpl.h new file mode 100644 index 00000000000000..125140c2797631 --- /dev/null +++ b/examples/tv-app/linux/AppImpl.h @@ -0,0 +1,120 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @brief Manages Content Apps + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + +namespace chip { +namespace AppPlatform { + +class DLL_EXPORT ApplicationBasicImpl : public ApplicationBasic +{ +public: + virtual ~ApplicationBasicImpl() {} + + inline const char * GetVendorName() override { return mVendorName; }; + inline uint16_t GetVendorId() override { return mVendorId; }; + inline const char * GetApplicationName() override { return mApplicationName; }; + inline uint16_t GetProductId() override { return mProductId; }; + inline app::Clusters::ApplicationBasic::ApplicationBasicStatus GetApplicationStatus() override { return mApplicationStatus; }; + inline const char * GetApplicationVersion() override { return mApplicationVersion; }; + + inline void SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationBasicStatus applicationStatus) override + { + mApplicationStatus = applicationStatus; + }; + + void SetVendorName(const char * szVendorName); + inline void SetVendorId(uint16_t vendorId) { mVendorId = vendorId; }; + void SetApplicationName(const char * szApplicationName); + inline void SetProductId(uint16_t productId) { mProductId = productId; }; + void SetApplicationVersion(const char * szApplicationVersion); + +protected: + static const int kVendorNameSize = 32; + static const int kApplicationNameSize = 32; + static const int kApplicationVersionSize = 32; + + char mVendorName[kVendorNameSize]; + uint16_t mVendorId; + char mApplicationName[kApplicationNameSize]; + uint16_t mProductId; + app::Clusters::ApplicationBasic::ApplicationBasicStatus mApplicationStatus = + app::Clusters::ApplicationBasic::ApplicationBasicStatus::kStopped; + char mApplicationVersion[kApplicationVersionSize]; +}; + +class DLL_EXPORT AccountLoginImpl : public AccountLogin +{ +public: + virtual ~AccountLoginImpl() {} + + inline void SetSetupPIN(uint32_t setupPIN) override { mSetupPIN = setupPIN; }; + uint32_t GetSetupPIN(const char * tempAccountId) override; + bool Login(const char * tempAccountId, uint32_t setupPin) override; + +protected: + uint32_t mSetupPIN = 0; +}; + +class DLL_EXPORT ContentAppImpl : public ContentApp +{ +public: + ContentAppImpl(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId, + const char * szApplicationVersion); + virtual ~ContentAppImpl() {} + + inline ApplicationBasic * GetApplicationBasic() override { return &mApplicationBasic; }; + inline AccountLogin * GetAccountLogin() override { return &mAccountLogin; }; + +protected: + ApplicationBasicImpl mApplicationBasic; + AccountLoginImpl mAccountLogin; +}; + +class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory +{ +public: + ContentAppFactoryImpl(); + virtual ~ContentAppFactoryImpl() {} + + ContentApp * LoadContentAppByVendorId(uint16_t vendorId); + +protected: + ContentAppImpl mContentApps[3] = { ContentAppImpl("Vendor1", 1, "App1", 11, "Version1"), + ContentAppImpl("Vendor2", 2, "App2", 22, "Version2"), + ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3") }; +}; + +} // namespace AppPlatform +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/examples/tv-app/linux/AppPlatformShellCommands.cpp b/examples/tv-app/linux/AppPlatformShellCommands.cpp new file mode 100644 index 00000000000000..96acfed6270709 --- /dev/null +++ b/examples/tv-app/linux/AppPlatformShellCommands.cpp @@ -0,0 +1,202 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @file Contains shell commands for a commissionee (eg. end device) related to commissioning. + */ + +#include "AppPlatformShellCommands.h" +#include "ControllerShellCommands.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::chip::Controller; +using namespace chip::AppPlatform; + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + +namespace chip { +namespace Shell { + +static CHIP_ERROR pairApp(bool printHeader, size_t index) +{ + streamer_t * sout = streamer_get(); + + if (printHeader) + { + streamer_printf(sout, "udc-commission %ld\r\n", index); + } + + DeviceCommissioner * commissioner = GetDeviceCommissioner(); + UDCClientState * state = commissioner->GetUserDirectedCommissioningServer()->GetUDCClients().GetUDCClientState(index); + if (state == nullptr) + { + streamer_printf(sout, "udc client[%d] null \r\n", index); + } + else + { + ContentApp * app = chip::AppPlatform::AppPlatform::GetInstance().GetLoadContentAppByVendorId(state->GetVendorId()); + if (app == nullptr) + { + streamer_printf(sout, "no app found for vendor id=%d \r\n", state->GetVendorId()); + return CHIP_ERROR_BAD_REQUEST; + } + + if (app->GetAccountLogin() == nullptr) + { + streamer_printf(sout, "no AccountLogin cluster for app with vendor id=%d \r\n", state->GetVendorId()); + return CHIP_ERROR_BAD_REQUEST; + } + + char rotatingIdString[chip::Dnssd::kMaxRotatingIdLen * 2 + 1] = ""; + Encoding::BytesToUppercaseHexString(state->GetRotatingId(), chip::Dnssd::kMaxRotatingIdLen, rotatingIdString, + sizeof(rotatingIdString)); + + uint32_t pincode = app->GetAccountLogin()->GetSetupPIN(rotatingIdString); + if (pincode == 0) + { + streamer_printf(sout, "udc no pin returned for vendor id=%d rotating ID=%s \r\n", state->GetVendorId(), + rotatingIdString); + return CHIP_ERROR_BAD_REQUEST; + } + + return CommissionerPairUDC(pincode, index); + } + return CHIP_NO_ERROR; +} + +static CHIP_ERROR PrintAllCommands() +{ + streamer_t * sout = streamer_get(); + streamer_printf(sout, " help Usage: app \r\n"); + streamer_printf(sout, " add Add app with given vendor ID [1, 2, 9050]. Usage: app add 9050\r\n"); + streamer_printf(sout, " remove Remove app with given vendor ID [1, 2, 9050]. Usage: app remove 9050\r\n"); + streamer_printf(sout, + " setpin Set pincode for app with given vendor ID. Usage: app setpin 9050 34567890\r\n"); + streamer_printf(sout, + " commission Commission given udc-entry using given pincode from corresponding app. Usage: " + "app commission 0\r\n"); + streamer_printf(sout, "\r\n"); + + return CHIP_NO_ERROR; +} + +static CHIP_ERROR AppPlatformHandler(int argc, char ** argv) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + if (argc == 0 || strcmp(argv[0], "help") == 0) + { + return PrintAllCommands(); + } + else if (strcmp(argv[0], "add") == 0) + { + if (argc < 2) + { + return PrintAllCommands(); + } + char * eptr; + + uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10); + chip::AppPlatform::AppPlatform::GetInstance().GetLoadContentAppByVendorId(vid); + + ChipLogProgress(DeviceLayer, "added app"); + + return CHIP_NO_ERROR; + } + else if (strcmp(argv[0], "remove") == 0) + { + if (argc < 2) + { + return PrintAllCommands(); + } + char * eptr; + + uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10); + chip::AppPlatform::AppPlatform::GetInstance().UnloadContentAppByVendorId(vid); + + ChipLogProgress(DeviceLayer, "removed app"); + + return CHIP_NO_ERROR; + } + else if (strcmp(argv[0], "setpin") == 0) + { + if (argc < 3) + { + return PrintAllCommands(); + } + char * eptr; + + uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10); + uint32_t pincode = (uint32_t) strtol(argv[2], &eptr, 10); + ContentApp * app = chip::AppPlatform::AppPlatform::GetInstance().GetLoadContentAppByVendorId(vid); + if (app == nullptr) + { + ChipLogProgress(DeviceLayer, "no app found for vendor id=%d ", vid); + return CHIP_ERROR_BAD_REQUEST; + } + if (app->GetAccountLogin() == nullptr) + { + ChipLogProgress(DeviceLayer, "no AccountLogin cluster for app with vendor id=%d ", vid); + return CHIP_ERROR_BAD_REQUEST; + } + app->GetAccountLogin()->SetSetupPIN(pincode); + + ChipLogProgress(DeviceLayer, "set pin success"); + + return CHIP_NO_ERROR; + } + else if (strcmp(argv[0], "commission") == 0) + { + if (argc < 2) + { + return PrintAllCommands(); + } + char * eptr; + size_t index = (size_t) strtol(argv[1], &eptr, 10); + return error = pairApp(true, index); + } + else + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + return error; +} + +void RegisterAppPlatformCommands() +{ + + static const shell_command_t sDeviceComand = { &AppPlatformHandler, "app", "App commands. Usage: app [command_name]" }; + + // Register the root `device` command with the top-level shell. + Engine::Root().RegisterCommands(&sDeviceComand, 1); + return; +} + +} // namespace Shell +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/examples/tv-app/linux/AppPlatformShellCommands.h b/examples/tv-app/linux/AppPlatformShellCommands.h new file mode 100644 index 00000000000000..f3c8317e7c86b8 --- /dev/null +++ b/examples/tv-app/linux/AppPlatformShellCommands.h @@ -0,0 +1,33 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @brief Registers shell commands for a commissionee (eg. end device) related to commissioning. + */ + +#include + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED +namespace chip { +namespace Shell { + +void RegisterAppPlatformCommands(); + +} // namespace Shell +} // namespace chip +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/examples/tv-app/linux/BUILD.gn b/examples/tv-app/linux/BUILD.gn index 0a1b7cc7ed11a6..98beeb571f033b 100644 --- a/examples/tv-app/linux/BUILD.gn +++ b/examples/tv-app/linux/BUILD.gn @@ -14,6 +14,7 @@ import("//build_overrides/build.gni") import("//build_overrides/chip.gni") +import("args.gni") import("${chip_root}/build/chip/tools.gni") @@ -22,6 +23,9 @@ assert(chip_build_tools) executable("chip-tv-app") { sources = [ "${chip_root}/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h", + "AppImpl.cpp", + "AppImpl.h", + "AppPlatformShellCommands.cpp", "include/account-login/AccountLoginManager.cpp", "include/account-login/AccountLoginManager.h", "include/application-basic/ApplicationBasicManager.cpp", @@ -62,6 +66,10 @@ executable("chip-tv-app") { cflags = [ "-Wconversion" ] + if (chip_build_libshell) { + cflags += [ "-DENABLE_CHIP_SHELL" ] + } + output_dir = root_out_dir } diff --git a/examples/tv-app/linux/include/application-basic/ApplicationBasicManager.cpp b/examples/tv-app/linux/include/application-basic/ApplicationBasicManager.cpp index 6c6a1d7f9be7b4..35c9f712ad4b67 100644 --- a/examples/tv-app/linux/include/application-basic/ApplicationBasicManager.cpp +++ b/examples/tv-app/linux/include/application-basic/ApplicationBasicManager.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include using namespace chip; +using namespace chip::AppPlatform; CHIP_ERROR ApplicationBasicManager::Init() { @@ -157,7 +159,22 @@ Application ApplicationBasicManager::getApplicationForEndpoint(chip::EndpointId bool applicationBasicClusterChangeApplicationStatus(app::Clusters::ApplicationBasic::ApplicationBasicStatus status, chip::EndpointId endpoint) { - // TODO: Insert code here ChipLogProgress(Zcl, "Sent an application status change request %d for endpoint %d", to_underlying(status), endpoint); + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + ContentApp * app = chip::AppPlatform::AppPlatform::GetInstance().GetContentAppByEndpointId(endpoint); + if (app == NULL) + { + if (endpoint == 3) + { + // TODO: Fix hardcoded app endpoints 3-5, fix test cases + return true; + } + ChipLogProgress(Zcl, "No app for endpoint %d", endpoint); + return false; + } + app->GetApplicationBasic()->SetApplicationStatus(status); +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + return true; } diff --git a/examples/tv-app/linux/main.cpp b/examples/tv-app/linux/main.cpp index 7997037f197194..73433186f7a2b0 100644 --- a/examples/tv-app/linux/main.cpp +++ b/examples/tv-app/linux/main.cpp @@ -16,11 +16,14 @@ * limitations under the License. */ +#include "AppImpl.h" #include "AppMain.h" +#include "AppPlatformShellCommands.h" #include #include #include +#include #include #include @@ -34,9 +37,14 @@ #include "include/target-navigator/TargetNavigatorManager.h" #include "include/tv-channel/TvChannelManager.h" +#if defined(ENABLE_CHIP_SHELL) +#include +#endif + using namespace chip; using namespace chip::Transport; using namespace chip::DeviceLayer; +using namespace chip::AppPlatform; bool emberAfBasicClusterMfgSpecificPingCallback(chip::app::Command * commandObj) { @@ -48,6 +56,10 @@ int main(int argc, char * argv[]) { CHIP_ERROR err = CHIP_NO_ERROR; +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + chip::AppPlatform::ContentAppFactoryImpl factory; +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + // Init Keypad Input manager err = KeypadInputManager().Init(); SuccessOrExit(err); @@ -81,6 +93,18 @@ int main(int argc, char * argv[]) SuccessOrExit(err); VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0); + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + chip::AppPlatform::AppPlatform::GetInstance().SetupAppPlatform(); + chip::AppPlatform::AppPlatform::GetInstance().SetContentAppFactory(&factory); +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + +#if defined(ENABLE_CHIP_SHELL) +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + chip::Shell::RegisterAppPlatformCommands(); +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED +#endif + ChipLinuxAppMainLoop(); exit: if (err != CHIP_NO_ERROR) diff --git a/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h b/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h index 718aa12d9a05f1..5a00545682f8b0 100644 --- a/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h +++ b/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h @@ -27,9 +27,6 @@ #pragma once -// include the CHIPProjectConfig from config/standalone -#include - // TVs need to be commissioners and likely want to be discoverable #define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY 1 @@ -48,3 +45,11 @@ #define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_NAME 1 #define CHIP_DEVICE_CONFIG_DEVICE_NAME "Test TV" + +#define CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED 1 + +// overrides CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT in CHIPProjectConfig +#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT 16 + +// include the CHIPProjectConfig from config/standalone +#include diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 15daf51f5b0188..b854555e0799df 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -148,6 +148,12 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/supported-modes-manager.h", ] + } else if (cluster == "application-launcher-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/util/ContentApp.cpp", + "${_app_root}/util/ContentAppPlatform.cpp", + ] } else { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ] } diff --git a/src/app/util/ContentApp.cpp b/src/app/util/ContentApp.cpp new file mode 100644 index 00000000000000..afc711a4d095b5 --- /dev/null +++ b/src/app/util/ContentApp.cpp @@ -0,0 +1,164 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @file Contains shell commands for a commissionee (eg. end device) related to commissioning. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::AppPlatform; + +namespace chip { +namespace AppPlatform { + +#define ZCL_DESCRIPTOR_CLUSTER_REVISION (1u) +#define ZCL_APPLICATION_BASIC_CLUSTER_REVISION (1u) + +EmberAfStatus ContentApp::HandleReadAttribute(ClusterId clusterId, chip::AttributeId attributeId, uint8_t * buffer, + uint16_t maxReadLength) +{ + ChipLogProgress(DeviceLayer, "Read Attribute for device %s cluster %d attribute=%d)", + GetApplicationBasic()->GetApplicationName(), static_cast(clusterId), + static_cast(attributeId)); + + EmberAfStatus ret = EMBER_ZCL_STATUS_FAILURE; + if (clusterId == ZCL_APPLICATION_BASIC_CLUSTER_ID) + { + ret = GetApplicationBasic()->HandleReadAttribute(attributeId, buffer, maxReadLength); + } + if (clusterId == ZCL_ACCOUNT_LOGIN_CLUSTER_ID) + { + ret = GetAccountLogin()->HandleReadAttribute(attributeId, buffer, maxReadLength); + } + return ret; +} + +EmberAfStatus ContentApp::HandleWriteAttribute(ClusterId clusterId, chip::AttributeId attributeId, uint8_t * buffer) +{ + ChipLogProgress(DeviceLayer, "Read Attribute for device %s cluster %d attribute=%d)", + GetApplicationBasic()->GetApplicationName(), static_cast(clusterId), + static_cast(attributeId)); + + EmberAfStatus ret = EMBER_ZCL_STATUS_FAILURE; + + if (clusterId == ZCL_APPLICATION_BASIC_CLUSTER_ID) + { + ret = GetApplicationBasic()->HandleWriteAttribute(attributeId, buffer); + } + if (clusterId == ZCL_ACCOUNT_LOGIN_CLUSTER_ID) + { + ret = GetAccountLogin()->HandleWriteAttribute(attributeId, buffer); + } + return ret; +} + +EmberAfStatus ApplicationBasic::HandleReadAttribute(chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength) +{ + ChipLogProgress(DeviceLayer, "ApplicationBasic::HandleReadAttribute: attrId=%d, maxReadLength=%d", + static_cast(attributeId), maxReadLength); + + if ((attributeId == ZCL_APPLICATION_VENDOR_NAME_ATTRIBUTE_ID) && (maxReadLength == 32)) + { + uint8_t bufferMemory[254]; + MutableByteSpan zclString(bufferMemory); + MakeZclCharString(zclString, GetVendorName()); + buffer = zclString.data(); + } + else if ((attributeId == ZCL_APPLICATION_VENDOR_ID_ATTRIBUTE_ID) && (maxReadLength == 1)) + { + *(uint16_t *) buffer = GetVendorId(); + } + else if ((attributeId == ZCL_APPLICATION_NAME_ATTRIBUTE_ID) && (maxReadLength == 32)) + { + uint8_t bufferMemory[254]; + MutableByteSpan zclString(bufferMemory); + MakeZclCharString(zclString, GetApplicationName()); + buffer = zclString.data(); + } + else if ((attributeId == ZCL_APPLICATION_PRODUCT_ID_ATTRIBUTE_ID) && (maxReadLength == 1)) + { + *(uint16_t *) buffer = GetProductId(); + } + else if ((attributeId == ZCL_APPLICATION_STATUS_ATTRIBUTE_ID) && (maxReadLength == 1)) + { + *buffer = (uint8_t) GetApplicationStatus(); + } + else if ((attributeId == ZCL_CLUSTER_REVISION_SERVER_ATTRIBUTE_ID) && (maxReadLength == 2)) + { + *buffer = (uint16_t) ZCL_APPLICATION_BASIC_CLUSTER_REVISION; + } + else + { + return EMBER_ZCL_STATUS_FAILURE; + } + + return EMBER_ZCL_STATUS_SUCCESS; +} + +EmberAfStatus ApplicationBasic::HandleWriteAttribute(chip::AttributeId attributeId, uint8_t * buffer) +{ + ChipLogProgress(DeviceLayer, "ApplicationBasic::HandleWriteAttribute: attrId=%d", static_cast(attributeId)); + + if (attributeId == ZCL_APPLICATION_STATUS_ATTRIBUTE_ID) + { + if (*buffer) + { + SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationBasicStatus::kActiveVisibleFocus); + } + else + { + SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationBasicStatus::kActiveVisibleNotFocus); + } + } + else + { + return EMBER_ZCL_STATUS_FAILURE; + } + + return EMBER_ZCL_STATUS_SUCCESS; +} + +EmberAfStatus AccountLogin::HandleReadAttribute(chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength) +{ + ChipLogProgress(DeviceLayer, "AccountLogin::HandleReadAttribute: attrId=%d, maxReadLength=%d", + static_cast(attributeId), maxReadLength); + return EMBER_ZCL_STATUS_FAILURE; +} + +EmberAfStatus AccountLogin::HandleWriteAttribute(chip::AttributeId attributeId, uint8_t * buffer) +{ + ChipLogProgress(DeviceLayer, "AccountLogin::HandleWriteAttribute: attrId=%d", static_cast(attributeId)); + return EMBER_ZCL_STATUS_FAILURE; +} + +} // namespace AppPlatform +} // namespace chip diff --git a/src/app/util/ContentApp.h b/src/app/util/ContentApp.h new file mode 100644 index 00000000000000..729397d12557e7 --- /dev/null +++ b/src/app/util/ContentApp.h @@ -0,0 +1,83 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @brief Manages Content Apps + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace chip { +namespace AppPlatform { + +class DLL_EXPORT ApplicationBasic +{ +public: + virtual ~ApplicationBasic() = default; + + virtual const char * GetVendorName() = 0; + virtual uint16_t GetVendorId() = 0; + virtual const char * GetApplicationName() = 0; + virtual uint16_t GetProductId() = 0; + virtual app::Clusters::ApplicationBasic::ApplicationBasicStatus GetApplicationStatus() = 0; + virtual const char * GetApplicationVersion() = 0; + virtual void SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationBasicStatus applicationStatus) = 0; + + EmberAfStatus HandleReadAttribute(chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength); + EmberAfStatus HandleWriteAttribute(chip::AttributeId attributeId, uint8_t * buffer); +}; + +class DLL_EXPORT AccountLogin +{ +public: + virtual ~AccountLogin() = default; + + virtual void SetSetupPIN(uint32_t setupPIN) = 0; + virtual uint32_t GetSetupPIN(const char * tempAccountId) = 0; + virtual bool Login(const char * tempAccountId, uint32_t setupPin) = 0; + + EmberAfStatus HandleReadAttribute(chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength); + EmberAfStatus HandleWriteAttribute(chip::AttributeId attributeId, uint8_t * buffer); +}; + +class DLL_EXPORT ContentApp +{ +public: + virtual ~ContentApp() = default; + + inline void SetEndpointId(chip::EndpointId id) { mEndpointId = id; }; + inline chip::EndpointId GetEndpointId() { return mEndpointId; }; + + virtual ApplicationBasic * GetApplicationBasic() = 0; + virtual AccountLogin * GetAccountLogin() = 0; + + EmberAfStatus HandleReadAttribute(ClusterId clusterId, chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength); + EmberAfStatus HandleWriteAttribute(ClusterId clusterId, chip::AttributeId attributeId, uint8_t * buffer); + +protected: + chip::EndpointId mEndpointId = 0; +}; + +} // namespace AppPlatform +} // namespace chip diff --git a/src/app/util/ContentAppPlatform.cpp b/src/app/util/ContentAppPlatform.cpp new file mode 100644 index 00000000000000..7b3c16ac501bb1 --- /dev/null +++ b/src/app/util/ContentAppPlatform.cpp @@ -0,0 +1,258 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @file Contains shell commands for a commissionee (eg. end device) related to commissioning. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::AppPlatform; + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + +// Device Version for dynamic endpoints: +#define DEVICE_VERSION_DEFAULT 1 + +EmberAfStatus emberAfExternalAttributeReadCallback(EndpointId endpoint, ClusterId clusterId, + EmberAfAttributeMetadata * attributeMetadata, uint16_t manufacturerCode, + uint8_t * buffer, uint16_t maxReadLength, int32_t index) +{ + uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); + + ChipLogProgress(DeviceLayer, "emberAfExternalAttributeReadCallback endpoint %d ", endpointIndex); + + EmberAfStatus ret = EMBER_ZCL_STATUS_FAILURE; + + ContentApp * app = chip::AppPlatform::AppPlatform::GetInstance().GetContentAppByEndpointId(endpoint); + if (app != NULL) + { + ret = app->HandleReadAttribute(clusterId, attributeMetadata->attributeId, buffer, maxReadLength); + } + + return ret; +} + +EmberAfStatus emberAfExternalAttributeWriteCallback(EndpointId endpoint, ClusterId clusterId, + EmberAfAttributeMetadata * attributeMetadata, uint16_t manufacturerCode, + uint8_t * buffer, int32_t index) +{ + uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); + + ChipLogProgress(DeviceLayer, "emberAfExternalAttributeWriteCallback endpoint %d ", endpointIndex); + + EmberAfStatus ret = EMBER_ZCL_STATUS_FAILURE; + + ContentApp * app = chip::AppPlatform::AppPlatform::GetInstance().GetContentAppByEndpointId(endpoint); + if (app != NULL) + { + ret = app->HandleWriteAttribute(clusterId, attributeMetadata->attributeId, buffer); + } + + return ret; +} + +namespace chip { +namespace AppPlatform { + +int AppPlatform::AddContentApp(ContentApp * app, EmberAfEndpointType * ep, uint16_t deviceType) +{ + ChipLogProgress(DeviceLayer, "Adding device %s ", app->GetApplicationBasic()->GetApplicationName()); + uint8_t index = 0; + // check if already loaded + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + if (mContentApps[index] == app) + { + ChipLogProgress(DeviceLayer, "Already added"); + return index; + } + index++; + } + + index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + if (NULL == mContentApps[index]) + { + mContentApps[index] = app; + EmberAfStatus ret; + while (1) + { + ret = emberAfSetDynamicEndpoint(index, mCurrentEndpointId, ep, deviceType, DEVICE_VERSION_DEFAULT); + if (ret == EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogProgress(DeviceLayer, "Added device %s to dynamic endpoint %d (index=%d)", + app->GetApplicationBasic()->GetApplicationName(), mCurrentEndpointId, index); + app->SetEndpointId(mCurrentEndpointId); + return index; + } + else if (ret != EMBER_ZCL_STATUS_DUPLICATE_EXISTS) + { + ChipLogProgress(DeviceLayer, "Adding device error=%d", ret); + return -1; + } + // Handle wrap condition + if (++mCurrentEndpointId < mFirstDynamicEndpointId) + { + mCurrentEndpointId = mFirstDynamicEndpointId; + } + } + } + index++; + } + ChipLogProgress(DeviceLayer, "Failed to add dynamic endpoint: No endpoints available!"); + return -1; +} + +int AppPlatform::RemoveContentApp(ContentApp * app) +{ + uint8_t index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + if (mContentApps[index] == app) + { + EndpointId ep = emberAfClearDynamicEndpoint(index); + mContentApps[index] = NULL; + ChipLogProgress(DeviceLayer, "Removed device %s from dynamic endpoint %d (index=%d)", + app->GetApplicationBasic()->GetApplicationName(), ep, index); + // Silence complaints about unused ep when progress logging + // disabled. + UNUSED_VAR(ep); + return index; + } + index++; + } + return -1; +} + +void AppPlatform::SetupAppPlatform() +{ + ChipLogProgress(DeviceLayer, "AppPlatform::SetupAppPlatform()"); + + // Clear out the device database + uint8_t index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + mContentApps[index] = NULL; + index++; + } + + // Set starting endpoint id where dynamic endpoints will be assigned, which + // will be the next consecutive endpoint id after the last fixed endpoint. + mFirstDynamicEndpointId = static_cast( + static_cast(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1))) + 1); + mCurrentEndpointId = mFirstDynamicEndpointId; + + { + for (int i = 0; i < emberAfFixedEndpointCount(); i++) + { + ChipLogProgress(DeviceLayer, "endpoint index=%d, id=%d", i, + static_cast(static_cast(emberAfEndpointFromIndex(static_cast(i))))); + } + } + + if (mCurrentEndpointId < emberAfFixedEndpointCount()) + { + mCurrentEndpointId = emberAfFixedEndpointCount(); + } + + ChipLogProgress(DeviceLayer, "emberAfFixedEndpointCount()=%d mCurrentEndpointId=%d", emberAfFixedEndpointCount(), + mCurrentEndpointId); + + // Disable last fixed endpoint, which is used as a placeholder for all of the + // supported clusters so that ZAP will generated the requisite code. + // emberAfEndpointEnableDisable(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1)), false); +} + +void AppPlatform::UnloadContentAppByVendorId(uint16_t vendorId) +{ + uint8_t index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + ContentApp * app = mContentApps[index]; + if (app != NULL && app->GetApplicationBasic()->GetVendorId() == vendorId) + { + EndpointId ep = emberAfClearDynamicEndpoint(index); + mContentApps[index] = NULL; + ChipLogProgress(DeviceLayer, "Removed device %s from dynamic endpoint %d (index=%d)", + app->GetApplicationBasic()->GetApplicationName(), ep, index); + // Silence complaints about unused ep when progress logging + // disabled. + UNUSED_VAR(ep); + return; + } + index++; + } + return; +} + +ContentApp * AppPlatform::GetLoadContentAppByVendorId(uint16_t vendorId) +{ + ChipLogProgress(DeviceLayer, "GetLoadContentAppByVendorId() - vendorId %d ", vendorId); + uint8_t index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + ContentApp * app = mContentApps[index]; + if (app != NULL && app->GetApplicationBasic()->GetVendorId() == vendorId) + { + return app; + } + index++; + } + if (mContentAppFactory != NULL) + { + return mContentAppFactory->LoadContentAppByVendorId(vendorId); + } + return NULL; +} + +ContentApp * AppPlatform::GetContentAppByEndpointId(chip::EndpointId id) +{ + uint8_t index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + ContentApp * app = mContentApps[index]; + if (app != NULL && app->GetEndpointId() == id) + { + return app; + } + index++; + } + ChipLogProgress(DeviceLayer, "GetContentAppByEndpointId() - endpoint %d not found ", id); + return NULL; +} + +} // namespace AppPlatform +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/src/app/util/ContentAppPlatform.h b/src/app/util/ContentAppPlatform.h new file mode 100644 index 00000000000000..35c876ca91e9be --- /dev/null +++ b/src/app/util/ContentAppPlatform.h @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @brief Manages Content Apps + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED +namespace chip { +namespace AppPlatform { + +class DLL_EXPORT ContentAppFactory +{ +public: + virtual ~ContentAppFactory() = default; + virtual ContentApp * LoadContentAppByVendorId(uint16_t vendorId) = 0; +}; + +class DLL_EXPORT AppPlatform +{ +public: + static AppPlatform & GetInstance() + { + static AppPlatform instance; + return instance; + } + + void SetupAppPlatform(); + + inline void SetContentAppFactory(ContentAppFactory * factory) { mContentAppFactory = factory; }; + + // add and remove apps from the platform. + // This will assign the app to an endpoint and make it accessible via Matter + int AddContentApp(ContentApp * app, EmberAfEndpointType * ep, uint16_t deviceType); + int RemoveContentApp(ContentApp * app); + + // load and unload by vendor id + void UnloadContentAppByVendorId(uint16_t vendorId); + ContentApp * GetLoadContentAppByVendorId(uint16_t vendorId); + + // helpful method to get a Content App by endpoint in order to perform attribute or command ops + ContentApp * GetContentAppByEndpointId(chip::EndpointId id); + +protected: + ContentAppFactory * mContentAppFactory = nullptr; + EndpointId mCurrentEndpointId; + EndpointId mFirstDynamicEndpointId; + ContentApp * mContentApps[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; +}; + +} // namespace AppPlatform +} // namespace chip +#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/src/app/util/attribute-storage.cpp b/src/app/util/attribute-storage.cpp index 7cdd712a33ec12..854689d7ceaf66 100644 --- a/src/app/util/attribute-storage.cpp +++ b/src/app/util/attribute-storage.cpp @@ -146,7 +146,7 @@ void emberAfEndpointConfigure(void) emAfEndpoints[ep].bitmask = EMBER_AF_ENDPOINT_ENABLED; } -#ifdef DYNAMIC_ENDPOINT_COUNT +#if CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT if (MAX_ENDPOINT_COUNT > FIXED_ENDPOINT_COUNT) { // This is assuming that EMBER_AF_ENDPOINT_DISABLED is 0 diff --git a/src/app/util/attribute-storage.h b/src/app/util/attribute-storage.h index 8587342429a5d2..66915e8ed627e8 100644 --- a/src/app/util/attribute-storage.h +++ b/src/app/util/attribute-storage.h @@ -45,6 +45,7 @@ #include #include #include +#include #if !defined(EMBER_SCRIPTED_TEST) #include @@ -67,11 +68,7 @@ // If we have fixed number of endpoints, then max is the same. #ifdef FIXED_ENDPOINT_COUNT -#ifdef DYNAMIC_ENDPOINT_COUNT -#define MAX_ENDPOINT_COUNT (FIXED_ENDPOINT_COUNT + DYNAMIC_ENDPOINT_COUNT) -#else -#define MAX_ENDPOINT_COUNT FIXED_ENDPOINT_COUNT -#endif +#define MAX_ENDPOINT_COUNT (FIXED_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) #endif #include diff --git a/src/include/platform/CHIPDeviceConfig.h b/src/include/platform/CHIPDeviceConfig.h index b7e40bd6dec042..d48736157d4320 100644 --- a/src/include/platform/CHIPDeviceConfig.h +++ b/src/include/platform/CHIPDeviceConfig.h @@ -1269,3 +1269,23 @@ #ifndef CHIP_DEVICE_CONFIG_PAIRING_SECONDARY_INSTRUCTION #define CHIP_DEVICE_CONFIG_PAIRING_SECONDARY_INSTRUCTION "" #endif + +// -------------------- App Platform Configuration -------------------- + +/** + * CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED + * + * Does this device support an app platform 1=Yes, 0=No + */ +#ifndef CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED +#define CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED 0 +#endif + +/** + * CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT + * + * When app platform is enabled, max number of endpoints + */ +#ifndef CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT +#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT 0 +#endif diff --git a/src/protocols/user_directed_commissioning/UDCClientState.h b/src/protocols/user_directed_commissioning/UDCClientState.h index 088121a67afdd1..1b8534711417dc 100644 --- a/src/protocols/user_directed_commissioning/UDCClientState.h +++ b/src/protocols/user_directed_commissioning/UDCClientState.h @@ -75,6 +75,15 @@ class UDCClientState uint16_t GetLongDiscriminator() const { return mLongDiscriminator; } void SetLongDiscriminator(uint16_t value) { mLongDiscriminator = value; } + uint16_t GetVendorId() const { return mVendorId; } + void SetVendorId(uint16_t value) { mVendorId = value; } + + uint16_t GetProductId() const { return mProductId; } + void SetProductId(uint16_t value) { mProductId = value; } + + const uint8_t * GetRotatingId() const { return mRotatingId; } + void SetRotatingId(const uint8_t * rotatingId) { memcpy(mRotatingId, rotatingId, sizeof(mRotatingId)); } + UDCClientProcessingState GetUDCClientProcessingState() const { return mUDCClientProcessingState; } void SetUDCClientProcessingState(UDCClientProcessingState state) { mUDCClientProcessingState = state; } @@ -102,6 +111,9 @@ class UDCClientState char mInstanceName[Dnssd::Commissionable::kInstanceNameMaxLength + 1]; char mDeviceName[Dnssd::kMaxDeviceNameLen + 1]; uint16_t mLongDiscriminator = 0; + uint16_t mVendorId; + uint16_t mProductId; + uint8_t mRotatingId[chip::Dnssd::kMaxRotatingIdLen]; UDCClientProcessingState mUDCClientProcessingState; System::Clock::Timestamp mExpirationTime = System::Clock::kZero; }; diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp b/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp index a227b96fff8969..ccd07027f56aa2 100644 --- a/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp +++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp @@ -144,9 +144,12 @@ void UserDirectedCommissioningServer::OnCommissionableNodeFound(const Dnssd::Dis client->SetPeerAddress(chip::Transport::PeerAddress::UDP(nodeData.ipAddress[0], nodeData.port)); } - // client->SetPeerAddress(chip::Transport::PeerAddress::UDP(nodeData.ipAddress[0], nodeData.port)); client->SetDeviceName(nodeData.deviceName); client->SetLongDiscriminator(nodeData.longDiscriminator); + client->SetVendorId(nodeData.vendorId); + client->SetProductId(nodeData.productId); + client->SetDeviceName(nodeData.deviceName); + client->SetRotatingId(nodeData.rotatingId); // Call the registered mUserConfirmationProvider, if any. if (mUserConfirmationProvider != nullptr) @@ -170,8 +173,13 @@ void UserDirectedCommissioningServer::PrintUDCClients() char addrBuffer[chip::Transport::PeerAddress::kMaxToStringSize]; state->GetPeerAddress().ToString(addrBuffer); - ChipLogProgress(AppServer, "UDC Client[%d] instance=%s deviceName=%s address=%s, disc=%d", i, state->GetInstanceName(), - state->GetDeviceName(), addrBuffer, state->GetLongDiscriminator()); + char rotatingIdString[chip::Dnssd::kMaxRotatingIdLen * 2 + 1] = ""; + Encoding::BytesToUppercaseHexString(state->GetRotatingId(), chip::Dnssd::kMaxRotatingIdLen, rotatingIdString, + sizeof(rotatingIdString)); + + ChipLogProgress(AppServer, "UDC Client[%d] instance=%s deviceName=%s address=%s, vid/pid=%d/%d disc=%d rid=%s", i, + state->GetInstanceName(), state->GetDeviceName(), addrBuffer, state->GetVendorId(), + state->GetProductId(), state->GetLongDiscriminator(), rotatingIdString); } } }