New Clusters & Device Types
The goal of new cluster and device type development is to
- write the cluster implementations
- write the code and supporting material that will allow zap to generate the appropriate ember layers
- write the unit tests, test plans and automation scripts that prove the code correctness and allow these new features to be certified
Unit tests, test plans and certification tests are covered in the testing section. This document will concentrate on implementing clusters and device types in the SDK.
Cluster Definition
Cluster Implementation
- Client side - codegen, you write the glue
- Server side - cpp implementation through Ember and / or AttributeAccessInterface and CommandHandlerInterface
- src/app/clusters/<your_cluster_name>
- build file: src/app/chip_data_model.gni
- build file uses data from the codegen to auto-populate the cluster list.
- Follow examples in there to get your code building into the image when selected in zap
Device Type Definitions
See How To Add New Device Types & Clusters for a detailed description of how and where to add cluster and device type definitions so they are picked up properly by ZAP/ember and the SDK.
Note that the output should also be verified against the spec using the .matter parser tools.
ZAP, Ember and Overrides
- Goal: get zap to understand the new cluster so it can be used on devices (XML and glue)
Cluster definitions and ZAP
Please see ZAP for an introduction to ZAP.
After implementing the changes outlined in the wiki article, your cluster and device type should show up in zap. you can check this by running zaptool with any zap file.
./scripts/tools/zap/run_zaptool.sh
To ensure the cluster and device type are correctly implemented for ZAP, open the endpoint configuration and ensure the device type appears in the device type list.
Next, check your cluster. The “domain” parameter in the XML controls which group the cluster is in. It should have all the expected attributes, commands and events.
Last, ensure that your attributes have the storage option set appropriately.
Cluster implementation - Ember and overrides
Cluster Server Initialization
The following diagram shows the flow of messages coming into the Matter core and ending in the cluster initialization code.
EmberAfInitializeAttributes - ember attribute storage - for all attributes marked as “RAM” in the zap, sets defaults in the storage MatterPluginServerCallback - .h is a generated file, .cpp impl is done in the server cluster code. Use this to setup the cluster and do attribute overrides registerAttributeAccessOverride - use this if you want to handle attribute reads and writes externally
Blue sections can be overridden.
Cluster Server Attributes
Two mechanisms
ZAP files and implementation
- For attributes marked as “RAM” storage in the zap file
- Storage allocated automatically, Ember handles read/write
- Generated “Get” and “Set” functions for each attribute in Accessors.h (generated file)
- You CAN register an override on the cluster. If you don’t try to encode the attribute in the override, it will fall through to the storage.
- If you DO always encode the attribute in the access override function, you’re wasting space.
- For attributes marked as “External” storage in the zap file
- NO storage is allocated, no fall through to ember storage
- NEED to register an access override for these to work
Cluster Server Attributes via Override (read)
AttributeAccessInterface::Read()
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath,
AttributeValueEncoder & aEncoder)
{
// Parse aPath to determine the requested attribute
switch (aPath.mAttributeId)
{
case SomeAttribute::Id:
// Just encode the value
aEncoder.Encode(mSomeValue);
break;
}
// Beware of lists - the need to use EncodeList to have
// chunking handled properly
return CHIP_NO_ERROR;
Cluster Server Attributes via override (write)
Write are handled using the same path as read, but land in the “Write” function of the AttributeAccessInterface.
AttributeAccessInterface::Write()
The attribute handler is responsible for constraint checking and Attribute persistence
Attribute Persistence
- When using AttributeAccessInterface, you need to manage any Attributes that require Persistence.
- This can be done by using GetSafeAttributePersistenceProvider()
- This provides a useful API for Reading & Writing values of any type to the default Persistence Store
Ember layer read / write
In the ember layer functions, the ember layer handles the encode and decode. This can work for simple attributes, but is can be challenging for complex attribute interactions. The ember layer is also VERY difficult to unit test.
The ember layer provides callbacks for attribute changes so you can handle them
void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath,
uint8_t type, uint16_t size, uint8_t * value)
Take care when using this that the callbacks are implemented in a way that can be used across examples
Cluster Server Commands
- As with Attributes, there is an ember layer option, and an override option
- Override
- Registered at runtime
- InteractionModelEngine::RegisterCommandHandler
- Implement CommandHandlerInterface
- Ember
Command Handler Code
- CommandHandlerInterface
- Can use HandleCommand function for convenience (sets handled)
- If not, need to set whether the command was handled
- if no, falls through to ember by default
- If entirely handled by this interface, add to src/app/common/templates/config-data.yaml to disable ember
- Ember interface
- Return true if the command was handled, false to have an invalid command response returned
- For both
- Need to handle the return to the caller using either AddResponse or AddStatus in the command handler
- Need to handle constraints checking and return the appropriate status or response per the spec
The config-data.yaml file is used to turn off ember command callback generation for clusters with pure CommandHandlerInterface implementations.
Events and Attribute Subscriptions
- Attribute change reporting
- If you go through the ember storage layer (generated Get/Set functions on the attribute), this is handled for you
- If you are using an AttributeAccessInterface, you need to tell the reporting engine that the attribute has changed
- MatterReportingAttributeChangeCallback
- Events
- No direct ember support
- Call LogEvent function in EventLogging.h. Caller has to either lock the Matter stack lock or queue the event to the Matter event queue when using LogEvent.
A note on Dynamic Endpoints
- Dynamic endpoint registration
- ZAP configs are static at compile time
- Can also use dynamic endpoint registration at runtime
- emberAfSetDynamicEndpoint
- If you have your own storage for attributes etc, need to account for dynamic endpoints as well as static