/* * * Copyright (c) 2021-2022 Project CHIP Authors * 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. */ #pragma once #include #include #include #include /** * Callback class that clusters can implement in order to interpose custom * attribute-handling logic. An AttributeAccessInterface instance is associated * with some specific cluster. A single instance may be used for a specific * endpoint or for all endpoints. * * Instances of AttributeAccessInterface that are registered via * AttributeAccessInterfaceRegistry::Instance().Register will be consulted before taking the * normal attribute access codepath and can use that codepath as a fallback if desired. */ namespace chip { namespace app { class AttributeAccessInterface { public: /** * aEndpointId can be Missing to indicate that this object is meant to be * used with all endpoints. */ AttributeAccessInterface(Optional aEndpointId, ClusterId aClusterId) : mEndpointId(aEndpointId), mClusterId(aClusterId) {} virtual ~AttributeAccessInterface() {} /** * Callback for reading attributes. * * @param [in] aPath indicates which exact data is being read. * @param [in] aEncoder the AttributeValueEncoder to use for encoding the * data. * * The implementation can do one of three things: * * 1) Return a failure. This is treated as a failed read and the error is * returned to the client, by converting it to a StatusIB. * 2) Return success and attempt to encode data using aEncoder. The data is * returned to the client. * 3) Return success and not attempt to encode any data using aEncoder. In * this case, Ember attribute access will happen for the read. This may * involve reading from the attribute store or external attribute * callbacks. */ virtual CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) = 0; /** * Callback for writing attributes. * * @param [in] aPath indicates which exact data is being written. * @param [in] aDecoder the AttributeValueDecoder to use for decoding the * data. * * The implementation can do one of three things: * * 1) Return a failure. This is treated as a failed write and the error is * sent to the client, by converting it to a StatusIB. * 2) Return success and attempt to decode from aDecoder. This is * treated as a successful write. * 3) Return success and not attempt to decode from aDecoder. In * this case, Ember attribute access will happen for the write. This may * involve writing to the attribute store or external attribute * callbacks. */ virtual CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) { return CHIP_NO_ERROR; } /** * Indicates the start of a series of list operations. This function will be called before the first Write operation of a series * of consequence attribute data of the same attribute. * * 1) This function will be called if the client tries to set a nullable list attribute to null. * 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation) * of the same attribute. * * @param [in] aPath indicates the path of the modified list. */ virtual void OnListWriteBegin(const ConcreteAttributePath & aPath) {} /** * Indicates the end of a series of list operations. This function will be called after the last Write operation of a series * of consequence attribute data of the same attribute. * * 1) This function will be called if the client tries to set a nullable list attribute to null. * 2) This function will only be called once for a series of consequent attribute data (regardless the kind of list operation) * of the same attribute. * 3) When aWriteWasSuccessful is true, the data written must be consistent or the list is untouched. * * @param [in] aPath indicates the path of the modified list * @param [in] aWriteWasSuccessful indicates whether the delivered list is complete. * */ virtual void OnListWriteEnd(const ConcreteAttributePath & aPath, bool aWriteWasSuccessful) {} /** * Mechanism for keeping track of a chain of AttributeAccessInterfaces. */ void SetNext(AttributeAccessInterface * aNext) { mNext = aNext; } AttributeAccessInterface * GetNext() const { return mNext; } /** * Check whether a this AttributeAccessInterface is relevant for a * particular endpoint+cluster. An AttributeAccessInterface will be used * for a read from a particular cluster only when this function returns * true. */ bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const { return (!mEndpointId.HasValue() || mEndpointId.Value() == aEndpointId) && mClusterId == aClusterId; } /** * Check whether an AttributeAccessInterface is relevant for a particular * specific endpoint. This is used to clean up overrides registered for an * endpoint that becomes disabled. */ bool MatchesEndpoint(EndpointId aEndpointId) const { return mEndpointId.HasValue() && mEndpointId.Value() == aEndpointId; } /** * Check whether another AttributeAccessInterface wants to handle the same set of * attributes as we do. */ bool Matches(const AttributeAccessInterface & aOther) const { return mClusterId == aOther.mClusterId && (!mEndpointId.HasValue() || !aOther.mEndpointId.HasValue() || mEndpointId.Value() == aOther.mEndpointId.Value()); } protected: Optional GetEndpointId() { return mEndpointId; } private: Optional mEndpointId; ClusterId mClusterId; AttributeAccessInterface * mNext = nullptr; }; } // namespace app } // namespace chip