/* * * Copyright (c) 2022 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 "binding-handler.h" #include "app/ConcreteCommandPath.h" #include "app/clusters/bindings/BindingManager.h" #include "controller/InvokeInteraction.h" #include using namespace chip; using namespace chip::app; static bool sEnabled = false; static void ProcessSwitchUnicastBindingCommand(CommandId commandId, const EmberBindingTableEntry & binding, Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, BindingCommandData * data) { auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { ChipLogProgress(NotSpecified, "Switch command succeeds"); }; auto onFailure = [](CHIP_ERROR error) { ChipLogError(NotSpecified, "Switch command failed: %" CHIP_ERROR_FORMAT, error.Format()); }; switch (data->clusterId) { case Clusters::OnOff::Id: switch (commandId) { case Clusters::OnOff::Commands::Toggle::Id: Clusters::OnOff::Commands::Toggle::Type toggleCommand; Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, toggleCommand, onSuccess, onFailure); break; case Clusters::OnOff::Commands::On::Id: Clusters::OnOff::Commands::On::Type onCommand; Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, onCommand, onSuccess, onFailure); break; case Clusters::OnOff::Commands::Off::Id: Clusters::OnOff::Commands::Off::Type offCommand; Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, offCommand, onSuccess, onFailure); break; default: ChipLogError(NotSpecified, "Unsupported Command Id"); break; } break; case Clusters::LevelControl::Id: if (commandId == Clusters::LevelControl::Commands::MoveToLevel::Id) { Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; moveToLevelCommand.level = data->level; Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, moveToLevelCommand, onSuccess, onFailure); } else { ChipLogError(NotSpecified, "Unsupported Command Id"); } break; case Clusters::ColorControl::Id: if (commandId == Clusters::ColorControl::Commands::MoveToColor::Id) { Clusters::ColorControl::Commands::MoveToColor::Type moveToColorCommand; moveToColorCommand.colorX = data->colorXY.x; moveToColorCommand.colorY = data->colorXY.y; Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, moveToColorCommand, onSuccess, onFailure); } else { ChipLogError(NotSpecified, "Unsupported Command Id"); } break; default: ChipLogError(NotSpecified, "Unsupported Cluster Id"); break; } } static void ProcessSwitchGroupBindingCommand(CommandId commandId, const EmberBindingTableEntry & binding, BindingCommandData * data) { Messaging::ExchangeManager & exchangeMgr = Server::GetInstance().GetExchangeManager(); switch (data->clusterId) { case Clusters::OnOff::Id: 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; default: ChipLogError(NotSpecified, "Unsupported Command Id"); break; } break; case Clusters::LevelControl::Id: if (commandId == Clusters::LevelControl::Commands::MoveToLevel::Id) { Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; moveToLevelCommand.level = data->level; Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, moveToLevelCommand); } else { ChipLogError(NotSpecified, "Unsupported Command Id"); } break; case Clusters::ColorControl::Id: if (commandId == Clusters::ColorControl::Commands::MoveToColor::Id) { Clusters::ColorControl::Commands::MoveToColor::Type moveToColorCommand; moveToColorCommand.colorX = data->colorXY.x; moveToColorCommand.colorY = data->colorXY.y; Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, moveToColorCommand); } else { ChipLogError(NotSpecified, "Unsupported Command Id"); } break; default: ChipLogError(NotSpecified, "Unsupported Cluster Id"); break; } } static void LightSwitchChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device, void * context) { VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "nullptr pointer passed")); BindingCommandData * data = static_cast(context); if (binding.type == MATTER_MULTICAST_BINDING && data->isGroup) { switch (data->clusterId) { case Clusters::OnOff::Id: case Clusters::LevelControl::Id: case Clusters::ColorControl::Id: ProcessSwitchGroupBindingCommand(data->commandId, binding, data); break; } } else if (binding.type == MATTER_UNICAST_BINDING && !data->isGroup) { switch (data->clusterId) { case Clusters::OnOff::Id: case Clusters::LevelControl::Id: case Clusters::ColorControl::Id: // TODO should not happen? VerifyOrReturn(peer_device != nullptr, ChipLogError(NotSpecified, "Peer is nullptr")); ProcessSwitchUnicastBindingCommand(data->commandId, binding, peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), data); break; default: ChipLogError(NotSpecified, "Unsupported Cluster Id"); break; } } } static void LightSwitchContextReleaseHandler(void * context) { VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "nullptr pointer passed")); Platform::Delete(static_cast(context)); } void InitBindingManager(intptr_t context) { auto & server = chip::Server::GetInstance(); BindingManagerInitParams bindingParams = { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }; CHIP_ERROR error = BindingManager::GetInstance().Init(bindingParams); if (error != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "BindingManager initialization failed: %" CHIP_ERROR_FORMAT, error.Format()); return; } BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(LightSwitchChangedHandler); BindingManager::GetInstance().RegisterBoundDeviceContextReleaseHandler(LightSwitchContextReleaseHandler); sEnabled = true; } void SwitchWorkerFunction(intptr_t context) { VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "nullptr pointer passed")); BindingCommandData * data = reinterpret_cast(context); if (!sEnabled) { ChipLogProgress(NotSpecified, "BindingManager not initialized"); Platform::Delete(data); } BindingManager::GetInstance().NotifyBoundClusterChanged(data->localEndpointId, data->clusterId, static_cast(data)); }