This directory implements the Matter Actions cluster (cluster ID 0x0025).
The Matter spec classifies the Actions cluster as “Scope: Node”. This means:
ActionsServer instance should exist at a time. The constructor logs an error if this constraint is violated.Delegate interface has no EndpointId parameters for the same reason: it was designed for a single cluster instance.There are two layers.
ActionsCluster (preferred for new code)ActionsCluster derives from DefaultServerCluster and is the canonical implementation. It is instantiated once for the aggregator endpoint.
ActionsCluster (one instance, on the aggregator endpoint) └── Actions::Delegate (supplied by the application)
Applications using the code-driven data model create an ActionsCluster directly and register it with RegisteredServerCluster<ActionsCluster>.
ActionsServer / CodegenIntegration (backwards-compatibility wrapper)ActionsServer is a thin wrapper around ActionsCluster for applications written against the older Ember/ZAP-generated API. It:
RegisteredServerCluster<ActionsCluster> for the aggregator endpoint.SetupURL) from Ember RAM at construction time.ActionListModified(EndpointId) / EndpointListModified(EndpointId) callbacks; calls for the wrong endpoint are silently ignored.The ZAP-generated plugin callbacks (MatterActionsClusterInitCallback, etc.) are left as empty stubs. Applications instantiate ActionsServer directly and register it with the codegen data model provider via Init(). This is the standard code-driven cluster pattern where the application owns the cluster lifecycle.
auto cluster = std::make_unique<ActionsCluster>(aggregatorEndpointId, myDelegate); // Register via RegisteredServerCluster<ActionsCluster> and call Init().
// Typically called from emberAfActionsClusterInitCallback, guarded to run once: sActionsDelegateImpl = std::make_unique<MyDelegate>(); sActionsServer = std::make_unique<ActionsServer>(aggregatorEndpointId, *sActionsDelegateImpl); sActionsServer->Init(); // When the action list changes: sActionsServer->ActionListModified(aggregatorEndpointId); // Shutdown is called automatically by the destructor.
Init() and Shutdown() are both idempotent.
The application supplies a concrete Actions::Delegate:
ReadActionAtIndex / ReadEndpointListAtIndex — iterate collections (return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED to signal end of list).HaveActionWithId — fast O(n) lookup by action ID.Handle* — one method per Actions command (e.g. HandleInstantAction). Return Status::Success to accept, another status to reject.When an action's state changes after a command has been accepted, call GenerateEvent on the ActionsCluster to emit the corresponding Matter event to the fabric:
cluster.GenerateEvent(Events::StateChanged::Type{ actionId, invokeId, newState }); cluster.GenerateEvent(Events::ActionFailed::Type{ actionId, invokeId, state, error });