blob: aaf08eb6bf2b9951cb11e86a78600d0a4b88d215 [file] [log] [blame]
/*
*
* Copyright (c) 2024 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.
*/
#pragma once
#include <app-common/zap-generated/cluster-objects.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/AttributeAccessInterface.h>
#include <app/CommandHandlerInterface.h>
#include <app/reporting/reporting.h>
#include <protocols/interaction_model/StatusCode.h>
namespace chip {
namespace app {
namespace Clusters {
namespace Actions {
static constexpr size_t kActionNameMaxSize = 128u;
static constexpr size_t kEndpointListNameMaxSize = 128u;
static constexpr size_t kEndpointListMaxSize = 256u;
class Delegate;
struct ActionStructStorage : public Structs::ActionStruct::Type
{
ActionStructStorage(){};
ActionStructStorage(uint16_t aAction, const CharSpan & aActionName, ActionTypeEnum aActionType, uint16_t aEpListID,
BitMask<CommandBits> aCommands, ActionStateEnum aActionState)
{
Set(aAction, aActionName, aActionType, aEpListID, aCommands, aActionState);
}
ActionStructStorage(const ActionStructStorage & aAction) { *this = aAction; }
ActionStructStorage & operator=(const ActionStructStorage & aAction)
{
Set(aAction.actionID, aAction.name, aAction.type, aAction.endpointListID, aAction.supportedCommands, aAction.state);
return *this;
}
void Set(uint16_t aAction, const CharSpan & aActionName, ActionTypeEnum aActionType, uint16_t aEpListID,
BitMask<CommandBits> aCommands, ActionStateEnum aActionState)
{
actionID = aAction;
type = aActionType;
endpointListID = aEpListID;
supportedCommands = aCommands;
state = aActionState;
MutableCharSpan actionName(mBuffer);
CopyCharSpanToMutableCharSpanWithTruncation(aActionName, actionName);
name = actionName;
}
private:
char mBuffer[kActionNameMaxSize];
};
struct EndpointListStorage : public Structs::EndpointListStruct::Type
{
EndpointListStorage(){};
EndpointListStorage(uint16_t aEpListId, const CharSpan & aEpListName, EndpointListTypeEnum aEpListType,
const DataModel::List<const EndpointId> & aEndpointList)
{
Set(aEpListId, aEpListName, aEpListType, aEndpointList);
}
EndpointListStorage(const EndpointListStorage & aEpList) { *this = aEpList; }
EndpointListStorage & operator=(const EndpointListStorage & aEpList)
{
Set(aEpList.endpointListID, aEpList.name, aEpList.type, aEpList.endpoints);
return *this;
}
void Set(uint16_t aEpListId, const CharSpan & aEpListName, EndpointListTypeEnum aEpListType,
const DataModel::List<const EndpointId> & aEndpointList)
{
endpointListID = aEpListId;
type = aEpListType;
size_t epListSize = std::min(aEndpointList.size(), MATTER_ARRAY_SIZE(mEpList));
for (size_t index = 0; index < epListSize; index++)
{
mEpList[index] = aEndpointList[index];
}
endpoints = DataModel::List<const EndpointId>(Span(mEpList, epListSize));
MutableCharSpan epListName(mBuffer);
CopyCharSpanToMutableCharSpanWithTruncation(aEpListName, epListName);
name = epListName;
}
private:
char mBuffer[kEndpointListNameMaxSize];
EndpointId mEpList[kEndpointListMaxSize];
};
class ActionsServer : public AttributeAccessInterface, public CommandHandlerInterface
{
public:
// Register for the Actions cluster on all endpoints.
ActionsServer(EndpointId aEndpointId, Delegate & aDelegate) :
AttributeAccessInterface(MakeOptional(aEndpointId), Actions::Id),
CommandHandlerInterface(MakeOptional(aEndpointId), Actions::Id), mDelegate(aDelegate), mEndpointId(aEndpointId)
{}
~ActionsServer();
/**
* Initialise the Actions server instance.
* @return Returns an error if the given endpoint and cluster have not been enabled in zap, if the
* AttributeAccessInterface or AttributeAccessInterface registration fails returns an error.
*/
CHIP_ERROR Init();
/**
* Unregisters the CommandHandlerInterface and AttributeAccessInterface.
*/
void Shutdown();
/**
* @brief
* Called when the state of an action is changed.
*/
void OnStateChanged(EndpointId aEndpoint, uint16_t aActionId, uint32_t aInvokeId, ActionStateEnum aActionState);
/**
* @brief
* Called when an action fails.
*/
void OnActionFailed(EndpointId aEndpoint, uint16_t aActionId, uint32_t aInvokeId, ActionStateEnum aActionState,
ActionErrorEnum aActionError);
void SetDefaultDelegate(EndpointId aEndpointId, Delegate * aDelegate);
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
/**
* A notification from an application to the sever that an ActionList is modified..
*
* @param aEndpoint The endpoint ID where the action should be updated
*/
void ActionListModified(EndpointId aEndpoint);
/**
* A notification from an application to the sever that an EndpointList is modified..
*
* @param aEndpoint The endpoint ID where the action should be updated
*/
void EndpointListModified(EndpointId aEndpoint);
private:
Delegate & mDelegate;
EndpointId mEndpointId;
static ActionsServer sInstance;
static constexpr size_t kMaxEndpointListLength = 256u;
static constexpr size_t kMaxActionListLength = 256u;
CHIP_ERROR ReadActionListAttribute(const ConcreteReadAttributePath & aPath,
const AttributeValueEncoder::ListEncodeHelper & aEncoder);
CHIP_ERROR ReadEndpointListAttribute(const ConcreteReadAttributePath & aPath,
const AttributeValueEncoder::ListEncodeHelper & aEncoder);
bool HaveActionWithId(EndpointId aEndpointId, uint16_t aActionId, uint16_t & aActionIndex);
// TODO: We should move to non-global dirty marker.
void MarkDirty(EndpointId aEndpointId, AttributeId aAttributeId)
{
MatterReportingAttributeChangeCallback(aEndpointId, Id, aAttributeId);
}
// Cannot use CommandHandlerInterface::HandleCommand directly because we need to do the HaveActionWithId() check before
// handling a command.
template <typename RequestT, typename FuncT>
void HandleCommand(HandlerContext & handlerContext, FuncT func);
void InvokeCommand(HandlerContext & handlerContext) override;
void HandleInstantAction(HandlerContext & ctx, const Commands::InstantAction::DecodableType & commandData);
void HandleInstantActionWithTransition(HandlerContext & ctx,
const Commands::InstantActionWithTransition::DecodableType & commandData);
void HandleStartAction(HandlerContext & ctx, const Commands::StartAction::DecodableType & commandData);
void HandleStartActionWithDuration(HandlerContext & ctx, const Commands::StartActionWithDuration::DecodableType & commandData);
void HandleStopAction(HandlerContext & ctx, const Commands::StopAction::DecodableType & commandData);
void HandlePauseAction(HandlerContext & ctx, const Commands::PauseAction::DecodableType & commandData);
void HandlePauseActionWithDuration(HandlerContext & ctx, const Commands::PauseActionWithDuration::DecodableType & commandData);
void HandleResumeAction(HandlerContext & ctx, const Commands::ResumeAction::DecodableType & commandData);
void HandleEnableAction(HandlerContext & ctx, const Commands::EnableAction::DecodableType & commandData);
void HandleEnableActionWithDuration(HandlerContext & ctx,
const Commands::EnableActionWithDuration::DecodableType & commandData);
void HandleDisableAction(HandlerContext & ctx, const Commands::DisableAction::DecodableType & commandData);
void HandleDisableActionWithDuration(HandlerContext & ctx,
const Commands::DisableActionWithDuration::DecodableType & commandData);
};
class Delegate
{
public:
virtual ~Delegate() = default;
/**
* Get the action at the Nth index from list of actions.
* @param index The index of the action to be returned. It is assumed that actions are indexable from 0 and with no gaps.
* @param action A reference to the ActionStructStorage which should be initialized via copy/assignments or calling Set().
* @return Returns a CHIP_NO_ERROR if there was no error and the action was returned successfully.
* CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the index is past the end of the list of actions.
*/
virtual CHIP_ERROR ReadActionAtIndex(uint16_t index, ActionStructStorage & action) = 0;
/**
* Get the EndpointList at the Nth index from list of endpointList.
* @param index The index of the endpointList to be returned. It is assumed that endpoint lists are indexable from 0 and with no
* gaps.
* @param action A reference to the EndpointListStorage which should be initialized via copy/assignments or calling Set().
* @return Returns a CHIP_NO_ERROR if there was no error and the epList was returned successfully.
* CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the index is past the end of the list of endpointLists.
*/
virtual CHIP_ERROR ReadEndpointListAtIndex(uint16_t index, EndpointListStorage & epList) = 0;
/**
* Check whether there is an action with the given actionId in the list of actions.
* @param aActionId The action ID to search for.
* @param aActionIndex A reference to the index at which an action with matching aActionId.
* @return Returns a true if matching action is found otherwise false.
*/
virtual bool HaveActionWithId(uint16_t aActionId, uint16_t & aActionIndex) = 0;
/**
* The implementations of the Handle* command callbacks below are expected to call OnStateChanged or
* OnActionFailed as needed to generate the events required by the spec.
*/
/**
* @brief Callback that will be called to handle an InstantAction command.
* @param actionId The actionId of an action.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleInstantAction(uint16_t actionId, Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle an InstantActionWithTransition command.
* @param actionId The actionId of an action.
* @param transitionTime The time for transition from the current state to the new state.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleInstantActionWithTransition(uint16_t actionId, uint16_t transitionTime,
Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a StartAction command.
* @param actionId The actionId of an action.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleStartAction(uint16_t actionId, Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a StartActionWithDuration command.
* @param actionId The actionId of an action.
* @param duration The time for which an action shall be in start state.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleStartActionWithDuration(uint16_t actionId, uint32_t duration,
Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a StopAction command.
* @param actionId The actionId of an action.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleStopAction(uint16_t actionId, Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a PauseAction command.
* @param actionId The actionId of an action.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandlePauseAction(uint16_t actionId, Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a PauseActionWithDuration command.
* @param actionId The actionId of an action.
* @param duration The time for which an action shall be in pause state.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandlePauseActionWithDuration(uint16_t actionId, uint32_t duration,
Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a ResumeAction command.
* @param actionId The actionId of an action.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleResumeAction(uint16_t actionId, Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle an EnableAction command.
* @param actionId The actionId of an action.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleEnableAction(uint16_t actionId, Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle an EnableActionWithDuration command.
* @param actionId The actionId of an action.
* @param duration The time for which an action shall be in active state.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleEnableActionWithDuration(uint16_t actionId, uint32_t duration,
Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a DisableAction command.
* @param actionId The actionId of an action.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleDisableAction(uint16_t actionId, Optional<uint32_t> invokeId) = 0;
/**
* @brief Callback that will be called to handle a DisableActionWithDuration command.
* @param actionId The actionId of an action.
* @param duration The time for which an action shall be in disable state.
* It should report Status::Success if successful and may report other Status codes if it fails.
*/
virtual Protocols::InteractionModel::Status HandleDisableActionWithDuration(uint16_t actionId, uint32_t duration,
Optional<uint32_t> invokeId) = 0;
};
} // namespace Actions
} // namespace Clusters
} // namespace app
} // namespace chip