| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2019 Google LLC. |
| * 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. |
| */ |
| |
| /********************************************************** |
| * Includes |
| *********************************************************/ |
| |
| #include "AppTask.h" |
| #include "AppConfig.h" |
| #include "AppEvent.h" |
| #include "CustomerAppTask.h" |
| #include "LEDWidget.h" |
| |
| #if defined(ENABLE_CHIP_SHELL) |
| #include "ShellCommands.h" |
| #endif // defined(ENABLE_CHIP_SHELL) |
| |
| #if SL_MATTER_DISPLAY_ENABLED |
| #include "lcd.h" |
| #if SL_MATTER_QR_CODE_ENABLED |
| #include "qrcodegen.h" |
| #endif // SL_MATTER_QR_CODE_ENABLED |
| #endif // SL_MATTER_DISPLAY_ENABLED |
| |
| #include <app-common/zap-generated/attributes/Accessors.h> |
| #include <app-common/zap-generated/cluster-objects.h> |
| #include <app-common/zap-generated/ids/Attributes.h> |
| #include <app-common/zap-generated/ids/Clusters.h> |
| #include <app/CommandSender.h> |
| #include <app/ConcreteAttributePath.h> |
| #include <app/clusters/bindings/BindingManager.h> |
| #include <app/clusters/switch-server/switch-server.h> |
| #include <app/server/Server.h> |
| #include <app/util/attribute-storage.h> |
| #include <assert.h> |
| #include <controller/InvokeInteraction.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <platform/CHIPDeviceLayer.h> |
| #include <platform/silabs/platformAbstraction/SilabsPlatform.h> |
| #include <setup_payload/OnboardingCodesUtil.h> |
| #include <setup_payload/QRCodeSetupPayloadGenerator.h> |
| #include <setup_payload/SetupPayload.h> |
| |
| /********************************************************** |
| * Defines and Constants |
| *********************************************************/ |
| |
| #define SYSTEM_STATE_LED &sl_led_led0 |
| |
| namespace { |
| constexpr chip::EndpointId kLightSwitchEndpoint = 1; |
| constexpr chip::EndpointId kGenericSwitchEndpoint = 2; |
| |
| constexpr chip::app::Clusters::LevelControl::Commands::Step::Type kStepCommand = { |
| .stepSize = 1, .transitionTime = 0, .optionsMask = 0, .optionsOverride = 0 |
| }; |
| } // namespace |
| |
| using namespace chip; |
| using namespace chip::app; |
| using namespace chip::app::Clusters; |
| using namespace chip::app::Clusters::LevelControl; |
| using namespace ::chip::DeviceLayer; |
| using namespace ::chip::DeviceLayer::Silabs; |
| |
| using namespace chip::TLV; |
| |
| namespace { |
| |
| CustomerAppTask & AppInstance() |
| { |
| return CustomerAppTask::GetAppTask(); |
| } |
| |
| using StepModeEnum = chip::app::Clusters::LevelControl::StepModeEnum; |
| |
| chip::EndpointId gLightSwitchEndpoint = chip::kInvalidEndpointId; |
| chip::EndpointId gGenericSwitchEndpoint = chip::kInvalidEndpointId; |
| StepModeEnum gStepDirection = StepModeEnum::kUp; |
| |
| bool sFunctionButtonPressed = false; |
| bool sActionButtonPressed = false; |
| bool sActionButtonSuppressed = false; |
| bool sIsButtonEventTriggered = false; |
| |
| osTimerId_t sLongPressTimer = nullptr; |
| |
| void PostLevelControlActionEvent(void * /* context */) |
| { |
| AppEvent event; |
| event.Handler = &CustomerAppTask::AppEventHandler; |
| if (sActionButtonPressed) |
| { |
| sActionButtonSuppressed = true; |
| event.Type = AppEvent::kEventType_TriggerLevelControlAction; |
| AppInstance().PostEvent(&event); |
| } |
| } |
| |
| void LightSwitchContextReleaseHandler(void * context) |
| { |
| VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "LightSwitchContextReleaseHandler: context is null")); |
| Platform::Delete(static_cast<BindingCommandData *>(context)); |
| } |
| |
| } // namespace |
| |
| /********************************************************** |
| * AppTask Definitions |
| *********************************************************/ |
| |
| CHIP_ERROR AppTask::AppInit() |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| chip::DeviceLayer::Silabs::GetPlatform().SetButtonsCb(&CustomerAppTask::ButtonEventHandler); |
| |
| err = AppInstance().InitLightSwitch(kLightSwitchEndpoint, kGenericSwitchEndpoint); |
| if (err != CHIP_NO_ERROR) |
| { |
| SILABS_LOG("InitLightSwitch failed!"); |
| appError(err); |
| } |
| |
| sLongPressTimer = osTimerNew(PostLevelControlActionEvent, osTimerOnce, nullptr, NULL); |
| if (sLongPressTimer == NULL) |
| { |
| SILABS_LOG("Timer create failed"); |
| appError(APP_ERROR_CREATE_TIMER_FAILED); |
| } |
| |
| return err; |
| } |
| |
| CHIP_ERROR AppTask::StartAppTask() |
| { |
| return BaseApplication::StartAppTask(AppTaskMain); |
| } |
| |
| void AppTask::AppTaskMain(void * pvParameter) |
| { |
| AppEvent event; |
| osMessageQueueId_t sAppEventQueue = *(static_cast<osMessageQueueId_t *>(pvParameter)); |
| |
| CHIP_ERROR err = AppInstance().Init(); |
| if (err != CHIP_NO_ERROR) |
| { |
| SILABS_LOG("AppTask.Init() failed"); |
| appError(err); |
| } |
| |
| #if !(defined(CHIP_CONFIG_ENABLE_ICD_SERVER) && CHIP_CONFIG_ENABLE_ICD_SERVER) |
| AppInstance().StartStatusLEDTimer(); |
| #endif |
| |
| SILABS_LOG("App Task started"); |
| while (true) |
| { |
| osStatus_t eventReceived = osMessageQueueGet(sAppEventQueue, &event, NULL, osWaitForever); |
| while (eventReceived == osOK) |
| { |
| AppInstance().DispatchEvent(&event); |
| eventReceived = osMessageQueueGet(sAppEventQueue, &event, NULL, 0); |
| } |
| } |
| } |
| |
| void AppTask::ButtonEventHandler(uint8_t button, uint8_t btnAction) |
| { |
| AppEvent event = {}; |
| event.Handler = &CustomerAppTask::AppEventHandler; |
| if (btnAction == to_underlying(SilabsPlatform::ButtonAction::ButtonPressed)) |
| { |
| event.Type = (button ? AppEvent::kEventType_ActionButtonPressed : AppEvent::kEventType_FunctionButtonPressed); |
| } |
| else |
| { |
| event.Type = (button ? AppEvent::kEventType_ActionButtonReleased : AppEvent::kEventType_FunctionButtonReleased); |
| } |
| AppInstance().PostEvent(&event); |
| } |
| |
| void AppTask::AppEventHandler(AppEvent * aEvent) |
| { |
| switch (aEvent->Type) |
| { |
| case AppEvent::kEventType_FunctionButtonPressed: |
| sFunctionButtonPressed = true; |
| if (sActionButtonPressed) |
| { |
| sActionButtonSuppressed = true; |
| gStepDirection = (gStepDirection == StepModeEnum::kUp) ? StepModeEnum::kDown : StepModeEnum::kUp; |
| ChipLogProgress(AppServer, "Step direction changed. Current Step Direction : %s", |
| ((gStepDirection == StepModeEnum::kUp) ? "kUp" : "kDown")); |
| } |
| else |
| { |
| sIsButtonEventTriggered = true; |
| // Post button press event to BaseApplication |
| AppEvent button_event = {}; |
| button_event.Type = AppEvent::kEventType_Button; |
| button_event.ButtonEvent.Action = static_cast<uint8_t>(SilabsPlatform::ButtonAction::ButtonPressed); |
| button_event.Handler = BaseApplication::ButtonHandler; |
| AppInstance().PostEvent(&button_event); |
| } |
| break; |
| |
| case AppEvent::kEventType_FunctionButtonReleased: |
| sFunctionButtonPressed = false; |
| if (sIsButtonEventTriggered) |
| { |
| sIsButtonEventTriggered = false; |
| // Post button release event to BaseApplication |
| AppEvent button_event = {}; |
| button_event.Type = AppEvent::kEventType_Button; |
| button_event.ButtonEvent.Action = static_cast<uint8_t>(SilabsPlatform::ButtonAction::ButtonReleased); |
| button_event.Handler = BaseApplication::ButtonHandler; |
| AppInstance().PostEvent(&button_event); |
| } |
| break; |
| |
| case AppEvent::kEventType_ActionButtonPressed: |
| sActionButtonPressed = true; |
| { |
| auto * switchData = Platform::New<GenericSwitchEventData>(); |
| if (switchData != nullptr) |
| { |
| switchData->endpoint = gGenericSwitchEndpoint; |
| switchData->event = Switch::Events::InitialPress::Id; |
| if (DeviceLayer::PlatformMgr().ScheduleWork(AppTask::GenericSwitchWorkerFunction, |
| reinterpret_cast<intptr_t>(switchData)) != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "Failed to schedule generic switch work"); |
| Platform::Delete(switchData); |
| } |
| } |
| else |
| { |
| ChipLogError(AppServer, "GenericSwitchEventData allocation failed"); |
| } |
| } |
| if (sFunctionButtonPressed) |
| { |
| sActionButtonSuppressed = true; |
| gStepDirection = (gStepDirection == StepModeEnum::kUp) ? StepModeEnum::kDown : StepModeEnum::kUp; |
| ChipLogProgress(AppServer, "Step direction changed. Current Step Direction : %s", |
| ((gStepDirection == StepModeEnum::kUp) ? "kUp" : "kDown")); |
| } |
| else if (sLongPressTimer) |
| { |
| osStatus_t status = osTimerStart(sLongPressTimer, pdMS_TO_TICKS(LONG_PRESS_TIMEOUT_MS)); |
| if (status != osOK) |
| { |
| SILABS_LOG("Timer start() failed with error code : %lx", status); |
| appError(APP_ERROR_START_TIMER_FAILED); |
| } |
| } |
| break; |
| |
| case AppEvent::kEventType_ActionButtonReleased: |
| sActionButtonPressed = false; |
| if (sLongPressTimer) |
| { |
| if (osTimerStop(sLongPressTimer) == osError) |
| { |
| SILABS_LOG("Timer stop() failed"); |
| appError(APP_ERROR_STOP_TIMER_FAILED); |
| } |
| } |
| if (sActionButtonSuppressed) |
| { |
| sActionButtonSuppressed = false; |
| } |
| else |
| { |
| BindingCommandData * toggleData = Platform::New<BindingCommandData>(); |
| // Toggle allocation failure cannot skip ShortRelease handling |
| if (toggleData != nullptr) |
| { |
| toggleData->localEndpointId = gLightSwitchEndpoint; |
| toggleData->clusterId = Clusters::OnOff::Id; |
| toggleData->isGroup = false; |
| toggleData->commandId = OnOff::Commands::Toggle::Id; |
| if (DeviceLayer::PlatformMgr().ScheduleWork(AppTask::SwitchWorkerFunction, |
| reinterpret_cast<intptr_t>(toggleData)) != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "Failed to schedule switch worker"); |
| Platform::Delete(toggleData); |
| } |
| } |
| else |
| { |
| ChipLogError(AppServer, "BindingCommandData allocation failed"); |
| } |
| } |
| { |
| auto * switchData = Platform::New<GenericSwitchEventData>(); |
| if (switchData != nullptr) |
| { |
| switchData->endpoint = gGenericSwitchEndpoint; |
| switchData->event = Switch::Events::ShortRelease::Id; |
| if (DeviceLayer::PlatformMgr().ScheduleWork(AppTask::GenericSwitchWorkerFunction, |
| reinterpret_cast<intptr_t>(switchData)) != CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "Failed to schedule generic switch work"); |
| Platform::Delete(switchData); |
| } |
| } |
| else |
| { |
| ChipLogError(AppServer, "GenericSwitchEventData allocation failed"); |
| } |
| } |
| break; |
| |
| case AppEvent::kEventType_TriggerLevelControlAction: { |
| BindingCommandData * data = Platform::New<BindingCommandData>(); |
| VerifyOrReturn(data != nullptr, ChipLogError(AppServer, "BindingCommandData allocation failed")); |
| data->localEndpointId = gLightSwitchEndpoint; |
| data->clusterId = Clusters::LevelControl::Id; |
| data->isGroup = false; |
| data->commandId = LevelControl::Commands::StepWithOnOff::Id; |
| BindingCommandData::Step stepData{ .stepMode = gStepDirection, |
| .stepSize = kStepCommand.stepSize, |
| .transitionTime = kStepCommand.transitionTime }; |
| stepData.optionsMask.Set(kStepCommand.optionsMask); |
| stepData.optionsOverride.Set(kStepCommand.optionsOverride); |
| data->commandData = stepData; |
| if (DeviceLayer::PlatformMgr().ScheduleWork(AppTask::SwitchWorkerFunction, reinterpret_cast<intptr_t>(data)) != |
| CHIP_NO_ERROR) |
| { |
| ChipLogError(AppServer, "Failed to schedule switch worker"); |
| Platform::Delete(data); |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| CHIP_ERROR AppTask::InitLightSwitch(EndpointId lightSwitchEndpoint, EndpointId genericSwitchEndpoint) |
| { |
| VerifyOrReturnError(lightSwitchEndpoint != kInvalidEndpointId, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(genericSwitchEndpoint != kInvalidEndpointId, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| gLightSwitchEndpoint = lightSwitchEndpoint; |
| gGenericSwitchEndpoint = genericSwitchEndpoint; |
| |
| CHIP_ERROR scheduleErr = chip::DeviceLayer::PlatformMgr().ScheduleWork(&CustomerAppTask::InitBindingHandler, 0); |
| if (scheduleErr != CHIP_NO_ERROR) |
| { |
| SILABS_LOG("InitBindingHandler() failed! Error: %" CHIP_ERROR_FORMAT, scheduleErr.Format()); |
| return scheduleErr; |
| } |
| |
| #if defined(ENABLE_CHIP_SHELL) |
| LightSwitchCommands::RegisterSwitchCommands(); |
| #endif // defined(ENABLE_CHIP_SHELL) |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void AppTask::GenericSwitchWorkerFunction(intptr_t context) |
| { |
| VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "GenericSwitchWorkerFunction - Invalid work")); |
| |
| GenericSwitchEventData * data = reinterpret_cast<GenericSwitchEventData *>(context); |
| auto switchCluster = Clusters::Switch::FindClusterOnEndpoint(data->endpoint); |
| |
| if (switchCluster != nullptr) |
| { |
| switch (data->event) |
| { |
| case Switch::Events::InitialPress::Id: { |
| const uint8_t currentPosition = 1; |
| if (switchCluster->SetCurrentPosition(currentPosition) == CHIP_NO_ERROR) |
| { |
| RETURN_SAFELY_IGNORED switchCluster->OnInitialPress(currentPosition); |
| } |
| break; |
| } |
| case Switch::Events::ShortRelease::Id: { |
| const uint8_t previousPosition = 1; |
| const uint8_t currentPosition = 0; |
| if (switchCluster->SetCurrentPosition(currentPosition) == CHIP_NO_ERROR) |
| { |
| RETURN_SAFELY_IGNORED switchCluster->OnShortRelease(previousPosition); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| Platform::Delete(data); |
| } |
| |
| void AppTask::ProcessOnOffBindingCommand(CommandId commandId, const Binding::TableEntry & binding, |
| OperationalDeviceProxy * 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()); |
| }; |
| |
| if (peer_device != nullptr) |
| { |
| VerifyOrDie(peer_device->ConnectionReady()); |
| Messaging::ExchangeManager * exchangeMgr = peer_device->GetExchangeManager(); |
| auto optionalSession = peer_device->GetSecureSession(); |
| const SessionHandle & sessionHandle = optionalSession.Value(); |
| |
| switch (commandId) |
| { |
| case Clusters::OnOff::Commands::Toggle::Id: { |
| Clusters::OnOff::Commands::Toggle::Type toggleCommand; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, toggleCommand, |
| onSuccess, onFailure); |
| break; |
| } |
| case Clusters::OnOff::Commands::On::Id: { |
| Clusters::OnOff::Commands::On::Type onCommand; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, onCommand, onSuccess, |
| onFailure); |
| break; |
| } |
| case Clusters::OnOff::Commands::Off::Id: { |
| Clusters::OnOff::Commands::Off::Type offCommand; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(exchangeMgr, sessionHandle, binding.remote, offCommand, |
| onSuccess, onFailure); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| else |
| { |
| Messaging::ExchangeManager & exchangeMgr = Server::GetInstance().GetExchangeManager(); |
| |
| switch (commandId) |
| { |
| case Clusters::OnOff::Commands::Toggle::Id: { |
| Clusters::OnOff::Commands::Toggle::Type toggleCommand; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| toggleCommand); |
| break; |
| } |
| case Clusters::OnOff::Commands::On::Id: { |
| Clusters::OnOff::Commands::On::Type onCommand; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| onCommand); |
| break; |
| } |
| case Clusters::OnOff::Commands::Off::Id: { |
| Clusters::OnOff::Commands::Off::Type offCommand; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| offCommand); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| void AppTask::ProcessLevelControlBindingCommand(BindingCommandData * data, const Binding::TableEntry & binding, |
| OperationalDeviceProxy * peer_device) |
| { |
| auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { |
| ChipLogProgress(NotSpecified, "LevelControl command succeeds"); |
| }; |
| |
| auto onFailure = [](CHIP_ERROR error) { |
| ChipLogError(NotSpecified, "LevelControl command failed: %" CHIP_ERROR_FORMAT, error.Format()); |
| }; |
| |
| if (peer_device != nullptr) |
| { |
| VerifyOrDie(peer_device->ConnectionReady()); |
| |
| switch (data->commandId) |
| { |
| case Clusters::LevelControl::Commands::MoveToLevel::Id: { |
| Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; |
| if (auto moveToLevel = std::get_if<BindingCommandData::MoveToLevel>(&data->commandData)) |
| { |
| moveToLevelCommand.level = moveToLevel->level; |
| moveToLevelCommand.transitionTime = moveToLevel->transitionTime; |
| moveToLevelCommand.optionsMask = moveToLevel->optionsMask; |
| moveToLevelCommand.optionsOverride = moveToLevel->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| moveToLevelCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::Move::Id: { |
| Clusters::LevelControl::Commands::Move::Type moveCommand; |
| if (auto move = std::get_if<BindingCommandData::Move>(&data->commandData)) |
| { |
| moveCommand.moveMode = move->moveMode; |
| moveCommand.rate = move->rate; |
| moveCommand.optionsMask = move->optionsMask; |
| moveCommand.optionsOverride = move->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| moveCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::Step::Id: { |
| Clusters::LevelControl::Commands::Step::Type stepCommand; |
| if (auto step = std::get_if<BindingCommandData::Step>(&data->commandData)) |
| { |
| stepCommand.stepMode = step->stepMode; |
| stepCommand.stepSize = step->stepSize; |
| stepCommand.transitionTime = step->transitionTime; |
| stepCommand.optionsMask = step->optionsMask; |
| stepCommand.optionsOverride = step->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| stepCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::Stop::Id: { |
| Clusters::LevelControl::Commands::Stop::Type stopCommand; |
| if (auto stop = std::get_if<BindingCommandData::Stop>(&data->commandData)) |
| { |
| stopCommand.optionsMask = stop->optionsMask; |
| stopCommand.optionsOverride = stop->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| stopCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::MoveToLevelWithOnOff::Id: { |
| Clusters::LevelControl::Commands::MoveToLevelWithOnOff::Type moveToLevelWithOnOffCommand; |
| if (auto moveToLevel = std::get_if<BindingCommandData::MoveToLevel>(&data->commandData)) |
| { |
| moveToLevelWithOnOffCommand.level = moveToLevel->level; |
| moveToLevelWithOnOffCommand.transitionTime = moveToLevel->transitionTime; |
| moveToLevelWithOnOffCommand.optionsMask = moveToLevel->optionsMask; |
| moveToLevelWithOnOffCommand.optionsOverride = moveToLevel->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| moveToLevelWithOnOffCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::MoveWithOnOff::Id: { |
| Clusters::LevelControl::Commands::MoveWithOnOff::Type moveWithOnOffCommand; |
| if (auto move = std::get_if<BindingCommandData::Move>(&data->commandData)) |
| { |
| moveWithOnOffCommand.moveMode = move->moveMode; |
| moveWithOnOffCommand.rate = move->rate; |
| moveWithOnOffCommand.optionsMask = move->optionsMask; |
| moveWithOnOffCommand.optionsOverride = move->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| moveWithOnOffCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::StepWithOnOff::Id: { |
| Clusters::LevelControl::Commands::StepWithOnOff::Type stepWithOnOffCommand; |
| if (auto step = std::get_if<BindingCommandData::Step>(&data->commandData)) |
| { |
| stepWithOnOffCommand.stepMode = step->stepMode; |
| stepWithOnOffCommand.stepSize = step->stepSize; |
| stepWithOnOffCommand.transitionTime = step->transitionTime; |
| stepWithOnOffCommand.optionsMask = step->optionsMask; |
| stepWithOnOffCommand.optionsOverride = step->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| stepWithOnOffCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::StopWithOnOff::Id: { |
| Clusters::LevelControl::Commands::StopWithOnOff::Type stopWithOnOffCommand; |
| if (auto stop = std::get_if<BindingCommandData::Stop>(&data->commandData)) |
| { |
| stopWithOnOffCommand.optionsMask = stop->optionsMask; |
| stopWithOnOffCommand.optionsOverride = stop->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeCommandRequest(peer_device->GetExchangeManager(), |
| peer_device->GetSecureSession().Value(), binding.remote, |
| stopWithOnOffCommand, onSuccess, onFailure); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| else |
| { |
| Messaging::ExchangeManager & exchangeMgr = Server::GetInstance().GetExchangeManager(); |
| |
| switch (data->commandId) |
| { |
| case Clusters::LevelControl::Commands::MoveToLevel::Id: { |
| Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; |
| if (auto moveToLevel = std::get_if<BindingCommandData::MoveToLevel>(&data->commandData)) |
| { |
| moveToLevelCommand.level = moveToLevel->level; |
| moveToLevelCommand.transitionTime = moveToLevel->transitionTime; |
| moveToLevelCommand.optionsMask = moveToLevel->optionsMask; |
| moveToLevelCommand.optionsOverride = moveToLevel->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| moveToLevelCommand); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::Move::Id: { |
| Clusters::LevelControl::Commands::Move::Type moveCommand; |
| if (auto move = std::get_if<BindingCommandData::Move>(&data->commandData)) |
| { |
| moveCommand.moveMode = move->moveMode; |
| moveCommand.rate = move->rate; |
| moveCommand.optionsMask = move->optionsMask; |
| moveCommand.optionsOverride = move->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| moveCommand); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::Step::Id: { |
| Clusters::LevelControl::Commands::Step::Type stepCommand; |
| if (auto step = std::get_if<BindingCommandData::Step>(&data->commandData)) |
| { |
| stepCommand.stepMode = step->stepMode; |
| stepCommand.stepSize = step->stepSize; |
| stepCommand.transitionTime = step->transitionTime; |
| stepCommand.optionsMask = step->optionsMask; |
| stepCommand.optionsOverride = step->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| stepCommand); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::Stop::Id: { |
| Clusters::LevelControl::Commands::Stop::Type stopCommand; |
| if (auto stop = std::get_if<BindingCommandData::Stop>(&data->commandData)) |
| { |
| stopCommand.optionsMask = stop->optionsMask; |
| stopCommand.optionsOverride = stop->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| stopCommand); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::MoveToLevelWithOnOff::Id: { |
| Clusters::LevelControl::Commands::MoveToLevelWithOnOff::Type moveToLevelWithOnOffCommand; |
| if (auto moveToLevel = std::get_if<BindingCommandData::MoveToLevel>(&data->commandData)) |
| { |
| moveToLevelWithOnOffCommand.level = moveToLevel->level; |
| moveToLevelWithOnOffCommand.transitionTime = moveToLevel->transitionTime; |
| moveToLevelWithOnOffCommand.optionsMask = moveToLevel->optionsMask; |
| moveToLevelWithOnOffCommand.optionsOverride = moveToLevel->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| moveToLevelWithOnOffCommand); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::MoveWithOnOff::Id: { |
| Clusters::LevelControl::Commands::MoveWithOnOff::Type moveWithOnOffCommand; |
| if (auto move = std::get_if<BindingCommandData::Move>(&data->commandData)) |
| { |
| moveWithOnOffCommand.moveMode = move->moveMode; |
| moveWithOnOffCommand.rate = move->rate; |
| moveWithOnOffCommand.optionsMask = move->optionsMask; |
| moveWithOnOffCommand.optionsOverride = move->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| moveWithOnOffCommand); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::StepWithOnOff::Id: { |
| Clusters::LevelControl::Commands::StepWithOnOff::Type stepWithOnOffCommand; |
| if (auto step = std::get_if<BindingCommandData::Step>(&data->commandData)) |
| { |
| stepWithOnOffCommand.stepMode = step->stepMode; |
| stepWithOnOffCommand.stepSize = step->stepSize; |
| stepWithOnOffCommand.transitionTime = step->transitionTime; |
| stepWithOnOffCommand.optionsMask = step->optionsMask; |
| stepWithOnOffCommand.optionsOverride = step->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| stepWithOnOffCommand); |
| } |
| break; |
| } |
| case Clusters::LevelControl::Commands::StopWithOnOff::Id: { |
| Clusters::LevelControl::Commands::StopWithOnOff::Type stopWithOnOffCommand; |
| if (auto stop = std::get_if<BindingCommandData::Stop>(&data->commandData)) |
| { |
| stopWithOnOffCommand.optionsMask = stop->optionsMask; |
| stopWithOnOffCommand.optionsOverride = stop->optionsOverride; |
| RETURN_SAFELY_IGNORED Controller::InvokeGroupCommandRequest(&exchangeMgr, binding.fabricIndex, binding.groupId, |
| stopWithOnOffCommand); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| void AppTask::LightSwitchChangedHandler(const Binding::TableEntry & binding, OperationalDeviceProxy * peer_device, void * context) |
| { |
| VerifyOrReturn(context != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectedFn: context is null")); |
| BindingCommandData * data = static_cast<BindingCommandData *>(context); |
| |
| const bool isMulticast = binding.type == Binding::MATTER_MULTICAST_BINDING && data->isGroup; |
| const bool isUnicast = binding.type == Binding::MATTER_UNICAST_BINDING && !data->isGroup; |
| |
| VerifyOrReturn(isMulticast || isUnicast); |
| |
| OperationalDeviceProxy * device = nullptr; |
| if (isUnicast) |
| { |
| VerifyOrDie(peer_device != nullptr && peer_device->ConnectionReady()); |
| device = peer_device; |
| } |
| |
| switch (data->clusterId) |
| { |
| case Clusters::OnOff::Id: |
| ProcessOnOffBindingCommand(data->commandId, binding, device); |
| break; |
| case Clusters::LevelControl::Id: |
| ProcessLevelControlBindingCommand(data, binding, device); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void AppTask::InitBindingHandler(intptr_t arg) |
| { |
| (void) arg; |
| |
| auto & server = chip::Server::GetInstance(); |
| LogErrorOnFailure(Binding::Manager::GetInstance().Init( |
| { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() })); |
| Binding::Manager::GetInstance().RegisterBoundDeviceChangedHandler(&CustomerAppTask::LightSwitchChangedHandler); |
| Binding::Manager::GetInstance().RegisterBoundDeviceContextReleaseHandler(LightSwitchContextReleaseHandler); |
| } |
| |
| void AppTask::SwitchWorkerFunction(intptr_t context) |
| { |
| VerifyOrReturn(context != 0, ChipLogError(NotSpecified, "SwitchWorkerFunction - Invalid work data")); |
| |
| BindingCommandData * data = reinterpret_cast<BindingCommandData *>(context); |
| CHIP_ERROR err = Binding::Manager::GetInstance().NotifyBoundClusterChanged(data->localEndpointId, data->clusterId, |
| static_cast<void *>(data)); |
| |
| if (err == CHIP_ERROR_INCORRECT_STATE || err == CHIP_ERROR_HANDLER_NOT_SET || err == CHIP_ERROR_NO_MEMORY) |
| { |
| Platform::Delete(data); |
| } |
| } |
| |
| void AppTask::DMPostAttributeChangeCallback(const ConcreteAttributePath & attributePath, uint8_t type, uint16_t size, |
| uint8_t * value) |
| { |
| ClusterId clusterId = attributePath.mClusterId; |
| [[maybe_unused]] AttributeId attributeId = attributePath.mAttributeId; |
| ChipLogDetail(Zcl, "Cluster callback: " ChipLogFormatMEI, ChipLogValueMEI(clusterId)); |
| |
| if (clusterId == Clusters::Identify::Id) |
| { |
| ChipLogProgress(Zcl, "Identify attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u", |
| ChipLogValueMEI(attributeId), type, *value, size); |
| } |
| } |