tree: 1c1fa5c998c48163155cc42f3a1cbf3c55066866
  1. codegen/
  2. tests/
  3. app_config_dependent_sources.cmake
  4. app_config_dependent_sources.gni
  5. BUILD.gn
  6. Modules.h
  7. NamedPowerSourceClusters.h
  8. power-source-server.h
  9. PowerSourceCluster.h
  10. README.md
src/app/clusters/power-source-server/README.md

Power Source Cluster

The Power Source cluster is used to monitor and report the state of a power source. A power source can be either a battery or a wired power source.

Overview

This directory contains an implementation of Matter Power Source cluster. This implementation does not handle the persistence of the Order attribute. Because the Order attribute is read-only, only the direct user of the cluster can change it, so it is expected of them to handle the persistence of the attribute. This implementation also doesn't handle the storage of fixed string attributes and the attribute endpointList. This implementation notifies the data model when an attribute changes in the way that the spec mandates.

Usage

The cluster needs to be created manually and registered directly using CodeDrivenDataModelProvider. Creation can be done using RegisteredServerCluster and LazyRegisteredServerCluster in src/app/server-cluster/SingleEndpointServerClusterRegistry.h.

How does the cluster work

Because of flash considerations the implementation is a template based on the feature set and attribute set for things that the user wants to be supported. This will be made more clear using examples

