The all-devices-app is a reference application demonstrating the Code-Driven Data Model within the Matter SDK. It implements a runtime-configurable data model.
This document describes the architectural layers, core classes, and design principles of the application.
The all-devices-app implements the Code-Driven Data Model:
provider.AddEndpoint(...) via CodeDrivenDataModelProvider.DefaultServerCluster (or similar code-driven base classes). Attributes and commands are strongly typed and encapsulated within the cluster classes.The all-devices-app enforces platform separation between core logic and target drivers:
all-devices-common/)Contains simulated device behaviors and capability management. This layer compiles independently of the operating system or hardware drivers. It includes:
device/types/: Concrete implementations of simulated Matter devices (e.g., OccupancySensor, DimmableLight, Speaker).device/api/: Base contracts and abstractions (DeviceInterface, SingleEndpoint, device/api/allocator/DynamicEndpointIdAllocator.h).device/capabilities/: Reusable device loads and capabilities (e.g., OnOffLoad, DimmableLoad, FanLoad).device-factory/: Registry (DeviceFactory<Hooks...>) responsible for mapping CLI device names to creation factories.oob-accessors/: Out-of-Band cluster manipulation layer (OOBAccessor, OOBAccessorRegistry).providers/: SDK-level data providers (such as AllDevicesExampleDeviceInfoProviderImpl) that supply node lifecycle information, storage interfaces, and descriptor details.posix/, esp32/, silabs/, telink/)These directories contain hardware-specific or OS-specific drivers, entrypoint main() functions, and build configurations.
DeviceFactoryPlatformOverride.h can register an LED driver for the on-off-light device instead of the simulated device.All single-endpoint devices inherit from DeviceInterface and SingleEndpoint. Device types follow a strict inheritance direction separating generic spec definitions from concrete simulations or hardware drivers.
SingleEndpoint <- Foo <- LoggingFoo)classDiagram class DeviceInterface { <<interface>> +Register(EndpointIdAllocator &, CodeDrivenDataModelProvider &, EndpointComposition)* CHIP_ERROR +Unregister(CodeDrivenDataModelProvider &)* } class SingleEndpoint { <<abstract>> #mEndpointId: EndpointId +Register(EndpointId, CodeDrivenDataModelProvider &, EndpointComposition)* CHIP_ERROR +Unregister(CodeDrivenDataModelProvider &)* +GetEndpointId() EndpointId } class Foo { <<device/types/foo/Foo.h>> #MandatoryClusters +Foo(Delegates &) #RegisterAdditionalClusters(EndpointId, CodeDrivenDataModelProvider &) CHIP_ERROR #UnregisterAdditionalClusters(CodeDrivenDataModelProvider &) void } class LoggingFoo { <<device/types/foo/impl/LoggingFoo.h>> #OptionalClusters +LoggingFoo(Context) #RegisterAdditionalClusters(...) override #UnregisterAdditionalClusters(...) override } DeviceInterface <|-- SingleEndpoint SingleEndpoint <|-- Foo Foo <|-- LoggingFoo
SingleEndpoint (all-devices-common/device/api/SingleEndpoint.h): Encapsulates endpoint state, EndpointId assignment, parent composition, and DeviceTypeEntry descriptors.device/types/foo/Foo.h, e.g. LaundryWasher): Represents the spec-defined Matter Device Type. It inherits directly from SingleEndpoint, defines the DeviceTypeEntry (ID and revision), owns only mandatory clusters, and accepts abstract Delegate & references via constructor injection. It contains zero simulation, logging, or hardware pin logic.device/types/foo/impl/LoggingFoo.h): Inherits from Foo (never the reverse). LoggingFoo (or EmulatedFoo) provides a self-contained simulation for DeviceFactory by implementing the required cluster delegates (inheriting privately from delegate bases before public Foo for base-from-member initialization safety). Hardware targets subclass or instantiate Foo with real hardware drivers.Because impl/LoggingFoo and platform subclasses derive from Foo, UI screens, Out-of-Band (OOB) CLI controls, and generic application logic bind to the base device type interface (Foo) without coupling to simulation or hardware details.
Optional clusters must not reside in the base Foo class. Keeping Foo restricted to mandatory clusters avoids RAM/Flash overhead for minimal products.
Because CodeDrivenDataModelProvider requires all clusters on an endpoint to be registered via provider.AddCluster() before provider.AddEndpoint() commits the endpoint (and removed after UnregisterDescriptor() unregisters the endpoint), base device classes expose virtual extension hooks inside their Register() and Unregister() methods:
RegisterAdditionalClusters(EndpointId endpoint, CodeDrivenDataModelProvider & provider): Invoked inside Foo::Register() after mandatory clusters are added, immediately before provider.AddEndpoint(mEndpointRegistration).UnregisterAdditionalClusters(CodeDrivenDataModelProvider & provider): Invoked inside Foo::Unregister() immediately after UnregisterDescriptor(provider).Subclasses in impl/ or platform targets override these hooks to attach optional clusters cleanly.
device/capabilities/)classDiagram class SingleEndpoint { <<abstract>> } class OnOffLoad { <<device/capabilities/on-off-load/OnOffLoad.h>> } class OnOffLight { <<device/types/on-off-light/OnOffLight.h>> } class LoggingOnOffLight { <<device/types/on-off-light/impl/LoggingOnOffLight.h>> } SingleEndpoint <|-- OnOffLoad OnOffLoad <|-- OnOffLight OnOffLight <|-- LoggingOnOffLight
A small set of closely related device families (OnOffLoad, DimmableLoad, FanLoad) use a 3-tier hierarchy: impl -> device_type -> capability -> SingleEndpoint.
SingleEndpoint even if cluster declarations repeat across similar devices.capabilities/impl/ (e.g. LoggingOnOffDelegate) and compose or inherit those in impl/LoggingFoo while keeping Foo derived directly from SingleEndpoint.The DeviceFactory<typename... Hooks> template class acts as the central device creator and post-registration dispatcher:
sequenceDiagram participant App as Application / main() participant Factory as DeviceFactory<Hooks...> participant Device as Concrete Device participant Provider as CodeDrivenDataModelProvider participant Hook as Registered Hooks App->>Factory: Create(deviceType, label) Factory->>Device: new ConcreteDevice(...) Factory-->>App: DeviceRegistrationEntry { device, afterDeviceRegistration, beforeDeviceUnregistration } App->>Device: Register(endpointIdAllocator, Provider) Device->>Provider: AddEndpoint / AddCluster (assigns EndpointId) Device-->>App: CHIP_NO_ERROR App->>Factory: afterDeviceRegistration() Factory->>Hook: (Hooks::AfterDeviceRegistration(*device), ...) App->>Factory: beforeDeviceUnregistration() Factory->>Hook: (Hooks::BeforeDeviceUnregistration(*device), ...) Factory->>Device: Unregister(Provider)
DeviceFactory constructor.DeviceFactory with target-specific static hooks:NoHooksDeviceFactory (DeviceFactory<>): Default specialization with no hooks, used by embedded targets (ESP32, SiLabs, Telink) to minimize binary footprint.PosixDeviceFactory (DeviceFactory<OOBAccessorHook, NamedPipe::Hook>): Specialized for POSIX, dynamically registering OOB cluster accessors and named pipe JSON translators only for instantiated devices.factory.Create(type, label) instantiates the device and returns a DeviceRegistrationEntry struct containing the std::unique_ptr<DeviceInterface> and a std::function<void()> afterDeviceRegistration callback and a std::function<void()> beforeDeviceUnregistration callback.CodeDrivenDataModelProvider, allocating its valid runtime EndpointId. entry.Register(...) invokes the device registration and then invokes the afterDeviceRegistration callback, which expands the variadic fold expression (Hooks::AfterDeviceRegistration(*rawDevice), ...) statically for each configured hook.entry.Unregister(provider). This first invokes the beforeDeviceUnregistration callback, which invokes each configured hook's BeforeDeviceUnregistration(*rawDevice) callback, allowing registrations tied to the device to be removed while the device is still valid.entry.Unregister(...) then calls DeviceInterface::Unregister(provider) to remove the endpoint and its clusters from the data model provider.DeviceInstances::Shutdown() unregisters every constructed device, unregisters the root node, and only then releases the device objects.When maintaining or extending the all-devices-app architecture, adhere to the following guidelines:
all-devices-common/. If a capability requires platform integration, define an interface in all-devices-common/ and provide the implementation in target directories (posix/, esp32/, etc.).DeviceInfoProvider or DeviceInstanceInfoProvider interfaces to handle non-volatile runtime variables and user settings.~Device() override = default; and manage lifecycle teardown explicitly by executing Unregister(provider). Teardown must unregister the endpoint first (via UnregisterDescriptor()) before removing or destroying individual clusters, as CodeDrivenDataModelProvider disallows removing clusters from an actively registered endpoint once started (CHIP_ERROR_INCORRECT_STATE).DeviceTypeParser.h, NetworkInfrastructureManager.h).Detailed architecture specifications for application subsystems:
OOBAccessor).