/** * * Copyright (c) 2023 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 #include #include #include namespace chip { namespace scenes { /// @brief Default implementation of handler, handle EFS from add scene and view scene commands for any cluster /// The implementation of SerializeSave and ApplyScene were omitted and must be implemented in a way that /// is compatible with the SerializeAdd output in order to function with the Default Scene Handler. /// It is worth noting that this implementation is very memory consuming. In the current worst case, /// (Color control cluster), the Extension Field Set's value pair list TLV occupies 99 bytes of memory class DefaultSceneHandlerImpl : public scenes::SceneHandler { template using List = app::DataModel::List; template using DecodableList = app::DataModel::DecodableList; using AttributeValuePairType = app::Clusters::ScenesManagement::Structs::AttributeValuePairStruct::Type; using AttributeValuePairDecodableType = app::Clusters::ScenesManagement::Structs::AttributeValuePairStruct::DecodableType; using ExtensionFieldSetDecodableType = app::Clusters::ScenesManagement::Structs::ExtensionFieldSet::DecodableType; using ExtensionFieldSetType = app::Clusters::ScenesManagement::Structs::ExtensionFieldSet::Type; public: /// @brief Struct meant to map the state of a cluster to a specific endpoint. Meant to be used to apply scenes using a timer for /// transitioning /// @tparam ValueType type of the value to map to the endpoint, must implement operator= and operator== for complex types template struct EndpointStatePair { EndpointStatePair(EndpointId endpoint = kInvalidEndpointId, ValueType value = ValueType{}) : mEndpoint(endpoint), mValue(value) {} EndpointId mEndpoint; ValueType mValue; }; template struct StatePairBuffer { static_assert(std::is_trivial::value, "ValueType must be trivial"); static_assert(MaxEndpointCount < std::numeric_limits::max(), "MaxEndpointCount must be less than 65535"); bool IsEmpty() const { return (mPairCount == 0); } CHIP_ERROR FindPair(const EndpointId endpoint, uint16_t & found_index) const { VerifyOrReturnError(!IsEmpty(), CHIP_ERROR_NOT_FOUND); for (found_index = 0; found_index < mPairCount; found_index++) { if (endpoint == mStatePairBuffer[found_index].mEndpoint) { return CHIP_NO_ERROR; } } return CHIP_ERROR_NOT_FOUND; } CHIP_ERROR InsertPair(const EndpointStatePair & status) { uint16_t idx; CHIP_ERROR err = FindPair(status.mEndpoint, idx); if (CHIP_NO_ERROR == err) { mStatePairBuffer[idx] = status; } else if (mPairCount < MaxEndpointCount) { // If not found, insert at the end mStatePairBuffer[mPairCount] = status; mPairCount++; } else { return CHIP_ERROR_NO_MEMORY; } return CHIP_NO_ERROR; } CHIP_ERROR GetPair(const EndpointId endpoint, EndpointStatePair & status) const { uint16_t idx; ReturnErrorOnFailure(FindPair(endpoint, idx)); status = mStatePairBuffer[idx]; return CHIP_NO_ERROR; } /// @brief Removes Pair and decrements Pair count if the endpoint existed in the array /// @param endpoint : endpoint id of the pair CHIP_ERROR RemovePair(const EndpointId endpoint) { uint16_t position; VerifyOrReturnValue(CHIP_NO_ERROR == FindPair(endpoint, position), CHIP_NO_ERROR); uint16_t nextPos = static_cast(position + 1); uint16_t moveNum = static_cast(mPairCount - nextPos); // Compress array after removal, if the removed position is not the last if (moveNum) { memmove(&mStatePairBuffer[position], &mStatePairBuffer[nextPos], sizeof(EndpointStatePair) * moveNum); } mPairCount--; // Clear the last occupied position mStatePairBuffer[mPairCount].mEndpoint = kInvalidEndpointId; return CHIP_NO_ERROR; } uint16_t mPairCount = 0; EndpointStatePair mStatePairBuffer[MaxEndpointCount]; }; /// @brief Helper struct that allows clusters that do not have an existing mechanism for doing // asynchronous work to perform scene transitions over some period of time. /// @tparam MaxEndpointCount template struct TransitionTimeInterface { EmberEventControl sceneHandlerEventControls[MaxEndpointCount]; TransitionTimeInterface(ClusterId clusterId, void (*callback)(EndpointId)) : mClusterId(clusterId), mCallback(callback) {} /** * @brief Configures EventControl callback * * @param[in] endpoint endpoint to start timer for * @return EmberEventControl* configured event control */ EmberEventControl * sceneEventControl(EndpointId endpoint) { EmberEventControl * controller = getEventControl(endpoint, Span(sceneHandlerEventControls)); VerifyOrReturnValue(controller != nullptr, nullptr); controller->endpoint = endpoint; controller->callback = mCallback; return controller; } ClusterId mClusterId; void (*mCallback)(EndpointId); private: /** * @brief event control object for an endpoint * * @param[in] endpoint target endpoint * @param[in] eventControlArray Array where to find the event control * @return EmberEventControl* configured event control */ EmberEventControl * getEventControl(EndpointId endpoint, const Span & eventControlArray) { uint16_t index = emberAfGetClusterServerEndpointIndex(endpoint, mClusterId, FixedEndpointCount); if (index >= eventControlArray.size()) { return nullptr; } return &eventControlArray[index]; } }; static constexpr uint8_t kMaxAvPair = CHIP_CONFIG_SCENES_MAX_AV_PAIRS_EFS; DefaultSceneHandlerImpl() = default; ~DefaultSceneHandlerImpl() override{}; /// @brief Encodes an attribute value list into a TLV structure and resizes the buffer to the size of the encoded data /// @param aVlist[in] Attribute value list to encode /// @param serializedBytes[out] Buffer to fill from the Attribute value list in a TLV format /// @return CHIP_ERROR virtual CHIP_ERROR EncodeAttributeValueList(const List & aVlist, MutableByteSpan & serializedBytes); /// @brief Decodes an attribute value list from a TLV structure and ensure it fits the member pair buffer /// @param serializedBytes [in] Buffer to read from /// @param aVlist [out] Attribute value list to fill from the TLV structure. Only valid while the buffer backing /// serializedBytes exists and its contents are not modified. /// @return CHIP_ERROR virtual CHIP_ERROR DecodeAttributeValueList(const ByteSpan & serializedBytes, DecodableList & aVlist); /// @brief From command AddScene, allows handler to filter through clusters in command to serialize only the supported ones. /// @param endpoint[in] Endpoint ID /// @param extensionFieldSet[in] ExtensionFieldSets provided by the AddScene Command, pre initialized /// @param serializedBytes[out] Buffer to fill from the ExtensionFieldSet in command /// @return CHIP_NO_ERROR if successful, CHIP_ERROR_INVALID_ARGUMENT if the cluster is not supported, CHIP_ERROR value /// otherwise virtual CHIP_ERROR SerializeAdd(EndpointId endpoint, const ExtensionFieldSetDecodableType & extensionFieldSet, MutableByteSpan & serializedBytes) override; /// @brief Simulates taking data from nvm and loading it in a command object if the cluster is supported by the endpoint /// @param endpoint target endpoint /// @param cluster target cluster /// @param serializedBytes data to deserialize into EFS /// @return CHIP_NO_ERROR if Extension Field Set was successfully populated, CHIP_ERROR_INVALID_ARGUMENT if the cluster is not /// supported, specific CHIP_ERROR otherwise virtual CHIP_ERROR Deserialize(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes, ExtensionFieldSetType & extensionFieldSet) override; private: AttributeValuePairType mAVPairs[kMaxAvPair]; }; } // namespace scenes } // namespace chip