Migrating Ember Clusters to Code-Driven Implementations#
This document provides a step-by-step guide for converting an existing Ember-based cluster to a modern, code-driven implementation. It is recommended to first read the guide on Writing Clusters for a general overview of the code-driven cluster architecture.
Migration Checklist#
This checklist provides a granular, step-by-step process for migrating an Ember cluster to a code-driven implementation.
Part 0: Optimizing for an Easier Review#
Before you begin the migration, consider structuring your changes into multiple, smaller pull requests. This approach significantly simplifies the review process, allowing reviewers to approve preliminary changes quickly. We recommend the following sequence of PRs:
[ ] PR 1: File Renames Only.
If your migration involves renaming files, submit a PR containing only the renames. A typical rename is from
<name>-server.cppto<Name>Cluster.cpp.Note: For backward compatibility with code generation, it is often best to not rename the header file.
Why: This prevents
git difffrom becoming confused and showing the entire file as deleted and recreated, making the actual code changes impossible to review.This type of PR can be reviewed and merged very quickly.
[ ] PR 2: Code Movement Only.
If you plan to reorder functions or move code blocks (e.g., moving helper functions to an anonymous namespace), submit a PR with only these movements. Do not change any logic.
Why: Reviewers can use tools like
git diff --color-movedto verify that code has only been moved, not altered. This allows for a rapid review of structural changes.This type of PR can also be fast-tracked.
[ ] PR 3: The Core Logic Changes.
This PR should contain the actual migration logic: implementing the new cluster class, moving attribute storage, and converting command handlers.
Why: With renames and code movements already handled, this PR will be much smaller and focused, allowing the reviewer to concentrate solely on the correctness of the migration logic.
This structure respects the reviewer’s time and helps get your changes merged faster. You can ask for an expedited review of the preliminary PRs in the project’s Slack channel.
Part 1: Analysis and Design#
[ ] 1.1: Understand the Existing Implementation:
[ ] Identify if the cluster uses a pure Ember implementation,
AttributeAccessInterface(AAI), orCommandHandlerInterface(CHI).Pure Ember Implementation: For simple attributes, the Ember framework in
src/app/utilhandles data storage and access. In a code-driven implementation, this data must be moved into member variables within your new cluster class.AAI and CHI: For more complex logic,
AAItranslates to theReadAttributeandWriteAttributemethods, whileCHItranslates to theInvokeCommandmethod.
[ ] Determine how attributes are stored (e.g.,
persist,ram,callback). This will inform how you handle data in the new implementation.If the value was
persist, the new cluster should handle persistence (load inStartupand store during writes).If the value was
ramand loaded from the ZAP UI,CodegenIntegration.cppshould load the value from ZAP via the generatedAccessors.h. A common example is theFeatureMap.If the value is internal to the cluster (e.g.,
ClusterRevision), it should be marked asExternalby adding it toattributeAccessInterfaceAttributesinzcl.json.
[ ] 1.2: Choose an Implementation Pattern:
[ ] Decide between a combined or modular implementation based on the cluster’s complexity and resource constraints. See the Writing Clusters guide for more details.
[ ] 1.3: Create the File Structure:
[ ] Use an existing directory for the cluster or create a new one if missing at
src/app/clusters/<name>/.Note: If the cluster was a pure Ember implementation, this directory may not exist. After creating it, you must add a mapping to
src/app/zap_cluster_list.jsonunder theServerDirectorieskey.
[ ] Add the following files:
<Name>Cluster.h<Name>Cluster.cppCodegenIntegration.cpptests/Test<Name>Cluster.cppBUILD.gntests/BUILD.gnapp_config_dependent_sources.gniapp_config_dependent_sources.cmake
Part 2: Implementation#
[ ] 2.1: Implement the Cluster Class:
[ ] Define the
<Name>Clusterclass, inheriting fromDefaultServerCluster.[ ] Add member variables for all attributes previously handled by Ember.
[ ] Implement the
StartupandShutdownmethods for resource management.
[ ] 2.2: Implement Attribute Logic:
[ ] Implement the
ReadAttributeandWriteAttributemethods, translating any existingAAIlogic.[ ] For persisted attributes, use the
AttributePersistencehelper to load inStartupand save on writes.[ ] Crucially, after a successful write, call a notification function (e.g.,
NotifyAttributeChangedIfSuccess) to ensure subscriptions work correctly.
[ ] 2.3: Implement Command Logic:
[ ] Implement the
InvokeCommandmethod, translating any existingCHIoremberAf...Callbacklogic.Example: An
emberAf<CLUSTER>Cluster<COMMAND>Callbackfunction becomes acasein theInvokeCommand’sswitchstatement:// This: emberAfAccessControlClusterReviewFabricRestrictionsCallback(...); // Becomes this in the `InvokeCommand` implementation: switch (request.path.mCommandId) { // ... case AccessControl::Commands::ReviewFabricRestrictions::Id: // ... }
[ ] Use a
switchstatement on the command ID to handle different commands.
[ ] 2.4: Implement Event Logic:
[ ] Replace any calls to
LogEventwithmContext->interactionContext.eventsGenerator.GenerateEventto make events unit-testable.[ ] Check if any events have non-default access permissions (e.g., require Administrator access) and implement the
EventInfomethod if necessary.
Part 3: Configuration and Integration#
[ ] 3.1: Update Build Files:
[ ] Configure
BUILD.gnandtests/BUILD.gnto include the new source files.[ ] Configure
app_config_dependent_sources.gniandapp_config_dependent_sources.cmaketo integrate the cluster into the build system.
[ ] 3.2: Implement Codegen Integration:
[ ] In
CodegenIntegration.cpp, use theCodegenClusterIntegrationhelper to read configuration values from the generated code.Note: The
CodegenClusterIntegrationhelper for optional attributes only supports attribute IDs up to 31. For clusters with higher attribute IDs, you will need a custom implementation.
[ ] 3.3: Update ZAP Configuration:
[ ] In
src/app/common/templates/config-data.yaml, add the cluster to theCommandHandlerInterfaceOnlyClustersarray.[ ] In
src/app/zap-templates/zcl/zcl.jsonandzcl-with-test-extensions.json, add all non-list attributes to theattributeAccessInterfaceAttributeslist.[ ] Run the ZAP regeneration script:
./scripts/run_in_build_env.sh 'scripts/tools/zap_regen_all.py'
Part 4: Testing#
[ ] 4.1: Write Unit Tests:
[ ] In
tests/Test<Name>Cluster.cpp, add unit tests for the new implementation.[ ] Ensure all attributes, commands, and feature combinations are tested.
[ ] 4.2: Perform Integration Testing:
[ ] Integrate the cluster into an example application (e.g.,
all-clusters-app).[ ] Manually validate the cluster’s functionality using
chip-toolormatter-repl.