blob: 77c2ba7fbc26728c5e6ffd7361d5c12a487821f8 [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 <app/clusters/on-off-server/OnOffCluster.h>
#include <app/clusters/on-off-server/OnOffEffectDelegate.h>
#include <app/clusters/scenes-server/ScenesIntegrationDelegate.h>
#include <cstdint>
#include <lib/support/TimerDelegate.h>
namespace chip::app::Clusters {
class OnOffLightingClusterTestAccess;
/// Implements the On/Off cluster with Lighting features.
///
/// This includes:
/// - GlobalSceneControl
/// - OnTime / OffWaitTime
/// - StartUpOnOff
/// - Timed commands
class OnOffLightingCluster : public OnOffCluster, public TimerContext
{
public:
enum class StartupType
{
kRegular,
// post OTA startup: on/off value must be preserved (i.e. ignore startup on off):
//
// Spec on StartupOnOff attribute says:
// This behavior does not apply to reboots associated with OTA.
// After an OTA restart, the OnOff attribute SHALL return to its value
// prior to the restart.
kOTA,
};
// Represents defaults for NVS values if no persistent storage set
struct Defaults
{
bool onOff{};
DataModel::Nullable<OnOff::StartUpOnOffEnum> startupOnOff{};
};
struct Context
{
TimerDelegate & timerDelegate;
OnOffEffectDelegate & effectDelegate;
chip::scenes::ScenesIntegrationDelegate * scenesIntegrationDelegate = nullptr;
BitMask<OnOff::Feature> featureMap = OnOff::Feature::kLighting;
StartupType startupType = StartupType::kRegular;
Defaults defaults{};
};
OnOffLightingCluster(EndpointId endpointId, const Context & context);
~OnOffLightingCluster() override;
uint16_t GetOnTime() const { return mOnTime; }
void SetOnTime(uint16_t value);
uint16_t GetOffWaitTime() const { return mOffWaitTime; }
void SetOffWaitTime(uint16_t value);
DataModel::Nullable<OnOff::StartUpOnOffEnum> GetStartupOnOff() const { return mStartUpOnOff; }
CHIP_ERROR SetStartupOnOff(DataModel::Nullable<OnOff::StartUpOnOffEnum> value);
// ServerClusterInterface implementation
CHIP_ERROR Startup(ServerClusterContext & context) override;
CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override;
CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder) override;
DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
AttributeValueEncoder & encoder) override;
DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
AttributeValueDecoder & decoder) override;
std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
chip::TLV::TLVReader & input_arguments,
CommandHandler * handler) override;
/// Sets the OnOff value with special behavior for integration with other clusters.
///
/// In particular, the 1.5.4.1 Lighting feature requires that if the Level Control cluster sets
/// the OnOff value, then:
/// - if value is set to false, OnTime is set to 0
/// - if value is set to true, OffWaitTime is set to 0
CHIP_ERROR SetOnOffWithTimeReset(bool on);
// TimerContext
void TimerFired() override;
private:
OnOffEffectDelegate & mEffectDelegate;
chip::scenes::ScenesIntegrationDelegate * mScenesIntegrationDelegate;
// Lighting Attributes
bool mGlobalSceneControl = true;
// On/Off times represent delayed on/off timeouts in 1/10th second resolution.
// They are updated by the underlying timer context (see TimerFired) according to the spec.
//
// The cluster may be in the following states:
// - ON - On (no timers)
// - OFF - Off (no timers)
// - TIMED_ON - On with a timer set (mOnTime decrements and will turn off on 0)
// - TIMED_OFF - Off with a timer set (mOffWaitTime decrements and will turn on on 0)
//
// Values are affected by the following:
// - HandleOnWithTimedOff (see spec diagram)
// - TimerFired (decreases times)
// - SetOnOffFromCommand (needs to transition to on/off correctly)
uint16_t mOnTime = 0;
uint16_t mOffWaitTime = 0;
// controlling cluster startup - these values are only used at startup however
// user may modify mStartUpOnOff attribute as it is writable.
DataModel::Nullable<OnOff::StartUpOnOffEnum> mStartUpOnOff;
StartupType mStartupType;
DataModel::ActionReturnStatus WriteImpl(const DataModel::WriteAttributeRequest & request, AttributeValueDecoder & decoder);
// Timer logic
void UpdateTimer();
// Command Handlers
DataModel::ActionReturnStatus HandleOffWithEffect(const DataModel::InvokeRequest & request,
chip::TLV::TLVReader & input_arguments);
DataModel::ActionReturnStatus HandleOnWithRecallGlobalScene(const DataModel::InvokeRequest & request);
DataModel::ActionReturnStatus HandleOnWithTimedOff(chip::TLV::TLVReader & input_arguments);
// Wrappers for basic commands to add lighting side effects
DataModel::ActionReturnStatus HandleOn();
DataModel::ActionReturnStatus HandleOff();
DataModel::ActionReturnStatus HandleToggle();
// Handles transitions for timed on/off. This is to be used by commands EXCEPT
// HandleOnWithTimedOff to represent the spec `Any command which causes the attribute to be set
// to TRUE/FALSE` like on/off/toggle/OnWithRecallGlobalScene/OffWithEffect
CHIP_ERROR SetOnOffFromCommand(bool on);
};
} // namespace chip::app::Clusters