/* * * Copyright (c) 2020 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 "BindingHandler.h" #include "AppConfig.h" #include "app/CommandSender.h" #include "app/clusters/bindings/BindingManager.h" #include "app/server/Server.h" #include "controller/InvokeInteraction.h" #include "platform/CHIPDeviceLayer.h" #include #include #if defined(ENABLE_CHIP_SHELL) #include "lib/shell/Engine.h" #include "lib/shell/commands/Help.h" #endif // ENABLE_CHIP_SHELL using namespace chip; using namespace chip::app; #if defined(ENABLE_CHIP_SHELL) using Shell::Engine; using Shell::shell_command_t; using Shell::streamer_get; using Shell::streamer_printf; Engine sShellSwitchSubCommands; Engine sShellSwitchOnOffSubCommands; Engine sShellSwitchGroupsSubCommands; Engine sShellSwitchGroupsOnOffSubCommands; Engine sShellSwitchBindingSubCommands; #endif // defined(ENABLE_CHIP_SHELL) namespace { void ProcessOnOffUnicastBindingCommand(CommandId commandId, const EmberBindingTableEntry & binding, DeviceProxy * peer_device) { auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { ChipLogProgress(NotSpecified, "OnOff command succeeds"); }; auto onFailure = [](CHIP_ERROR error) { ChipLogError(NotSpecified, "OnOff command failed: %" CHIP_ERROR_FORMAT, error.Format()); }; // Eason+ ChipLogDetail(AppServer, "ProcessOnOffUnicastBindingCommand~~~~"); switch (commandId) { case Clusters::OnOff::Commands::Toggle::Id: Clusters::OnOff::Commands::Toggle::Type toggleCommand; Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), binding.remote, toggleCommand, onSuccess, onFailure); break; case Clusters::OnOff::Commands::On::Id: Clusters::OnOff::Commands::On::Type onCommand; Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), binding.remote, onCommand, onSuccess, onFailure); break; case Clusters::OnOff::Commands::Off::Id: Clusters::OnOff::Commands::Off::Type offCommand; Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), binding.remote, offCommand, onSuccess, onFailure); break; } } void ProcessOnOffGroupBindingCommand(CommandId commandId, const EmberBindingTableEntry & binding) { Messaging::ExchangeManager & exchangeMgr = Server::GetInstance().GetExchangeManager(); switch (commandId) { case Clusters::OnOff::Commands::Toggle::Id: Clusters::OnOff::Commands::Toggle::Type toggleCommand; Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, toggleCommand); break; case Clusters::OnOff::Commands::On::Id: Clusters::OnOff::Commands::On::Type onCommand; Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, onCommand); break; case Clusters::OnOff::Commands::Off::Id: Clusters::OnOff::Commands::Off::Type offCommand; Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, offCommand); break; } } // void LightSwitchChangedHandler(const EmberBindingTableEntry & binding, DeviceProxy * peer_device, void * context) void LightSwitchChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device, void * context) { VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectedFn: context is null")); BindingCommandData * data = static_cast(context); // Eason+ ChipLogDetail(AppServer, "LightSwitchChangedHandler~~~~~~~"); if (binding.type == MATTER_MULTICAST_BINDING && data->isGroup) { switch (data->clusterId) { case Clusters::OnOff::Id: ProcessOnOffGroupBindingCommand(data->commandId, binding); break; } } else if (binding.type == MATTER_UNICAST_BINDING && !data->isGroup) { switch (data->clusterId) { case Clusters::OnOff::Id: ProcessOnOffUnicastBindingCommand(data->commandId, binding, peer_device); break; } } } void LightSwitchContextReleaseHandler(void * context) { VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "LightSwitchContextReleaseHandler: context is null")); Platform::Delete(static_cast(context)); } #ifdef ENABLE_CHIP_SHELL /******************************************************** * Switch shell functions *********************************************************/ CHIP_ERROR SwitchHelpHandler(int argc, char ** argv) { sShellSwitchSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); return CHIP_NO_ERROR; } CHIP_ERROR SwitchCommandHandler(int argc, char ** argv) { if (argc == 0) { return SwitchHelpHandler(argc, argv); } return sShellSwitchSubCommands.ExecCommand(argc, argv); } /******************************************************** * OnOff switch shell functions *********************************************************/ CHIP_ERROR OnOffHelpHandler(int argc, char ** argv) { sShellSwitchOnOffSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); return CHIP_NO_ERROR; } CHIP_ERROR OnOffSwitchCommandHandler(int argc, char ** argv) { if (argc == 0) { return OnOffHelpHandler(argc, argv); } return sShellSwitchOnOffSubCommands.ExecCommand(argc, argv); } CHIP_ERROR OnSwitchCommandHandler(int argc, char ** argv) { BindingCommandData * data = Platform::New(); data->commandId = Clusters::OnOff::Commands::On::Id; data->clusterId = Clusters::OnOff::Id; DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); return CHIP_NO_ERROR; } CHIP_ERROR OffSwitchCommandHandler(int argc, char ** argv) { BindingCommandData * data = Platform::New(); data->commandId = Clusters::OnOff::Commands::Off::Id; data->clusterId = Clusters::OnOff::Id; DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); return CHIP_NO_ERROR; } CHIP_ERROR ToggleSwitchCommandHandler(int argc, char ** argv) { BindingCommandData * data = Platform::New(); data->commandId = Clusters::OnOff::Commands::Toggle::Id; data->clusterId = Clusters::OnOff::Id; DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); return CHIP_NO_ERROR; } /******************************************************** * bind switch shell functions *********************************************************/ CHIP_ERROR BindingHelpHandler(int argc, char ** argv) { sShellSwitchBindingSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); return CHIP_NO_ERROR; } CHIP_ERROR BindingSwitchCommandHandler(int argc, char ** argv) { if (argc == 0) { return BindingHelpHandler(argc, argv); } return sShellSwitchBindingSubCommands.ExecCommand(argc, argv); } CHIP_ERROR BindingGroupBindCommandHandler(int argc, char ** argv) { VerifyOrReturnError(argc == 2, CHIP_ERROR_INVALID_ARGUMENT); EmberBindingTableEntry * entry = Platform::New(); entry->type = MATTER_MULTICAST_BINDING; entry->fabricIndex = atoi(argv[0]); entry->groupId = atoi(argv[1]); entry->local = 1; // Hardcoded to endpoint 1 for now entry->clusterId.emplace(6); // Hardcoded to OnOff cluster for now DeviceLayer::PlatformMgr().ScheduleWork(BindingWorkerFunction, reinterpret_cast(entry)); return CHIP_NO_ERROR; } CHIP_ERROR BindingUnicastBindCommandHandler(int argc, char ** argv) { VerifyOrReturnError(argc == 3, CHIP_ERROR_INVALID_ARGUMENT); EmberBindingTableEntry * entry = Platform::New(); entry->type = MATTER_UNICAST_BINDING; entry->fabricIndex = atoi(argv[0]); entry->nodeId = atoi(argv[1]); entry->local = 1; // Hardcoded to endpoint 1 for now entry->remote = atoi(argv[2]); entry->clusterId.emplace(6); // Hardcode to OnOff cluster for now DeviceLayer::PlatformMgr().ScheduleWork(BindingWorkerFunction, reinterpret_cast(entry)); return CHIP_NO_ERROR; } /******************************************************** * Groups switch shell functions *********************************************************/ CHIP_ERROR GroupsHelpHandler(int argc, char ** argv) { sShellSwitchGroupsSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); return CHIP_NO_ERROR; } CHIP_ERROR GroupsSwitchCommandHandler(int argc, char ** argv) { if (argc == 0) { return GroupsHelpHandler(argc, argv); } return sShellSwitchGroupsSubCommands.ExecCommand(argc, argv); } /******************************************************** * Groups OnOff switch shell functions *********************************************************/ CHIP_ERROR GroupsOnOffHelpHandler(int argc, char ** argv) { sShellSwitchGroupsOnOffSubCommands.ForEachCommand(Shell::PrintCommandHelp, nullptr); return CHIP_NO_ERROR; } CHIP_ERROR GroupsOnOffSwitchCommandHandler(int argc, char ** argv) { if (argc == 0) { return GroupsOnOffHelpHandler(argc, argv); } return sShellSwitchGroupsOnOffSubCommands.ExecCommand(argc, argv); } CHIP_ERROR GroupOnSwitchCommandHandler(int argc, char ** argv) { BindingCommandData * data = Platform::New(); data->commandId = Clusters::OnOff::Commands::On::Id; data->clusterId = Clusters::OnOff::Id; data->isGroup = true; DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); return CHIP_NO_ERROR; } CHIP_ERROR GroupOffSwitchCommandHandler(int argc, char ** argv) { BindingCommandData * data = Platform::New(); data->commandId = Clusters::OnOff::Commands::Off::Id; data->clusterId = Clusters::OnOff::Id; data->isGroup = true; DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); return CHIP_NO_ERROR; } CHIP_ERROR GroupToggleSwitchCommandHandler(int argc, char ** argv) { BindingCommandData * data = Platform::New(); data->commandId = Clusters::OnOff::Commands::Toggle::Id; data->clusterId = Clusters::OnOff::Id; data->isGroup = true; DeviceLayer::PlatformMgr().ScheduleWork(SwitchWorkerFunction, reinterpret_cast(data)); return CHIP_NO_ERROR; } /** * @brief configures switch matter shell * */ static void RegisterSwitchCommands() { static const shell_command_t sSwitchSubCommands[] = { { &SwitchHelpHandler, "help", "Usage: switch " }, { &OnOffSwitchCommandHandler, "onoff", " Usage: switch onoff " }, { &GroupsSwitchCommandHandler, "groups", "Usage: switch groups " }, { &BindingSwitchCommandHandler, "binding", "Usage: switch binding " } }; static const shell_command_t sSwitchOnOffSubCommands[] = { { &OnOffHelpHandler, "help", "Usage : switch ononff " }, { &OnSwitchCommandHandler, "on", "Sends on command to bound lighting app" }, { &OffSwitchCommandHandler, "off", "Sends off command to bound lighting app" }, { &ToggleSwitchCommandHandler, "toggle", "Sends toggle command to bound lighting app" } }; static const shell_command_t sSwitchGroupsSubCommands[] = { { &GroupsHelpHandler, "help", "Usage: switch groups " }, { &GroupsOnOffSwitchCommandHandler, "onoff", "Usage: switch groups onoff " } }; static const shell_command_t sSwitchGroupsOnOffSubCommands[] = { { &GroupsOnOffHelpHandler, "help", "Usage: switch groups onoff " }, { &GroupOnSwitchCommandHandler, "on", "Sends on command to bound group" }, { &GroupOffSwitchCommandHandler, "off", "Sends off command to bound group" }, { &GroupToggleSwitchCommandHandler, "toggle", "Sends toggle command to group" } }; static const shell_command_t sSwitchBindingSubCommands[] = { { &BindingHelpHandler, "help", "Usage: switch binding " }, { &BindingGroupBindCommandHandler, "group", "Usage: switch binding group " }, { &BindingUnicastBindCommandHandler, "unicast", "Usage: switch binding group " } }; static const shell_command_t sSwitchCommand = { &SwitchCommandHandler, "switch", "Light-switch commands. Usage: switch " }; sShellSwitchGroupsOnOffSubCommands.RegisterCommands(sSwitchGroupsOnOffSubCommands, ArraySize(sSwitchGroupsOnOffSubCommands)); sShellSwitchOnOffSubCommands.RegisterCommands(sSwitchOnOffSubCommands, ArraySize(sSwitchOnOffSubCommands)); sShellSwitchGroupsSubCommands.RegisterCommands(sSwitchGroupsSubCommands, ArraySize(sSwitchGroupsSubCommands)); sShellSwitchBindingSubCommands.RegisterCommands(sSwitchBindingSubCommands, ArraySize(sSwitchBindingSubCommands)); sShellSwitchSubCommands.RegisterCommands(sSwitchSubCommands, ArraySize(sSwitchSubCommands)); Engine::Root().RegisterCommands(&sSwitchCommand, 1); } #endif // ENABLE_CHIP_SHELL void InitBindingHandlerInternal(intptr_t arg) { auto & server = chip::Server::GetInstance(); chip::BindingManager::GetInstance().Init( { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }); chip::BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(LightSwitchChangedHandler); chip::BindingManager::GetInstance().RegisterBoundDeviceContextReleaseHandler(LightSwitchContextReleaseHandler); } } // namespace /******************************************************** * Switch functions *********************************************************/ void SwitchWorkerFunction(intptr_t context) { VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "SwitchWorkerFunction - Invalid work data")); BindingCommandData * data = reinterpret_cast(context); BindingManager::GetInstance().NotifyBoundClusterChanged(data->localEndpointId, data->clusterId, static_cast(data)); } void BindingWorkerFunction(intptr_t context) { VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "BindingWorkerFunction - Invalid work data")); EmberBindingTableEntry * entry = reinterpret_cast(context); AddBindingEntry(*entry); Platform::Delete(entry); } CHIP_ERROR InitBindingHandler() { // The initialization of binding manager will try establishing connection with unicast peers // so it requires the Server instance to be correctly initialized. Post the init function to // the event queue so that everything is ready when initialization is conducted. chip::DeviceLayer::PlatformMgr().ScheduleWork(InitBindingHandlerInternal); #if defined(ENABLE_CHIP_SHELL) RegisterSwitchCommands(); #endif return CHIP_NO_ERROR; }