| /** |
| * Copyright (c) 2020-2025 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 "zap-generated/gen_config.h" |
| #include <app/clusters/on-off-server/codegen/scenes-integration.h> |
| |
| #ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT |
| |
| #include <app-common/zap-generated/attributes/Accessors.h> |
| #include <app/clusters/on-off-server/on-off-server.h> // nogncheck |
| #include <app/clusters/scenes-server/CodegenAttributeValuePairValidator.h> // nogncheck |
| #include <app/clusters/scenes-server/CodegenEndpointToIndex.h> // nogncheck |
| #include <app/clusters/scenes-server/scenes-server.h> // nogncheck |
| |
| using namespace chip; |
| using namespace chip::app::Clusters; |
| using namespace chip::app::Clusters::OnOff; |
| |
| using chip::Protocols::InteractionModel::Status; |
| |
| static constexpr size_t kOnOffMaxEndpointCount = |
| MATTER_DM_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; |
| |
| static void sceneOnOffCallback(EndpointId endpoint); |
| using OnOffEndPointPair = scenes::DefaultSceneHandlerImpl::EndpointStatePair<bool>; |
| using OnOffTransitionTimeInterface = scenes::DefaultSceneHandlerImpl::TransitionTimeInterface< |
| scenes::CodegenEndpointToIndex<OnOff::Id, MATTER_DM_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT, kOnOffMaxEndpointCount>>; |
| |
| #if CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS |
| |
| class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl |
| { |
| public: |
| DefaultSceneHandlerImpl::StatePairBuffer<bool, kOnOffMaxEndpointCount> mSceneEndpointStatePairs; |
| // As per spec, 1 attribute is scenable in the on off cluster |
| static constexpr uint8_t scenableAttributeCount = 1; |
| |
| DefaultOnOffSceneHandler() : scenes::DefaultSceneHandlerImpl(scenes::CodegenAttributeValuePairValidator::Instance()) {} |
| ~DefaultOnOffSceneHandler() override = default; |
| |
| // Default function for OnOff cluster, only checks if OnOff is enabled on the endpoint |
| bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override |
| { |
| return (cluster == OnOff::Id) && (emberAfContainsServer(endpoint, OnOff::Id)); |
| } |
| |
| /// @brief Serialize the Cluster's EFS value |
| /// @param endpoint target endpoint |
| /// @param cluster target cluster |
| /// @param serializedBytes data to serialize into EFS |
| /// @return CHIP_NO_ERROR if successfully serialized the data, CHIP_ERROR_INVALID_ARGUMENT otherwise |
| CHIP_ERROR SerializeSave(EndpointId endpoint, ClusterId cluster, MutableByteSpan & serializedBytes) override |
| { |
| using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type; |
| |
| bool currentValue; |
| // read current on/off value |
| Status status = Attributes::OnOff::Get(endpoint, ¤tValue); |
| if (status != Status::Success) |
| { |
| ChipLogError(Zcl, "ERR: reading on/off %x", to_underlying(status)); |
| return CHIP_ERROR_READ_FAILED; |
| } |
| |
| AttributeValuePair pairs[scenableAttributeCount]; |
| |
| pairs[0].attributeID = Attributes::OnOff::Id; |
| pairs[0].valueUnsigned8.SetValue(currentValue); |
| |
| app::DataModel::List<AttributeValuePair> attributeValueList(pairs); |
| |
| return EncodeAttributeValueList(attributeValueList, serializedBytes); |
| } |
| |
| /// @brief Default EFS interaction when applying scene to the OnOff Cluster |
| /// @param endpoint target endpoint |
| /// @param cluster target cluster |
| /// @param serializedBytes Data from nvm |
| /// @param timeMs transition time in ms |
| /// @return CHIP_NO_ERROR if value as expected, CHIP_ERROR_INVALID_ARGUMENT otherwise |
| CHIP_ERROR ApplyScene(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes, |
| scenes::TransitionTimeMs timeMs) override |
| { |
| app::DataModel::DecodableList<ScenesManagement::Structs::AttributeValuePairStruct::DecodableType> attributeValueList; |
| |
| VerifyOrReturnError(cluster == OnOff::Id, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList)); |
| |
| size_t attributeCount = 0; |
| ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount)); |
| VerifyOrReturnError(attributeCount <= scenableAttributeCount, CHIP_ERROR_BUFFER_TOO_SMALL); |
| |
| auto pair_iterator = attributeValueList.begin(); |
| while (pair_iterator.Next()) |
| { |
| auto & decodePair = pair_iterator.GetValue(); |
| VerifyOrReturnError(decodePair.attributeID == Attributes::OnOff::Id, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(decodePair.valueUnsigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); |
| ReturnErrorOnFailure(mSceneEndpointStatePairs.InsertPair( |
| OnOffEndPointPair(endpoint, static_cast<bool>(decodePair.valueUnsigned8.Value())))); |
| } |
| // Verify that the EFS was completely read |
| CHIP_ERROR err = pair_iterator.GetStatus(); |
| if (CHIP_NO_ERROR != err) |
| { |
| TEMPORARY_RETURN_IGNORED mSceneEndpointStatePairs.RemovePair(endpoint); |
| return err; |
| } |
| |
| VerifyOrReturnError(mTransitionTimeInterface.sceneEventControl(endpoint) != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| OnOffServer::Instance().scheduleTimerCallbackMs(mTransitionTimeInterface.sceneEventControl(endpoint), timeMs); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| private: |
| OnOffTransitionTimeInterface mTransitionTimeInterface = OnOffTransitionTimeInterface(sceneOnOffCallback); |
| }; |
| static DefaultOnOffSceneHandler sOnOffSceneHandler; |
| |
| static void sceneOnOffCallback(EndpointId endpoint) |
| { |
| OnOffEndPointPair savedState; |
| ReturnOnFailure(sOnOffSceneHandler.mSceneEndpointStatePairs.GetPair(endpoint, savedState)); |
| CommandId command = (savedState.mValue) ? Commands::On::Id : Commands::Off::Id; |
| OnOffServer::Instance().setOnOffValue(endpoint, command, false); |
| ReturnOnFailure(sOnOffSceneHandler.mSceneEndpointStatePairs.RemovePair(endpoint)); |
| } |
| |
| namespace chip::app::Clusters::OnOff::Internal::Scenes { |
| chip::scenes::SceneHandler * GlobalHandler() |
| { |
| return &sOnOffSceneHandler; |
| } |
| |
| void RegisterGlobalHandler(chip::EndpointId endpoint) |
| { |
| app::Clusters::ScenesManagement::ScenesServer::Instance().RegisterSceneHandler(endpoint, GlobalHandler()); |
| } |
| |
| } // namespace chip::app::Clusters::OnOff::Internal::Scenes |
| |
| #endif // CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS |
| |
| namespace chip::app::Clusters::OnOff::Internal::Scenes { |
| |
| void Store(FabricIndex fabricIndex, EndpointId endpoint) |
| { |
| ScenesManagement::ScenesServer::Instance().StoreCurrentScene( |
| fabricIndex, endpoint, ScenesManagement::ScenesServer::kGlobalSceneGroupId, ScenesManagement::ScenesServer::kGlobalSceneId); |
| } |
| |
| void Recall(FabricIndex fabricIndex, EndpointId endpoint) |
| { |
| ScenesManagement::ScenesServer::Instance().RecallScene( |
| fabricIndex, endpoint, ScenesManagement::ScenesServer::kGlobalSceneGroupId, ScenesManagement::ScenesServer::kGlobalSceneId); |
| } |
| |
| void MarkInvalid(EndpointId endpoint) |
| { |
| ScenesManagement::ScenesServer::Instance().MakeSceneInvalidForAllFabrics(endpoint); |
| } |
| |
| } // namespace chip::app::Clusters::OnOff::Internal::Scenes |
| |
| #endif // MATTER_DM_PLUGIN_SCENES_MANAGEMENT |