diff --git a/src/app/clusters/mode-select-server/mode-select-server.cpp b/src/app/clusters/mode-select-server/mode-select-server.cpp index 2c9d88c1872737..009b60939ca0e6 100644 --- a/src/app/clusters/mode-select-server/mode-select-server.cpp +++ b/src/app/clusters/mode-select-server/mode-select-server.cpp @@ -37,6 +37,10 @@ #include #endif // EMBER_AF_PLUGIN_ON_OFF +#ifdef EMBER_AF_PLUGIN_SCENES +#include +#endif // EMBER_AF_PLUGIN_SCENES + using namespace std; using namespace chip; using namespace chip::app; @@ -95,6 +99,118 @@ CHIP_ERROR ModeSelectAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } // anonymous namespace +#ifdef EMBER_AF_PLUGIN_SCENES + +static constexpr size_t kModeSelectMaxEnpointCount = + EMBER_AF_MODE_SELECT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; +class DefaultModeSelectSceneHandler : public scenes::DefaultSceneHandlerImpl +{ +public: + // As per spec, 1 attribute is scenable in the mode select cluster + static constexpr uint8_t scenableAttributeCount = 1; + + DefaultModeSelectSceneHandler() = default; + ~DefaultModeSelectSceneHandler() override {} + + // Default function for the mode select cluster, only puts the mode select cluster ID in the span if supported on the given + // endpoint + virtual void GetSupportedClusters(EndpointId endpoint, Span & clusterBuffer) override + { + ClusterId * buffer = clusterBuffer.data(); + if (emberAfContainsServer(endpoint, ModeSelect::Id) && clusterBuffer.size() >= 1) + { + buffer[0] = ModeSelect::Id; + clusterBuffer.reduce_size(1); + } + else + { + clusterBuffer.reduce_size(0); + } + } + + // Default function for mode select cluster, only checks if mode select is enabled on the endpoint + bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override + { + return (cluster == ModeSelect::Id) && (emberAfContainsServer(endpoint, ModeSelect::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; + + uint8_t currentMode; + // read CurrentMode value + EmberAfStatus status = Attributes::CurrentMode::Get(endpoint, ¤tMode); + if (status != EMBER_ZCL_STATUS_SUCCESS) + { + ChipLogError(Zcl, "ERR: reading CurrentMode %x", status); + return CHIP_ERROR_READ_FAILED; + } + + AttributeValuePair pairs[scenableAttributeCount]; + + pairs[0].attributeID = Attributes::CurrentMode::Id; + pairs[0].attributeValue = currentMode; + + app::DataModel::List attributeValueList(pairs); + + return EncodeAttributeValueList(attributeValueList, serializedBytes); + } + + /// @brief Default EFS interaction when applying scene to the ModeSelect 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; + + VerifyOrReturnError(cluster == CurrentMode::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::CurrentMode::Id, CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorOnFailure( + mSceneEndpointStatePairs.InsertPair(EndpointStatePair(endpoint, static_cast(decodePair.attributeValue)))); + } + // Verify that the EFS was completely read + CHIP_ERROR err = pair_iterator.GetStatus(); + if (CHIP_NO_ERROR != err) + { + mSceneEndpointStatePairs.RemovePair(endpoint); + return err; + } + + // TODO : Implement timer callback and pass level to it + // CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(delayMs), timerCallback, + // reinterpret_cast(static_cast(endpoint))); + + return CHIP_NO_ERROR; + } + +private: + // TODO : Implement timer callback function here +}; +static DefaultModeSelectSceneHandler sModeSelectSceneHandler; + +#endif // EMBER_AF_PLUGIN_SCENES + bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandler, const ConcreteCommandPath & commandPath, const ModeSelect::Commands::ChangeToMode::DecodableType & commandData) { @@ -113,6 +229,15 @@ bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandle } ModeSelect::Attributes::CurrentMode::Set(endpointId, newMode); + // TODO : Compare with previous mode before invalidating scene? +#ifdef EMBER_AF_PLUGIN_SCENES + // the scene has been changed (the value of on/off has changed) so + // the current scene as described in the attribute table is invalid, + // so mark it as invalid (just writes the valid/invalid attribute) + + Scenes::ScenesServer::Instance().MakeSceneInvalid(endpoint); +#endif // EMBER_AF_PLUGIN_SCENES + ChipLogProgress(Zcl, "ModeSelect: ChangeToMode successful"); commandHandler->AddStatus(commandPath, Status::Success); return true; @@ -138,6 +263,13 @@ void emberAfModeSelectClusterServerInitCallback(EndpointId endpointId) EmberAfStatus status = Attributes::StartUpMode::Get(endpointId, startUpMode); if (status == EMBER_ZCL_STATUS_SUCCESS && !startUpMode.IsNull()) { + +#ifdef EMBER_AF_PLUGIN_SCENES +// TODO: Complete init and add getSceneHandler for the cluster +// Registers Scene handlers for the On/Off cluster on the server +// app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpoint, handler); +#endif // EMBER_AF_PLUGIN_SCENES + #ifdef EMBER_AF_PLUGIN_ON_OFF // OnMode with Power Up // If the On/Off feature is supported and the On/Off cluster attribute StartUpOnOff is present, with a