blob: bbd9c8feb5c203857ef79418044709a450f50a23 [file]
/*
*
* Copyright (c) 2026 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "Modules.h"
#include <algorithm>
#include <app/persistence/AttributePersistence.h>
#include <app/server-cluster/AttributeListBuilder.h>
#include <app/server-cluster/DefaultServerCluster.h>
#include <app/server-cluster/OptionalAttributeSet.h>
#include <clusters/PowerSource/Attributes.h>
#include <clusters/PowerSource/Commands.h>
#include <clusters/PowerSource/Enums.h>
#include <clusters/PowerSource/Events.h>
#include <clusters/PowerSource/Metadata.h>
#include <lib/support/ScopedMemoryBuffer.h>
#include <lib/support/TimerDelegate.h>
#include <limits>
namespace chip::app::Clusters {
namespace PowerSource::detail {
template <size_t MaxVal>
constexpr static BitSetType SpanToBitSet(Span<const uint8_t> span)
{
static_assert(MaxVal < kBitSetSize, "MaxVal must be less than kBitSetSize to fit in the BitSetType");
BitSetType val{};
for (uint8_t el : span)
{
if (el < MaxVal)
{
val.set(el);
}
}
return val;
}
template <class = void> // to silence "unused" warnings
static void BitSetToSpan(BitSetType bitset, Span<uint8_t> & buffer)
{
size_t bufInd = 0;
for (uint8_t i = 0; i < bitset.size() && bufInd < buffer.size(); i++)
{
if (bitset.test(i))
{
buffer[bufInd++] = i;
}
}
buffer.reduce_size(bufInd);
}
template <class To, class From, class = std::enable_if_t<sizeof(From) == sizeof(To), void>>
constexpr static Span<To> ConvertSpanType(Span<From> span)
{
return Span(reinterpret_cast<To *>(span.data()), span.size());
}
template <typename T>
CHIP_ERROR EncodeListOfValues(AttributeValueEncoder & encoder, const T & valueList)
{
return encoder.EncodeList([&valueList](const auto & enc) -> CHIP_ERROR {
for (const auto & value : valueList)
{
ReturnErrorOnFailure(enc.Encode(value));
}
return CHIP_NO_ERROR;
});
}
constexpr static AttributeSet GetValidOptionalAttributeSet(AttributeSet optionalAttributeSet,
BitFlags<PowerSource::Feature> features)
{
using namespace PowerSource::Attributes;
constexpr uint32_t wiredMandatoryAttributeBits = OptionalAttributeSet<WiredCurrentType::Id>::All();
constexpr uint32_t wiredAttributeBits =
OptionalAttributeSet<WiredAssessedInputVoltage::Id, WiredAssessedInputFrequency::Id, WiredCurrentType::Id,
WiredAssessedCurrent::Id, WiredNominalVoltage::Id, WiredMaximumCurrent::Id, WiredPresent::Id,
ActiveWiredFaults::Id>::All();
constexpr uint32_t batteryMandatoryAttributeBits =
OptionalAttributeSet<BatChargeLevel::Id, BatReplacementNeeded::Id, BatReplaceability::Id>::All();
constexpr uint32_t batteryAttributeBits =
OptionalAttributeSet<BatVoltage::Id, BatPercentRemaining::Id, BatTimeRemaining::Id, BatChargeLevel::Id,
BatReplacementNeeded::Id, BatReplaceability::Id, BatPresent::Id, ActiveBatFaults::Id>::All();
constexpr uint32_t replaceableBatteryMandatoryAttributeBits =
OptionalAttributeSet<BatReplacementDescription::Id, BatQuantity::Id>::All();
constexpr uint32_t replaceableBatteryAttributeBits =
OptionalAttributeSet<BatReplacementDescription::Id, BatCommonDesignation::Id, BatANSIDesignation::Id, BatIECDesignation::Id,
BatApprovedChemistry::Id, BatQuantity::Id>::All();
constexpr uint32_t rechargeableBatteryMandatoryAttributeBits =
OptionalAttributeSet<BatChargeState::Id, BatFunctionalWhileCharging::Id>::All();
constexpr uint32_t rechargeableBatteryAttributeBits =
OptionalAttributeSet<BatChargeState::Id, BatTimeToFullCharge::Id, BatFunctionalWhileCharging::Id, BatChargingCurrent::Id,
ActiveBatChargeFaults::Id>::All();
constexpr uint32_t capacityAttributeBit = OptionalAttributeSet<BatCapacity::Id>::All();
uint32_t mandatoryBits = 0;
uint32_t disabledBits = 0;
if (features.Has(PowerSource::Feature::kWired))
{
mandatoryBits |= wiredMandatoryAttributeBits;
}
else
{
disabledBits |= wiredAttributeBits;
}
if (features.Has(PowerSource::Feature::kBattery))
{
mandatoryBits |= batteryMandatoryAttributeBits;
}
else
{
disabledBits |= batteryAttributeBits;
}
if (features.Has(PowerSource::Feature::kReplaceable))
{
mandatoryBits |= replaceableBatteryMandatoryAttributeBits;
}
else
{
disabledBits |= replaceableBatteryAttributeBits;
}
if (features.Has(PowerSource::Feature::kRechargeable))
{
mandatoryBits |= rechargeableBatteryMandatoryAttributeBits;
}
else
{
disabledBits |= rechargeableBatteryAttributeBits;
}
if (!(features.Has(PowerSource::Feature::kReplaceable) || features.Has(PowerSource::Feature::kRechargeable)))
{
disabledBits |= capacityAttributeBit;
}
uint32_t finalBits = (optionalAttributeSet.Raw() | mandatoryBits) & ~disabledBits;
return AttributeSet(finalBits);
}
} // namespace PowerSource::detail
using PowerSourceOptionalAttributeSet =
app::OptionalAttributeSet<PowerSource::Attributes::WiredAssessedInputVoltage::Id,
PowerSource::Attributes::WiredAssessedInputFrequency::Id,
PowerSource::Attributes::WiredAssessedCurrent::Id, PowerSource::Attributes::WiredNominalVoltage::Id,
PowerSource::Attributes::WiredMaximumCurrent::Id, PowerSource::Attributes::WiredPresent::Id,
PowerSource::Attributes::ActiveWiredFaults::Id, PowerSource::Attributes::BatVoltage::Id,
PowerSource::Attributes::BatPercentRemaining::Id, PowerSource::Attributes::BatTimeRemaining::Id,
PowerSource::Attributes::BatPresent::Id, PowerSource::Attributes::ActiveBatFaults::Id,
PowerSource::Attributes::BatCommonDesignation::Id, PowerSource::Attributes::BatANSIDesignation::Id,
PowerSource::Attributes::BatIECDesignation::Id, PowerSource::Attributes::BatApprovedChemistry::Id,
PowerSource::Attributes::BatCapacity::Id, PowerSource::Attributes::BatTimeToFullCharge::Id,
PowerSource::Attributes::BatChargingCurrent::Id, PowerSource::Attributes::ActiveBatChargeFaults::Id>;
template <std::underlying_type_t<PowerSource::Feature> supportedFeatureBits, uint32_t supportedOptionalAttributeBits>
struct PowerSourceClusterConfig : public PowerSource::detail::AllModules<supportedFeatureBits, supportedOptionalAttributeBits>
{
constexpr static BitFlags<PowerSource::Feature> supportedFeatures{ supportedFeatureBits };
static_assert(supportedFeatures.Has(PowerSource::Feature::kWired) ^ supportedFeatures.Has(PowerSource::Feature::kBattery),
"Exactly one of Wired or Battery features must be set");
constexpr static AttributeSet supportedOptionalAttributeSet =
PowerSource::detail::GetValidOptionalAttributeSet(AttributeSet(supportedOptionalAttributeBits), supportedFeatures);
PowerSourceClusterConfig(CharSpan desc, PowerSource::WiredCurrentTypeEnum currType)
{
static_assert(supportedFeatures.Has(PowerSource::Feature::kWired),
"This constructor should only be used for a Wired power source configuration");
this->description = desc;
this->wiredCurrentType = currType;
}
PowerSourceClusterConfig(CharSpan desc, PowerSource::BatReplaceabilityEnum replability, TimerDelegate & timerDelegate)
{
static_assert(supportedFeatures.Has(PowerSource::Feature::kBattery),
"This constructor should only be used for a Battery power source configuration");
this->description = desc;
this->batReplaceability = replability;
this->mTimerDelegate = &timerDelegate;
}
TimerDelegate & GetTimerDelegate()
{
static_assert(supportedFeatures.Has(PowerSource::Feature::kBattery),
"TimerDelegate is only relevant for a Battery power source configuration");
return *this->mTimerDelegate;
}
PowerSourceClusterConfig & MakeReplaceable(CharSpan replDesc, uint8_t quan)
{
static_assert(supportedFeatures.Has(PowerSource::Feature::kReplaceable),
"This method can only be used for a power source configuration that supports being replaceable");
this->batReplacementDescription = replDesc;
this->batQuantity = quan;
this->batReplaceable = true;
return *this;
}
PowerSourceClusterConfig & MakeRechargeable()
{
static_assert(supportedFeatures.Has(PowerSource::Feature::kRechargeable),
"This method can only be used for a power source configuration that supports being rechargeable");
this->batRechargeable = true;
return *this;
}
PowerSourceOptionalAttributeSet usedOptionalAttributes{ UINT32_MAX }; // all supported attributes are marked as used by default.
};
template <std::underlying_type_t<PowerSource::Feature> supportedFeatureBits, uint32_t supportedOptionalAttributeBits>
class PowerSourceCluster : protected PowerSourceClusterConfig<supportedFeatureBits, supportedOptionalAttributeBits>,
public DefaultServerCluster,
public PowerSource::detail::BatteryTimerContext::NotifierDelegate,
protected PowerSource::detail::BatteryTimerContextsModule<
BitFlags<PowerSource::Feature>(supportedFeatureBits).Has(PowerSource::Feature::kBattery)>
{
public:
using Config = PowerSourceClusterConfig<supportedFeatureBits, supportedOptionalAttributeBits>;
using Config::supportedFeatures;
using Config::supportedOptionalAttributeSet;
using BitSetType = typename PowerSource::detail::BitSetType;
using PowerSourceStatusEnum = PowerSource::PowerSourceStatusEnum;
using WiredCurrentTypeEnum = PowerSource::WiredCurrentTypeEnum;
using WiredFaultEnum = PowerSource::WiredFaultEnum;
using BatChargeLevelEnum = PowerSource::BatChargeLevelEnum;
using BatReplaceabilityEnum = PowerSource::BatReplaceabilityEnum;
using BatFaultEnum = PowerSource::BatFaultEnum;
using BatCommonDesignationEnum = PowerSource::BatCommonDesignationEnum;
using BatApprovedChemistryEnum = PowerSource::BatApprovedChemistryEnum;
using BatChargeStateEnum = PowerSource::BatChargeStateEnum;
using BatChargeFaultEnum = PowerSource::BatChargeFaultEnum;
static_assert(supportedFeatures.Has(PowerSource::Feature::kWired) ^ supportedFeatures.Has(PowerSource::Feature::kBattery),
"Exactly one of Wired or Battery features must be set");
template <bool batteryFeatureNotSupported = !supportedFeatures.Has(PowerSource::Feature::kBattery),
std::enable_if_t<batteryFeatureNotSupported, int> = 0>
PowerSourceCluster(EndpointId endpointId, const Config & config) :
Config(config), DefaultServerCluster({ endpointId, PowerSource::Id })
{}
template <bool batteryFeatureSupported = supportedFeatures.Has(PowerSource::Feature::kBattery),
std::enable_if_t<batteryFeatureSupported, size_t> = 0>
PowerSourceCluster(EndpointId endpointId, const Config & config) :
Config(config), DefaultServerCluster({ endpointId, PowerSource::Id }),
PowerSource::detail::BatteryTimerContextsModule<batteryFeatureSupported>(Config::GetTimerDelegate(), *this)
{}
// implement `PowerSource::detail::BatteryTimerContext::NotifierDelegate`
void Notify(AttributeId id) override { NotifyAttributeChanged(id); }
DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder) override
{
using namespace PowerSource::Attributes;
using namespace PowerSource::detail;
// `ReadAttribute` is guaranteed to only be called for attributes that are supported by the cluster, so the code below is
// valid.
AttributeId id = request.path.mAttributeId;
switch (id)
{
case Status::Id:
return encoder.Encode(this->status);
case Order::Id:
return encoder.Encode(this->order);
case Description::Id:
return encoder.Encode(
this->description.SubSpan(0, std::min(this->description.size(), Description::TypeInfo::MaxLength())));
case EndpointList::Id:
return EncodeListOfValues(encoder, GetEndpointList());
case Globals::Attributes::FeatureMap::Id:
return encoder.Encode(Features().Raw());
case Globals::Attributes::ClusterRevision::Id:
return encoder.Encode(PowerSource::kRevision);
// Feature dependent and optional attributes
case WiredAssessedInputVoltage::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(WiredAssessedInputVoltage::Id))
{
return encoder.Encode(this->wiredAssessedInputVoltage);
}
break;
case WiredAssessedInputFrequency::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(WiredAssessedInputFrequency::Id))
{
return encoder.Encode(this->wiredAssessedInputFrequency);
}
break;
case WiredCurrentType::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(WiredCurrentType::Id))
{
return encoder.Encode(this->wiredCurrentType);
}
break;
case WiredAssessedCurrent::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(WiredAssessedCurrent::Id))
{
return encoder.Encode(this->wiredAssessedCurrent);
}
break;
case WiredNominalVoltage::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(WiredNominalVoltage::Id))
{
return encoder.Encode(this->wiredNominalVoltage);
}
break;
case WiredMaximumCurrent::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(WiredMaximumCurrent::Id))
{
return encoder.Encode(this->wiredMaximumCurrent);
}
break;
case WiredPresent::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(WiredPresent::Id))
{
return encoder.Encode(this->wiredPresent);
}
break;
case ActiveWiredFaults::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(ActiveWiredFaults::Id))
{
uint8_t faultsBuf[to_underlying(WiredFaultEnum::kUnknownEnumValue)];
auto faultsSpan = Span(faultsBuf, to_underlying(WiredFaultEnum::kUnknownEnumValue));
BitSetToSpan(this->activeWiredFaultsBitSet, faultsSpan);
return EncodeListOfValues(encoder, faultsSpan);
}
break;
case BatVoltage::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatVoltage::Id))
{
return encoder.Encode(this->batVoltage);
}
break;
case BatPercentRemaining::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatPercentRemaining::Id))
{
return encoder.Encode(this->batPercentRemaining);
}
break;
case BatTimeRemaining::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatTimeRemaining::Id))
{
return encoder.Encode(this->batTimeRemaining);
}
break;
case BatChargeLevel::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatChargeLevel::Id))
{
return encoder.Encode(this->batChargeLevel);
}
break;
case BatReplacementNeeded::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatReplacementNeeded::Id))
{
return encoder.Encode(this->batReplacementNeeded);
}
break;
case BatReplaceability::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatReplaceability::Id))
{
return encoder.Encode(this->batReplaceability);
}
break;
case BatPresent::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatPresent::Id))
{
return encoder.Encode(this->batPresent);
}
break;
case ActiveBatFaults::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(ActiveBatFaults::Id))
{
uint8_t faultsBuf[to_underlying(BatFaultEnum::kUnknownEnumValue)];
auto faultsSpan = Span(faultsBuf, to_underlying(BatFaultEnum::kUnknownEnumValue));
BitSetToSpan(this->activeBatFaultsBitSet, faultsSpan);
return EncodeListOfValues(encoder, faultsSpan);
}
break;
case BatReplacementDescription::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatReplacementDescription::Id))
{
return encoder.Encode(this->batReplacementDescription.SubSpan(
0, std::min(this->batReplacementDescription.size(), BatReplacementDescription::TypeInfo::MaxLength())));
}
break;
case BatCommonDesignation::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatCommonDesignation::Id))
{
return encoder.Encode(this->batCommonDesignation);
}
break;
case BatANSIDesignation::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatANSIDesignation::Id))
{
return encoder.Encode(this->batANSIDesignation.SubSpan(
0, std::min(this->batANSIDesignation.size(), BatANSIDesignation::TypeInfo::MaxLength())));
}
break;
case BatIECDesignation::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatIECDesignation::Id))
{
return encoder.Encode(this->batIECDesignation.SubSpan(
0, std::min(this->batIECDesignation.size(), BatIECDesignation::TypeInfo::MaxLength())));
}
break;
case BatApprovedChemistry::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatApprovedChemistry::Id))
{
return encoder.Encode(this->batApprovedChemistry);
}
break;
case BatCapacity::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatCapacity::Id))
{
return encoder.Encode(this->batCapacity);
}
break;
case BatQuantity::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatQuantity::Id))
{
return encoder.Encode(this->batQuantity);
}
break;
case BatChargeState::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatChargeState::Id))
{
return encoder.Encode(this->batChargeState);
}
break;
case BatTimeToFullCharge::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatTimeToFullCharge::Id))
{
return encoder.Encode(this->batTimeToFullCharge);
}
break;
case BatFunctionalWhileCharging::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatFunctionalWhileCharging::Id))
{
return encoder.Encode(this->batFunctionalWhileCharging);
}
break;
case BatChargingCurrent::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(BatChargingCurrent::Id))
{
return encoder.Encode(this->batChargingCurrent);
}
break;
case ActiveBatChargeFaults::Id:
if constexpr (supportedOptionalAttributeSet.IsSet(ActiveBatChargeFaults::Id))
{
uint8_t faultsBuf[to_underlying(BatChargeFaultEnum::kUnknownEnumValue)];
auto faultsSpan = Span(faultsBuf, to_underlying(BatChargeFaultEnum::kUnknownEnumValue));
BitSetToSpan(this->activeBatChargeFaultsBitSet, faultsSpan);
return EncodeListOfValues(encoder, faultsSpan);
}
break;
default:
break;
}
return Protocols::InteractionModel::Status::UnsupportedAttribute;
}
CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override
{
using namespace PowerSource::Attributes;
AttributeListBuilder attributeListBuilder(builder);
AttributeSet optionalAttributeSet(
PowerSource::detail::GetValidOptionalAttributeSet(this->usedOptionalAttributes, Features()).Raw() &
supportedOptionalAttributeSet.Raw());
constexpr DataModel::AttributeEntry kOptionalAttributes[] = { WiredAssessedInputVoltage::kMetadataEntry,
WiredAssessedInputFrequency::kMetadataEntry,
WiredCurrentType::kMetadataEntry,
WiredAssessedCurrent::kMetadataEntry,
WiredNominalVoltage::kMetadataEntry,
WiredMaximumCurrent::kMetadataEntry,
WiredPresent::kMetadataEntry,
ActiveWiredFaults::kMetadataEntry,
BatVoltage::kMetadataEntry,
BatPercentRemaining::kMetadataEntry,
BatTimeRemaining::kMetadataEntry,
BatChargeLevel::kMetadataEntry,
BatReplacementNeeded::kMetadataEntry,
BatReplaceability::kMetadataEntry,
BatPresent::kMetadataEntry,
ActiveBatFaults::kMetadataEntry,
BatReplacementDescription::kMetadataEntry,
BatCommonDesignation::kMetadataEntry,
BatANSIDesignation::kMetadataEntry,
BatIECDesignation::kMetadataEntry,
BatApprovedChemistry::kMetadataEntry,
BatCapacity::kMetadataEntry,
BatQuantity::kMetadataEntry,
BatChargeState::kMetadataEntry,
BatTimeToFullCharge::kMetadataEntry,
BatFunctionalWhileCharging::kMetadataEntry,
BatChargingCurrent::kMetadataEntry,
ActiveBatChargeFaults::kMetadataEntry };
return attributeListBuilder.Append(Span(kMandatoryMetadata), Span(kOptionalAttributes), optionalAttributeSet);
}
#define ENABLE_IF_ATTRIBUTE_SUPPORTED(attrName) \
static_assert(supportedOptionalAttributeSet.IsSet(PowerSource::Attributes::attrName::Id), \
"This method can only be used if the " #attrName " attribute is supported"); \
if constexpr (!supportedOptionalAttributeSet.IsSet(PowerSource::Attributes::attrName::Id)) \
{ \
return {}; \
} \
else
#define ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(attrName) \
static_assert(supportedOptionalAttributeSet.IsSet(PowerSource::Attributes::attrName::Id), \
"This method can only be used if the " #attrName " attribute is supported"); \
if constexpr (supportedOptionalAttributeSet.IsSet(PowerSource::Attributes::attrName::Id))
// Getters
PowerSourceStatusEnum GetStatus() const { return this->status; }
uint8_t GetOrder() const { return this->order; }
CharSpan GetDescription() const { return this->description; }
DataModel::Nullable<uint32_t> GetWiredAssessedInputVoltage() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(WiredAssessedInputVoltage)
{
return this->wiredAssessedInputVoltage;
}
}
DataModel::Nullable<uint16_t> GetWiredAssessedInputFrequency() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(WiredAssessedInputFrequency)
{
return this->wiredAssessedInputFrequency;
}
}
WiredCurrentTypeEnum GetWiredCurrentType() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(WiredCurrentType)
{
return this->wiredCurrentType;
}
}
DataModel::Nullable<uint32_t> GetWiredAssessedCurrent() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(WiredAssessedCurrent)
{
return this->wiredAssessedCurrent;
}
}
uint32_t GetWiredNominalVoltage() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(WiredNominalVoltage)
{
return this->wiredNominalVoltage;
}
}
uint32_t GetWiredMaximumCurrent() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(WiredMaximumCurrent)
{
return this->wiredMaximumCurrent;
}
}
bool GetWiredPresent() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(WiredPresent)
{
return this->wiredPresent;
}
}
void GetActiveWiredFaults(Span<WiredFaultEnum> & buffer) const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(ActiveWiredFaults)
{
auto span = PowerSource::detail::ConvertSpanType<uint8_t>(buffer);
PowerSource::detail::BitSetToSpan(this->activeWiredFaultsBitSet, span);
buffer = PowerSource::detail::ConvertSpanType<WiredFaultEnum>(span);
}
}
DataModel::Nullable<uint32_t> GetBatVoltage() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatVoltage)
{
return this->batVoltage;
}
}
DataModel::Nullable<uint8_t> GetBatPercentRemaining() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatPercentRemaining)
{
return this->batPercentRemaining;
}
}
DataModel::Nullable<uint32_t> GetBatTimeRemaining() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatTimeRemaining)
{
return this->batTimeRemaining;
}
}
BatChargeLevelEnum GetBatChargeLevel() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatChargeLevel)
{
return this->batChargeLevel;
}
}
bool GetBatReplacementNeeded() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatReplacementNeeded)
{
return this->batReplacementNeeded;
}
}
BatReplaceabilityEnum GetBatReplaceability() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatReplaceability)
{
return this->batReplaceability;
}
}
bool GetBatPresent() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatPresent)
{
return this->batPresent;
}
}
void GetActiveBatFaults(Span<BatFaultEnum> & buffer) const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(ActiveBatFaults)
{
auto span = PowerSource::detail::ConvertSpanType<uint8_t>(buffer);
PowerSource::detail::BitSetToSpan(this->activeBatFaultsBitSet, span);
buffer = PowerSource::detail::ConvertSpanType<BatFaultEnum>(span);
}
}
CharSpan GetBatReplacementDescription() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatReplacementDescription)
{
return this->batReplacementDescription;
}
}
BatCommonDesignationEnum GetBatCommonDesignation() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatCommonDesignation)
{
return this->batCommonDesignation;
}
}
CharSpan GetBatANSIDesignation() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatANSIDesignation)
{
return this->batANSIDesignation;
}
}
CharSpan GetBatIECDesignation() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatIECDesignation)
{
return this->batIECDesignation;
}
}
BatApprovedChemistryEnum GetBatApprovedChemistry() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatApprovedChemistry)
{
return this->batApprovedChemistry;
}
}
uint32_t GetBatCapacity() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatCapacity)
{
return this->batCapacity;
}
}
uint8_t GetBatQuantity() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatQuantity)
{
return this->batQuantity;
}
}
BatChargeStateEnum GetBatChargeState() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatChargeState)
{
return this->batChargeState;
}
}
DataModel::Nullable<uint32_t> GetBatTimeToFullCharge() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatTimeToFullCharge)
{
return this->batTimeToFullCharge;
}
}
bool GetBatFunctionalWhileCharging() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatFunctionalWhileCharging)
{
return this->batFunctionalWhileCharging;
}
}
DataModel::Nullable<uint32_t> GetBatChargingCurrent() const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatChargingCurrent)
{
return this->batChargingCurrent;
}
}
void GetActiveBatChargeFaults(Span<BatChargeFaultEnum> & buffer) const
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(ActiveBatChargeFaults)
{
auto span = PowerSource::detail::ConvertSpanType<uint8_t>(buffer);
PowerSource::detail::BitSetToSpan(this->activeBatChargeFaultsBitSet, span);
buffer = PowerSource::detail::ConvertSpanType<BatChargeFaultEnum>(span);
}
}
Span<const EndpointId> GetEndpointList() const { return this->endpointList; }
// Setters
/// Attributes marked with the `Fixed` quality do not have setters.
/// They can be only set during construction using the `PowerSourceClusterConfig` class.
/// `Fixed` attributes are `Description`, `WiredCurrentType`, `WiredNominalVoltage`, `WiredMaximumCurrent`,
/// `BatReplaceability`, `BatReplacementDescription`, `BatCommonDesignation`, `BatANSIDesignation`, `BatIECDesignation`,
/// `BatApprovedChemistry`, `BatCapacity`, `BatQuantity`.
CHIP_ERROR SetStatus(PowerSourceStatusEnum val)
{
VerifyOrReturnError(val != PowerSourceStatusEnum::kUnknownEnumValue, CHIP_ERROR_INVALID_ARGUMENT);
SetAttributeValue(this->status, val, PowerSource::Attributes::Status::Id);
return CHIP_NO_ERROR;
}
void SetOrder(uint8_t val)
{
// This attribute is marked as `Persistent` but the cluster will not support the persistence because the attribute is
// `ReadOnly` to clients.
SetAttributeValue(this->order, val, PowerSource::Attributes::Order::Id);
}
void SetWiredAssessedInputVoltage(DataModel::Nullable<uint32_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(WiredAssessedInputVoltage)
{
this->wiredAssessedInputVoltage = val;
// no notifying because attribute marked with 'Changes Omitted' quality
}
}
void SetWiredAssessedInputFrequency(DataModel::Nullable<uint16_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(WiredAssessedInputFrequency)
{
this->wiredAssessedInputFrequency = val;
// no notifying because attribute marked with 'Changes Omitted' quality
}
}
void SetWiredAssessedCurrent(DataModel::Nullable<uint32_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(WiredAssessedCurrent)
{
this->wiredAssessedCurrent = val;
// no notifying because attribute marked with 'Changes Omitted' quality
}
}
void SetWiredPresent(bool val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(WiredPresent)
{
SetAttributeValue(this->wiredPresent, val, PowerSource::Attributes::WiredPresent::Id);
}
}
void SetActiveWiredFaults(Span<const WiredFaultEnum> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(ActiveWiredFaults)
{
BitSetType bitset = PowerSource::detail::SpanToBitSet<to_underlying(WiredFaultEnum::kUnknownEnumValue)>(
PowerSource::detail::ConvertSpanType<const uint8_t>(val));
if (this->activeWiredFaultsBitSet != bitset)
{
GenerateWiredFaultEventAndSetAndNotify(bitset);
}
}
}
CHIP_ERROR AddActiveWiredFault(WiredFaultEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(ActiveWiredFaults)
{
if (val == WiredFaultEnum::kUnknownEnumValue)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (this->activeWiredFaultsBitSet.test(to_underlying(val)))
{
return CHIP_NO_ERROR;
}
BitSetType newBitSet = this->activeWiredFaultsBitSet;
newBitSet.set(to_underlying(val));
GenerateWiredFaultEventAndSetAndNotify(newBitSet);
return CHIP_NO_ERROR;
}
}
CHIP_ERROR RemoveActiveWiredFault(WiredFaultEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(ActiveWiredFaults)
{
if (val == WiredFaultEnum::kUnknownEnumValue)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (!this->activeWiredFaultsBitSet.test(to_underlying(val)))
{
return CHIP_NO_ERROR;
}
BitSetType newBitSet = this->activeWiredFaultsBitSet;
newBitSet.set(to_underlying(val), false);
GenerateWiredFaultEventAndSetAndNotify(newBitSet);
return CHIP_NO_ERROR;
}
}
void SetBatVoltage(DataModel::Nullable<uint32_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(BatVoltage)
{
this->batVoltage = val;
// no notifying because attribute marked with 'Changes Omitted' quality
}
}
CHIP_ERROR SetBatPercentRemaining(DataModel::Nullable<uint8_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatPercentRemaining)
{
if (!val.IsNull())
{
// Maximum value of 200 representing 100% battery.
VerifyOrReturnError(val.Value() <= 200, CHIP_IM_GLOBAL_STATUS(ConstraintError));
}
return SetQuietNullableAttribute(this->batPercentRemaining, val, this->batPercentRemainingNotifyTimerContext);
}
}
CHIP_ERROR SetBatTimeRemaining(DataModel::Nullable<uint32_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatTimeRemaining)
{
return SetQuietNullableAttribute(this->batTimeRemaining, val, this->batTimeRemainingNotifyTimerContext);
}
}
CHIP_ERROR SetBatChargeLevel(BatChargeLevelEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatChargeLevel)
{
VerifyOrReturnError(val != BatChargeLevelEnum::kUnknownEnumValue, CHIP_ERROR_INVALID_ARGUMENT);
SetAttributeValue(this->batChargeLevel, val, PowerSource::Attributes::BatChargeLevel::Id);
return CHIP_NO_ERROR;
}
}
void SetBatReplacementNeeded(bool val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(BatReplacementNeeded)
{
SetAttributeValue(this->batReplacementNeeded, val, PowerSource::Attributes::BatReplacementNeeded::Id);
}
}
void SetBatPresent(bool val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(BatPresent)
{
SetAttributeValue(this->batPresent, val, PowerSource::Attributes::BatPresent::Id);
}
}
void SetActiveBatFaults(Span<const BatFaultEnum> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(ActiveBatFaults)
{
BitSetType newBitSet = PowerSource::detail::SpanToBitSet<to_underlying(BatFaultEnum::kUnknownEnumValue)>(
PowerSource::detail::ConvertSpanType<const uint8_t>(val));
if (this->activeBatFaultsBitSet != newBitSet)
{
GenerateBatFaultEventAndSetAndNotify(newBitSet);
}
}
}
CHIP_ERROR AddActiveBatFault(BatFaultEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(ActiveBatFaults)
{
if (val == BatFaultEnum::kUnknownEnumValue)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (this->activeBatFaultsBitSet.test(to_underlying(val)))
{
return CHIP_NO_ERROR;
}
BitSetType newBitSet = this->activeBatFaultsBitSet;
newBitSet.set(to_underlying(val));
GenerateBatFaultEventAndSetAndNotify(newBitSet);
return CHIP_NO_ERROR;
}
}
CHIP_ERROR RemoveActiveBatFault(BatFaultEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(ActiveBatFaults)
{
if (val == BatFaultEnum::kUnknownEnumValue)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (!this->activeBatFaultsBitSet.test(to_underlying(val)))
{
return CHIP_NO_ERROR;
}
BitSetType newBitSet = this->activeBatFaultsBitSet;
newBitSet.set(to_underlying(val), false);
GenerateBatFaultEventAndSetAndNotify(newBitSet);
return CHIP_NO_ERROR;
}
}
CHIP_ERROR SetBatChargeState(BatChargeStateEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatChargeState)
{
VerifyOrReturnError(val != BatChargeStateEnum::kUnknownEnumValue, CHIP_ERROR_INVALID_ARGUMENT);
SetAttributeValue(this->batChargeState, val, PowerSource::Attributes::BatChargeState::Id);
return CHIP_NO_ERROR;
}
}
CHIP_ERROR SetBatTimeToFullCharge(DataModel::Nullable<uint32_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(BatTimeToFullCharge)
{
return SetQuietNullableAttribute(this->batTimeToFullCharge, val, this->batTimeToFullChargeNotifyTimerContext);
}
}
void SetBatFunctionalWhileCharging(bool val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(BatFunctionalWhileCharging)
{
SetAttributeValue(this->batFunctionalWhileCharging, val, PowerSource::Attributes::BatFunctionalWhileCharging::Id);
}
}
void SetBatChargingCurrent(DataModel::Nullable<uint32_t> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(BatChargingCurrent)
{
this->batChargingCurrent = val;
// no notifying because attribute marked with 'Changes Omitted' quality
}
}
void SetActiveBatChargeFaults(Span<const BatChargeFaultEnum> val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN(ActiveBatChargeFaults)
{
BitSetType newBitSet = PowerSource::detail::SpanToBitSet<to_underlying(BatChargeFaultEnum::kUnknownEnumValue)>(
PowerSource::detail::ConvertSpanType<const uint8_t>(val));
if (this->activeBatChargeFaultsBitSet != newBitSet)
{
GenerateBatChargeFaultEventAndSetAndNotify(newBitSet);
}
}
}
CHIP_ERROR AddActiveBatChargeFault(BatChargeFaultEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(ActiveBatChargeFaults)
{
if (val == BatChargeFaultEnum::kUnknownEnumValue)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (this->activeBatChargeFaultsBitSet.test(to_underlying(val)))
{
return CHIP_NO_ERROR;
}
BitSetType newBitSet = this->activeBatChargeFaultsBitSet;
newBitSet.set(to_underlying(val));
GenerateBatChargeFaultEventAndSetAndNotify(newBitSet);
return CHIP_NO_ERROR;
}
}
CHIP_ERROR RemoveActiveBatChargeFault(BatChargeFaultEnum val)
{
ENABLE_IF_ATTRIBUTE_SUPPORTED(ActiveBatChargeFaults)
{
if (val == BatChargeFaultEnum::kUnknownEnumValue)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (!this->activeBatChargeFaultsBitSet.test(to_underlying(val)))
{
return CHIP_NO_ERROR;
}
BitSetType newBitSet = this->activeBatChargeFaultsBitSet;
newBitSet.set(to_underlying(val), false);
GenerateBatChargeFaultEventAndSetAndNotify(newBitSet);
return CHIP_NO_ERROR;
}
}
/// This function takes the list by view, it doesn't store the data into the cluster.
/// If `CHIP_NO_ERROR` is returned, it is guaranteed that the internal `endpointList` points to the new storage, EVEN IF the
/// content is the same. If something other then `CHIP_NO_ERROR` is returned, the internal `endpointList` is not updated and
/// still points to the old storage. The underlying storage of `val` needs to be valid until this function is called again and a
/// `CHIP_NO_ERROR` is returned.
CHIP_ERROR SetEndpointList(Span<const EndpointId> val)
{
if (GetEndpointList().data_equal(val))
{
this->endpointList = val;
return CHIP_NO_ERROR;
}
for (const auto & endpointId : val)
{
VerifyOrReturnError(endpointId != kInvalidEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
}
this->endpointList = val;
NotifyAttributeChanged(PowerSource::Attributes::EndpointList::Id);
return CHIP_NO_ERROR;
}
#undef ENABLE_IF_ATTRIBUTE_SUPPORTED
#undef ENABLE_IF_ATTRIBUTE_SUPPORTED_NO_RETURN
private:
BitFlags<PowerSource::Feature> Features() const
{
if constexpr (supportedFeatures.Has(PowerSource::Feature::kWired))
{
return BitFlags<PowerSource::Feature>(PowerSource::Feature::kWired);
}
else
{
return BitFlags<PowerSource::Feature>(PowerSource::Feature::kBattery)
.Set(PowerSource::Feature::kReplaceable, this->batReplaceable)
.Set(PowerSource::Feature::kRechargeable, this->batRechargeable);
}
}
template <class T>
CHIP_ERROR SetQuietNullableAttribute(DataModel::Nullable<T> & oldVal, DataModel::Nullable<T> newVal,
PowerSource::detail::BatteryTimerContext & timerContext)
{
if (oldVal == newVal)
{
return CHIP_NO_ERROR;
}
if (oldVal.IsNull() || newVal.IsNull())
{
// Cancel the current timer to guarantee a notification with `NotifyOrSchedule` if needed
timerContext.CancelCurrentTimer();
}
oldVal = newVal;
if (!newVal.IsNull())
{
// if not a null value, notify with a timer to avoid too frequent notifications with non null values.
LogErrorOnFailure(timerContext.NotifyOrSchedule(newVal.Value()));
}
else
{
// if new value is null, notify immediately without timer, so a new non null value can be notified immediately without
// waiting for the timer.
NotifyAttributeChanged(timerContext.GetAttributeId());
}
return CHIP_NO_ERROR;
}
void GenerateWiredFaultEventAndSetAndNotify(BitSetType newBitSet)
{
if constexpr (!supportedOptionalAttributeSet.IsSet(PowerSource::Attributes::ActiveWiredFaults::Id))
{
return;
}
if (mContext != nullptr)
{
WiredFaultEnum oldBuf[to_underlying(WiredFaultEnum::kUnknownEnumValue)]{};
WiredFaultEnum newBuf[to_underlying(WiredFaultEnum::kUnknownEnumValue)]{};
auto oldSpan = Span(oldBuf, to_underlying(WiredFaultEnum::kUnknownEnumValue));
auto oldSpanBytes = PowerSource::detail::ConvertSpanType<uint8_t>(oldSpan);
PowerSource::detail::BitSetToSpan(this->activeWiredFaultsBitSet, oldSpanBytes);
oldSpan.reduce_size(oldSpanBytes.size());
auto newSpan = Span(newBuf, to_underlying(WiredFaultEnum::kUnknownEnumValue));
auto newSpanBytes = PowerSource::detail::ConvertSpanType<uint8_t>(newSpan);
PowerSource::detail::BitSetToSpan(newBitSet, newSpanBytes);
newSpan.reduce_size(newSpanBytes.size());
PowerSource::Events::WiredFaultChange::Type event_data{ oldSpan, newSpan };
mContext->interactionContext.eventsGenerator.GenerateEvent(event_data, mPath.mEndpointId);
}
SetAttributeValue(this->activeWiredFaultsBitSet, newBitSet, PowerSource::Attributes::ActiveWiredFaults::Id);
}
void GenerateBatFaultEventAndSetAndNotify(BitSetType newBitSet)
{
if constexpr (!supportedOptionalAttributeSet.IsSet(PowerSource::Attributes::ActiveBatFaults::Id))
{
return;
}
if (mContext != nullptr)
{
BatFaultEnum oldBuf[to_underlying(BatFaultEnum::kUnknownEnumValue)]{};
BatFaultEnum newBuf[to_underlying(BatFaultEnum::kUnknownEnumValue)]{};
auto oldSpan = Span(oldBuf, to_underlying(BatFaultEnum::kUnknownEnumValue));
auto oldSpanBytes = PowerSource::detail::ConvertSpanType<uint8_t>(oldSpan);
PowerSource::detail::BitSetToSpan(this->activeBatFaultsBitSet, oldSpanBytes);
oldSpan.reduce_size(oldSpanBytes.size());
auto newSpan = Span(newBuf, to_underlying(BatFaultEnum::kUnknownEnumValue));
auto newSpanBytes = PowerSource::detail::ConvertSpanType<uint8_t>(newSpan);
PowerSource::detail::BitSetToSpan(newBitSet, newSpanBytes);
newSpan.reduce_size(newSpanBytes.size());
PowerSource::Events::BatFaultChange::Type event_data{ oldSpan, newSpan };
mContext->interactionContext.eventsGenerator.GenerateEvent(event_data, mPath.mEndpointId);
}
SetAttributeValue(this->activeBatFaultsBitSet, newBitSet, PowerSource::Attributes::ActiveBatFaults::Id);
}
void GenerateBatChargeFaultEventAndSetAndNotify(BitSetType newBitSet)
{
if constexpr (!supportedOptionalAttributeSet.IsSet(PowerSource::Attributes::ActiveBatChargeFaults::Id))
{
return;
}
if (mContext != nullptr)
{
BatChargeFaultEnum oldBuf[to_underlying(BatChargeFaultEnum::kUnknownEnumValue)]{};
BatChargeFaultEnum newBuf[to_underlying(BatChargeFaultEnum::kUnknownEnumValue)]{};
auto oldSpan = Span(oldBuf, to_underlying(BatChargeFaultEnum::kUnknownEnumValue));
auto oldSpanBytes = PowerSource::detail::ConvertSpanType<uint8_t>(oldSpan);
PowerSource::detail::BitSetToSpan(this->activeBatChargeFaultsBitSet, oldSpanBytes);
oldSpan.reduce_size(oldSpanBytes.size());
auto newSpan = Span(newBuf, to_underlying(BatChargeFaultEnum::kUnknownEnumValue));
auto newSpanBytes = PowerSource::detail::ConvertSpanType<uint8_t>(newSpan);
PowerSource::detail::BitSetToSpan(newBitSet, newSpanBytes);
newSpan.reduce_size(newSpanBytes.size());
PowerSource::Events::BatChargeFaultChange::Type event_data{ oldSpan, newSpan };
mContext->interactionContext.eventsGenerator.GenerateEvent(event_data, mPath.mEndpointId);
}
SetAttributeValue(this->activeBatChargeFaultsBitSet, newBitSet, PowerSource::Attributes::ActiveBatChargeFaults::Id);
}
constexpr static System::Clock::Timeout kNotifyTimerDuration = System::Clock::Seconds16(10);
};
} // namespace chip::app::Clusters