Examples

  • Configuration for a wired power source besides mandatory attributes.

    This cluster supports WiredAssessedInputVoltage and WiredNominalVoltage optional attributes

    #include <app/clusters/power-source-server/PowerSourceCluster.h>
    #include <app/server-cluster/SingleEndpointServerClusterRegistry.h>
    #include <data-model-providers/codedriven/CodeDrivenDataModelProvider.h>
    
    using namespace app;
    using namespace app::Clusters;
    
    constexpr EndpointId powerSourceEndpointId = 1;
    

    Define the template specialization to be used

    // this is the "more correct" way to do this :)
    constexpr uint32_t GetOptionalAttributeBits()
    {
        PowerSource::PowerSourceOptionalAttributeSet optAttributes{};
        optAttributes.Set<PowerSource::Attributes::WiredAssessedInputVoltage::Id>();
        optAttributes.Set<PowerSource::Attributes::WiredNominalVoltage::Id>();
        return optAttributes.Raw();
    }
    
    constexpr uint32_t optionalAttributeBits = GetOptionalAttributeBits();
    
    // -- OR --
    
    constexpr uint32_t optionalAttributeBits = OptionalAttributeSet<
        PowerSource::Attributes::WiredAssessedInputVoltage::Id,
        PowerSource::Attributes::WiredNominalVoltage::Id>::All();
    
    constexpr uint32_t featureBits = BitFlags<PowerSource::Feature>(PowerSource::Feature::kWired).Raw();
    
    using MyWiredPowerSourceCluster = PowerSourceCluster<featureBits, optionalAttributeBits>;
    
    

    Create config object for the cluster, the cluster itself, and then use it


    // the config MyWiredPowerSourceCluster::Config config("Wired Power Source (description)"_span, PowerSource::WiredCurrentTypeEnum::kDc); config.wiredNominalVoltage = 23000; // 23V // the cluster RegisteredServerCluster<MyWiredPowerSourceCluster> powerSourceInstance(powerSourceEndpointId, config); // register the cluster, this will call `Startup` on the cluster CodeDrivenDataModelProvider::Instance().Registry().Register(powerSourceInstance.Registration()); // use the cluster powerSourceInstance.Cluster().SetWiredAssessedInputVoltage(20000); // 20V

    Note that functions associated with attributes that are not supported will throw a compile error.

  • Configuration for three batteries, one simple, one rechargeable, one replaceable

    Simple battery will use the BatPercentRemaining optional attribute. Rechargeable battery will use the BatPercentRemaining, BatChargingCurrent optional attributes. Replaceable battery will use the BatTimeRemaining, BatCapacity optional attributes.

    It will be very beneficial to use one template specialization for the three of these configurations.

     #include <app/clusters/power-source-server/PowerSourceCluster.h>
     #include <app/server-cluster/SingleEndpointServerClusterRegistry.h>
     #include <platform/DefaultTimerDelegate.h>
     #include <data-model-providers/codedriven/CodeDrivenDataModelProvider.h>
    
     using namespace app;
     using namespace app::Clusters;
    
     constexpr EndpointId powerSourceEndpointId1 = 1;
     constexpr EndpointId powerSourceEndpointId2 = 2;
     constexpr EndpointId powerSourceEndpointId3 = 3;
    
     DefaultTimerDelegate timerDelegate;
    

    Define the template specialization to be used

    constexpr uint32_t GetOptionalAttributeBits()
    {
        PowerSource::PowerSourceOptionalAttributeSet optAttributes{};
        // enable all optional attributes that are used in any of the configurations
        optAttributes.Set<PowerSource::Attributes::BatPercentRemaining::Id>();
        optAttributes.Set<PowerSource::Attributes::BatTimeRemaining::Id>();
        optAttributes.Set<PowerSource::Attributes::BatChargingCurrent::Id>();
        optAttributes.Set<PowerSource::Attributes::BatCapacity::Id>();
        return optAttributes.Raw();
    }
    
    constexpr uint32_t optionalAttributeBits = GetOptionalAttributeBits();
    
    // enable all features that are used in any of the configurations
    constexpr uint32_t featureBits = BitFlags<PowerSource::Feature>(
        PowerSource::Feature::kBattery,
        PowerSource::Feature::kRechargeable,
        PowerSource::Feature::kReplaceable
    ).Raw();
    
    using MyBatteryPowerSourceCluster = PowerSourceCluster<featureBits, optionalAttributeBits>;
    
    

    Create config objects for the clusters, the clusters, and then use them All three objects will have the same functionality in terms of accessible functions and config field members

    But it is possible to inform the cluster exactly what attributes are used or supported from the manufacturer side using the field usedOptionalAttributes in the config object. This will make the cluster to report specific information about what the cluster supports to the data model. This will mean, that technically the cluster will have the capability to support certain attributes, but if manufacturer can't support it, the data model will see them as unsupported.


    // The attribute sets of configurations PowerSource::PowerSourceOptionalAttributeSet attrSet1{}; attrSet1.Set<PowerSource::Attributes::BatPercentRemaining::Id>(); PowerSource::PowerSourceOptionalAttributeSet attrSet2{}; attrSet2.Set<PowerSource::Attributes::BatPercentRemaining::Id>(); attrSet2.Set<PowerSource::Attributes::BatChargingCurrent::Id>(); PowerSource::PowerSourceOptionalAttributeSet attrSet3{}; attrSet3.Set<PowerSource::Attributes::BatTimeRemaining::Id>(); attrSet3.Set<PowerSource::Attributes::BatCapacity::Id>(); // the configs MyBatteryPowerSourceCluster::Config config1("Simple battery cluster"_span, PowerSource::BatReplaceabilityEnum::kUnspecified, timerDelegate); config1.batPercentRemaining = 200; // 100%, doubled percentage config1.usedOptionalAttributes = attrSet1; MyBatteryPowerSourceCluster::Config config2("Rechargeable battery cluster"_span, PowerSource::BatReplaceabilityEnum::kNotReplaceable, timerDelegate); // this will make the cluster report itself as rechargeable, otherwise it would be like a simple cluster to the data model config2.MakeRechargeable(); config2.batPercentRemaining = 200; config2.batChargingCurrent = 5000; // 5A config2.usedOptionalAttributes = attrSet2; MyBatteryPowerSourceCluster::Config config3("Replaceable battery cluster"_span, PowerSource::BatReplaceabilityEnum::kUserReplaceable, timerDelegate); // make it replaceable config3.MakeReplaceable("Description for replacement"_span, /* quantity */ 1); config3.batTimeRemaining = 3600; // 1h estimated uptime config3.batCapacity = 2000; // 2000 mAh capacity config3.usedOptionalAttributes = attrSet3; // the clusters RegisteredServerCluster<MyBatteryPowerSourceCluster> simpleBatteryInstance(powerSourceEndpointId1, config1); RegisteredServerCluster<MyBatteryPowerSourceCluster> rechargeableBatteryInstance(powerSourceEndpointId2, config2); RegisteredServerCluster<MyBatteryPowerSourceCluster> replaceableBatteryInstance(powerSourceEndpointId3, config3); // register the clusters, this will call `Startup` on them CodeDrivenDataModelProvider::Instance().Registry().Register(simpleBatteryInstance.Registration()); CodeDrivenDataModelProvider::Instance().Registry().Register(rechargeableBatteryInstance.Registration()); CodeDrivenDataModelProvider::Instance().Registry().Register(replaceableBatteryInstance.Registration()); // use the clusters ... replaceableBatteryInstance.Cluster().SetBatReplacementNeeded(true);
  • Configuration for one wired power source and one simple battery

    When it is needed to have both wired and battery power sources, it is not possible to do this with one template specialization by design.

     #include <app/clusters/power-source-server/PowerSourceCluster.h>
     #include <app/server-cluster/SingleEndpointServerClusterRegistry.h>
     #include <platform/DefaultTimerDelegate.h>
     #include <data-model-providers/codedriven/CodeDrivenDataModelProvider.h>
    
     using namespace app;
     using namespace app::Clusters;
    
     constexpr EndpointId powerSourceEndpointId1 = 1;
     constexpr EndpointId powerSourceEndpointId2 = 2;
    
     DefaultTimerDelegate timerDelegate;
    

    Define the template specializations to be used


    // enable all features that are used in any wired configuration (so just kWired) using MyWiredPowerSourceCluster = PowerSourceCluster<BitFlags<PowerSource::Feature>(PowerSource::Feature::kWired).Raw(), 0 /* no optional attributes */>; // enable kBattery for this type using MyBatteryPowerSourceCluster = PowerSourceCluster<BitFlags<PowerSource::Feature>(PowerSource::Feature::kBattery).Raw(), 0/* no optional attributes */>;

    Create config objects for the clusters, the clusters, and then use them


    // the configs MyWiredPowerSourceCluster::Config config1("Wired cluster"_span, PowerSource::WiredCurrentTypeEnum::kAc); config1.status = PowerSource::PowerSourceStatusEnum::kUnavailable; MyBatteryPowerSourceCluster::Config config2("Simple battery cluster"_span, PowerSource::BatReplaceabilityEnum::kNotReplaceable, timerDelegate); // the clusters RegisteredServerCluster<MyWiredPowerSourceCluster> wiredInstance(powerSourceEndpointId1, config1); RegisteredServerCluster<MyBatteryPowerSourceCluster> batteryInstance(powerSourceEndpointId2, config2); // register the clusters, this will call `Startup` on them CodeDrivenDataModelProvider::Instance().Registry().Register(wiredInstance.Registration()); CodeDrivenDataModelProvider::Instance().Registry().Register(batteryInstance.Registration()); // use the clusters ... wiredInstance.Cluster().SetOrder(2);

Notes

For simplicity there are 4 already named specializations of the class in the NamedPowerSourceClusters.h -

  • MinimalWiredPowerSourceCluster
  • MinimalBatteryPowerSourceCluster
  • FullWiredPowerSourceCluster (supports ALL wired optional attributes)
  • FullBatteryPowerSourceCluster (supports ALL battery optional attributes, and is BOTH replaceable and rechargeable)

Instead of RegisteredServerCluster one can use LazyRegisteredServerCluster which can defer the creation and overall provides dynamic lifetime management.

Some attributes can be can be set only in the config object, and not after the clusters creation, because they are Fixed attributes.

Again, all this is to have the cluster to be the smallest possible type for the functionality needed. So if you do not have any FLASH restrictions, you can use Full*PowerSourceClusters for everything.