diff --git a/src/app/clusters/level-control/level-control.cpp b/src/app/clusters/level-control/level-control.cpp index ac436931daabf1..b61d2c34156570 100644 --- a/src/app/clusters/level-control/level-control.cpp +++ b/src/app/clusters/level-control/level-control.cpp @@ -123,6 +123,137 @@ static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId, chip::Optional> optionsMask, chip::Optional> optionsOverride); +#ifdef EMBER_AF_PLUGIN_SCENES +class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl +{ +public: + // As per spec, 2 attributes are scenable in the level control cluster + static constexpr uint8_t kLevelMaxScenableAttributes = 2; + + DefaultLevelControlSceneHandler() = default; + ~DefaultLevelControlSceneHandler() override {} + + // Default function for LevelControl cluster, only puts the LevelControl cluster ID in the span if supported on the caller + // endpoint + virtual void GetSupportedClusters(EndpointId endpoint, Span & clusterBuffer) override + { + if (emberAfContainsServer(endpoint, LevelControl::Id) && clusterBuffer.size() >= 1) + { + clusterBuffer[0] = LevelControl::Id; + clusterBuffer.reduce_size(1); + } + else + { + clusterBuffer.reduce_size(0); + } + } + + // Default function for LevelControl cluster, only checks if LevelControl is enabled on the endpoint + bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override + { + return (cluster == LevelControl::Id) && (emberAfContainsServer(endpoint, LevelControl::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 = Scenes::Structs::AttributeValuePair::Type; + + app::DataModel::Nullable level; + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::CurrentLevel::Get(endpoint, level), CHIP_ERROR_READ_FAILED); + + AttributeValuePair pairs[kLevelMaxScenableAttributes]; + + uint8_t maxLevel; + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::MaxLevel::Get(endpoint, &maxLevel), CHIP_ERROR_READ_FAILED); + + pairs[0].attributeID = Attributes::CurrentLevel::Id; + pairs[0].attributeValue = (!level.IsNull()) ? level.Value() : maxLevel + 1; + size_t attributeCount = 1; + if (LevelControlHasFeature(endpoint, LevelControl::Feature::kFrequency)) + { + uint16_t frequency; + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::CurrentFrequency::Get(endpoint, &frequency), + CHIP_ERROR_READ_FAILED); + pairs[attributeCount].attributeID = Attributes::CurrentFrequency::Id; + pairs[attributeCount].attributeValue = frequency; + attributeCount++; + } + + app::DataModel::List attributeValueList(pairs, attributeCount); + + 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 attributeValueList; + + ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList)); + + size_t attributeCount = 0; + ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount)); + VerifyOrReturnError(attributeCount <= kLevelMaxScenableAttributes, CHIP_ERROR_BUFFER_TOO_SMALL); + + auto pair_iterator = attributeValueList.begin(); + + // The level control cluster should have a maximum of 2 attributes + uint8_t level = 0; + // TODO : Uncomment when frequency is supported by the level control cluster + // uint16_t frequency; + while (pair_iterator.Next()) + { + auto & decodePair = pair_iterator.GetValue(); + + // If attribute ID was encoded, checks which attribute from LC cluster is there + switch (decodePair.attributeID) + { + case Attributes::CurrentLevel::Id: + level = static_cast(decodePair.attributeValue); + break; + case Attributes::CurrentFrequency::Id: + // TODO : Uncomment when frequency is supported by the level control cluster + // frequency = static_cast(decodePair.attributeValue); + break; + default: + return CHIP_ERROR_INVALID_ARGUMENT; + } + } + ReturnErrorOnFailure(pair_iterator.GetStatus()); + + // TODO : Implement action on frequency when frequency not provisional anymore + // if(LevelControlHasFeature(endpoint, LevelControl::Feature::kFrequency)){} + Status status; + CommandId command = LevelControlHasFeature(endpoint, LevelControl::Feature::kOnOff) ? Commands::MoveToLevelWithOnOff::Id + : Commands::MoveToLevel::Id; + + status = moveToLevelHandler( + endpoint, command, level, app::DataModel::MakeNullable(static_cast(timeMs / 100)), + chip::Optional>(), chip::Optional>(), INVALID_STORED_LEVEL); + + if (status != Status::Success) + { + return CHIP_ERROR_READ_FAILED; + } + + return CHIP_NO_ERROR; + } +}; +static DefaultLevelControlSceneHandler sLevelControlSceneHandler; + +#endif // EMBER_AF_PLUGIN_SCENES + #if !defined(IGNORE_LEVEL_CONTROL_CLUSTER_OPTIONS) && defined(EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP) static void reallyUpdateCoupledColorTemp(EndpointId endpoint); #define updateCoupledColorTemp(endpoint) reallyUpdateCoupledColorTemp(endpoint) @@ -446,7 +577,7 @@ static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId, } #endif // IGNORE_LEVEL_CONTROL_CLUSTER_OPTIONS - // By default, we return true to continue supporting backwards compatibility. + // By default, we return true to continue supporting backwards compatibility. return true; } @@ -482,6 +613,16 @@ Status MoveToLevel(EndpointId endpointId, const Commands::MoveToLevel::Decodable Optional>(optionsOverride), INVALID_STORED_LEVEL); // Don't revert to the stored level } + +chip::scenes::SceneHandler * GetSceneHandler() +{ +#ifdef EMBER_AF_PLUGIN_SCENES + return &sLevelControlSceneHandler; +#else + return nullptr; +#endif // EMBER_AF_PLUGIN_SCENES +} + } // namespace LevelControlServer bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(app::CommandHandler * commandObj, @@ -1304,6 +1445,11 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint) } } +#ifdef EMBER_AF_PLUGIN_SCENES + // Registers Scene handlers for the level control cluster on the server + app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpoint, LevelControlServer::GetSceneHandler()); +#endif + emberAfPluginLevelControlClusterServerPostInitCallback(endpoint); } diff --git a/src/app/clusters/level-control/level-control.h b/src/app/clusters/level-control/level-control.h index 402c30de3048d5..ba4bea7816752b 100644 --- a/src/app/clusters/level-control/level-control.h +++ b/src/app/clusters/level-control/level-control.h @@ -28,6 +28,7 @@ #include #include +#include #include /** @brief Level Control Cluster Server Post Init @@ -52,4 +53,6 @@ chip::Protocols::InteractionModel::Status MoveToLevel(chip::EndpointId endpointId, const chip::app::Clusters::LevelControl::Commands::MoveToLevel::DecodableType & commandData); +chip::scenes::SceneHandler * GetSceneHandler(); + } // namespace LevelControlServer diff --git a/src/app/clusters/on-off-server/on-off-server.cpp b/src/app/clusters/on-off-server/on-off-server.cpp index 440ea0a471fa30..16a7e60664d658 100644 --- a/src/app/clusters/on-off-server/on-off-server.cpp +++ b/src/app/clusters/on-off-server/on-off-server.cpp @@ -289,7 +289,13 @@ class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl return err; } - OnOffServer::Instance().scheduleTimerCallbackMs(sceneEventControl(endpoint), timeMs); +#ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL + if (!(LevelControlWithOnOffFeaturePresent(endpoint) && + Scenes::ScenesServer::Instance().IsHandlerRegistered(endpoint, LevelControlServer::GetSceneHandler()))) +#endif + { + OnOffServer::Instance().scheduleTimerCallbackMs(sceneEventControl(endpoint), timeMs); + } return CHIP_NO_ERROR; } @@ -592,7 +598,7 @@ void OnOffServer::initOnOffServer(chip::EndpointId endpoint) #ifdef EMBER_AF_PLUGIN_SCENES // Registers Scene handlers for the On/Off cluster on the server - app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(OnOffServer::Instance().GetSceneHandler()); + app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpoint, OnOffServer::Instance().GetSceneHandler()); #endif #ifdef EMBER_AF_PLUGIN_MODE_SELECT diff --git a/src/app/clusters/scenes-server/SceneTableImpl.cpp b/src/app/clusters/scenes-server/SceneTableImpl.cpp index 3ff044e4bd506c..70ad7ab9325cda 100644 --- a/src/app/clusters/scenes-server/SceneTableImpl.cpp +++ b/src/app/clusters/scenes-server/SceneTableImpl.cpp @@ -848,26 +848,22 @@ CHIP_ERROR DefaultSceneTableImpl::SceneSaveEFS(SceneTableEntry & scene) /// @param scene Scene providing the EFSs (extension field sets) CHIP_ERROR DefaultSceneTableImpl::SceneApplyEFS(const SceneTableEntry & scene) { - ExtensionFieldSet EFS; - TransitionTimeMs time; - clusterId cluster; - if (!this->HandlerListEmpty()) { for (uint8_t i = 0; i < scene.mStorageData.mExtensionFieldSets.GetFieldSetCount(); i++) { + ExtensionFieldSet EFS; scene.mStorageData.mExtensionFieldSets.GetFieldSetAtPosition(EFS, i); - cluster = EFS.mID; - time = scene.mStorageData.mSceneTransitionTimeMs; ByteSpan EFSSpan = MutableByteSpan(EFS.mBytesBuffer, EFS.mUsedBytes); if (!EFS.IsEmpty()) { for (auto & handler : mHandlerList) { - if (handler.SupportsCluster(mEndpointId, cluster)) + if (handler.SupportsCluster(mEndpointId, EFS.mID)) { - ReturnErrorOnFailure(handler.ApplyScene(mEndpointId, cluster, EFSSpan, time)); + ReturnErrorOnFailure( + handler.ApplyScene(mEndpointId, EFS.mID, EFSSpan, scene.mStorageData.mSceneTransitionTimeMs)); break; } } diff --git a/src/app/clusters/scenes-server/scenes-server.cpp b/src/app/clusters/scenes-server/scenes-server.cpp index 1c89884f687246..4cfc016e7be038 100644 --- a/src/app/clusters/scenes-server/scenes-server.cpp +++ b/src/app/clusters/scenes-server/scenes-server.cpp @@ -553,27 +553,27 @@ void ScenesServer::RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, Gr } } -bool ScenesServer::IsHandlerRegistered(scenes::SceneHandler * handler) +bool ScenesServer::IsHandlerRegistered(EndpointId aEndpointId, scenes::SceneHandler * handler) { - SceneTable * sceneTable = scenes::GetSceneTableImpl(); + SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId); return sceneTable->mHandlerList.Contains(handler); } -void ScenesServer::RegisterSceneHandler(scenes::SceneHandler * handler) +void ScenesServer::RegisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler) { - SceneTable * sceneTable = scenes::GetSceneTableImpl(); + SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId); - if (!IsHandlerRegistered(handler)) + if (!IsHandlerRegistered(aEndpointId, handler)) { sceneTable->RegisterHandler(handler); } } -void ScenesServer::UnregisterSceneHandler(scenes::SceneHandler * handler) +void ScenesServer::UnregisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler) { - SceneTable * sceneTable = scenes::GetSceneTableImpl(); + SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId); - if (IsHandlerRegistered(handler)) + if (IsHandlerRegistered(aEndpointId, handler)) { sceneTable->UnregisterHandler(handler); } diff --git a/src/app/clusters/scenes-server/scenes-server.h b/src/app/clusters/scenes-server/scenes-server.h index 47eb05db6616f4..a96d1dc29ce3ca 100644 --- a/src/app/clusters/scenes-server/scenes-server.h +++ b/src/app/clusters/scenes-server/scenes-server.h @@ -51,9 +51,9 @@ class ScenesServer : public CommandHandlerInterface, public AttributeAccessInter void StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId); void RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId); - bool IsHandlerRegistered(scenes::SceneHandler * handler); - void RegisterSceneHandler(scenes::SceneHandler * handler); - void UnregisterSceneHandler(scenes::SceneHandler * handler); + bool IsHandlerRegistered(EndpointId aEndpointId, scenes::SceneHandler * handler); + void RegisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler); + void UnregisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler); private: ScenesServer() : CommandHandlerInterface(Optional(), Id), AttributeAccessInterface(Optional(), Id) {}