Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fa46be1
Added rangehood app
bhmanda-silabs Oct 27, 2025
d1ba54b
Cleanedup rangehoodapp
bhmanda-silabs Oct 27, 2025
198abad
Resolved build errors
bhmanda-silabs Oct 28, 2025
cf49e8d
Added fanDelegate
bhmanda-silabs Oct 28, 2025
cec579c
Added button,LCD functionality to rangehood-app
bhmanda-silabs Oct 29, 2025
497571f
Restyled by whitespace
restyled-commits Oct 29, 2025
bf8f9f9
Restyled by clang-format
restyled-commits Oct 29, 2025
0cfc7bd
Restyled by gn
restyled-commits Oct 29, 2025
be0ec91
Removed speed related code
bhmanda-silabs Oct 31, 2025
9774090
Removed duplicate file
bhmanda-silabs Oct 31, 2025
10571ae
Resolved review comments
bhmanda-silabs Oct 31, 2025
df0099d
seperating common code
sabollim-silabs Nov 7, 2025
993bfda
restruncturing
sabollim-silabs Nov 10, 2025
b3e1cd7
PR comments
sabollim-silabs Nov 10, 2025
7242385
removing lightning feature in on/off
sabollim-silabs Nov 10, 2025
b379f88
Restyled by whitespace
restyled-commits Nov 10, 2025
0518d5f
Restyled by clang-format
restyled-commits Nov 10, 2025
f8a933f
Removed step command and lighting attributes which are not required
bhmanda-silabs Nov 10, 2025
491011d
Addressed review comments
bhmanda-silabs Nov 11, 2025
9b0f0ad
Added return status code for get and set on/off state APIs
bhmanda-silabs Nov 11, 2025
e524b0d
Resolved review comments
bhmanda-silabs Nov 11, 2025
756eb2e
Resolved build errors
bhmanda-silabs Nov 11, 2025
929417f
Restyled by whitespace
restyled-commits Nov 11, 2025
37e6fcf
Restyled by clang-format
restyled-commits Nov 11, 2025
a5e77f8
Restyled by gn
restyled-commits Nov 11, 2025
997a0fb
Resolved review comments
bhmanda-silabs Nov 13, 2025
2cdc309
Added construtor inplace of Init
bhmanda-silabs Nov 13, 2025
b58eac2
Restyled by whitespace
restyled-commits Nov 13, 2025
beed203
Restyled by clang-format
restyled-commits Nov 13, 2025
cdff3b3
Removed lock/unlock chipstack as it is having lock
bhmanda-silabs Nov 13, 2025
59ca047
Restyled by whitespace
restyled-commits Nov 13, 2025
62f33a3
Added a proper comment for lock/unlock chip stack
bhmanda-silabs Nov 13, 2025
4c9f3b5
Restyled by clang-format
restyled-commits Nov 13, 2025
a86b80e
Added proper function comments
bhmanda-silabs Nov 14, 2025
43e769c
Restyled by whitespace
restyled-commits Nov 14, 2025
f423e8c
Restyled by clang-format
restyled-commits Nov 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
*
* Copyright (c) 2025 Project CHIP Authors
* Copyright (c) 2025 Google LLC.
* All rights reserved.
*
* 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 <stdbool.h>
#include <stdint.h>

#include <app/clusters/fan-control-server/fan-control-server.h>
#include <app/data-model/Nullable.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>

class ExtractorHoodEndpoint
{
public:
/**
* @brief Construct the ExtractorHood endpoint.
* @param endpointId The endpoint ID
* @param lowPercent Percent value for Low mode (default: 30)
* @param mediumPercent Percent value for Medium mode (default: 60)
* @param highPercent Percent value for High/On mode (default: 100)
*
* Off is always 0 per spec: "The value 0 SHALL map to Off and be its own range".
*/
ExtractorHoodEndpoint(chip::EndpointId endpointId, chip::Percent lowPercent = 30, chip::Percent mediumPercent = 60,
chip::Percent highPercent = 100) :
mEndpointId(endpointId),
mFanModeOffPercent(0), mFanModeLowPercent(lowPercent), mFanModeMediumPercent(mediumPercent),
mFanModeHighPercent(highPercent)
{}

/**
* @brief Initialize the ExtractorHood endpoint runtime state.
* Reads current PercentSetting and synchronizes PercentCurrent.
*
* @note Must be called after construction and after the Matter stack is initialized.
* @return CHIP_NO_ERROR on success, error code otherwise
*/
CHIP_ERROR Init();

chip::app::DataModel::Nullable<chip::Percent> GetPercentSetting() const;

/**
* @brief Get the FanMode attribute.
**/
chip::Protocols::InteractionModel::Status GetFanMode(chip::app::Clusters::FanControl::FanModeEnum & fanMode) const;

/** @brief Set the PercentCurrent attribute if it differs from the current value.
*
**/
chip::Protocols::InteractionModel::Status SetPercentCurrent(chip::Percent newPercentSetting);

/**
* @brief Handle percent setting change and update percent current accordingly
* This is called when the PercentSetting attribute changes and updates PercentCurrent
* if the fan mode is not Auto and the value is different
*
* @param newPercentSetting The new percent setting value
* @return Status Success on success, error code otherwise
*/
chip::Protocols::InteractionModel::Status HandlePercentSettingChange(chip::Percent newPercentSetting);

/**
* @brief Handle fan mode change and update percent current accordingly
* This maps fan modes to their corresponding percent values and updates the PercentCurrent attribute
*
* @param newFanMode The new fan mode to apply
* @return Status Success on success, error code otherwise
*/
chip::Protocols::InteractionModel::Status HandleFanModeChange(chip::app::Clusters::FanControl::FanModeEnum newFanMode);

/**
* @brief Update the FanMode attribute
*/
chip::Protocols::InteractionModel::Status UpdateFanModeAttribute(chip::app::Clusters::FanControl::FanModeEnum newFanMode);

/**
* @brief Toggle fan mode between Off and High
* This is typically used for button press toggles
* @return Status Success on success, error code otherwise
*/
chip::Protocols::InteractionModel::Status ToggleFanMode();

private:
chip::EndpointId mEndpointId = chip::kInvalidEndpointId;

// Fan Mode Percent Mappings (set during initialization)
chip::Percent mFanModeOffPercent;
chip::Percent mFanModeLowPercent;
chip::Percent mFanModeMediumPercent;
chip::Percent mFanModeHighPercent;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* 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/on-off-server.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>

class LightEndpoint
{
public:
LightEndpoint(chip::EndpointId endpointId) : mEndpointId(endpointId) {}

/**
* @brief Get the current On/Off state from the Matter attribute.
* @param[out] state true if light is on, false if off on success.
* @return Interaction Model status code.
*/
CHIP_ERROR GetOnOffState(bool & state);

/**
* @brief Set On/Off state for the Light.
* @param[in] state Desired state (true => On, false => Off).
* @return Interaction Model status code.
*/
CHIP_ERROR SetOnOffState(bool state);

private:
chip::EndpointId mEndpointId = chip::kInvalidEndpointId;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* 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.
*/

#include "ExtractorHoodEndpoint.h"

#include <app-common/zap-generated/attributes/Accessors.h>
#include <app/clusters/fan-control-server/fan-control-server.h>
#include <lib/core/CHIPError.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceLayer.h>

#include <algorithm>

using namespace chip;
using namespace chip::app;
using namespace chip::DeviceLayer;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::FanControl;
using Status = chip::Protocols::InteractionModel::Status;

CHIP_ERROR ExtractorHoodEndpoint::Init()
{
// Initialize percent current from percent setting
// This ensures the fan speed reflects the current setting on startup
DeviceLayer::PlatformMgr().LockChipStack();
DataModel::Nullable<chip::Percent> percentSettingNullable = GetPercentSetting();
DeviceLayer::PlatformMgr().UnlockChipStack();

Percent initialPercentSetting = percentSettingNullable.IsNull() ? 0 : percentSettingNullable.Value();
DeviceLayer::PlatformMgr().LockChipStack();
Status status = HandlePercentSettingChange(initialPercentSetting);
DeviceLayer::PlatformMgr().UnlockChipStack();
if (status != Status::Success)
{
ChipLogError(NotSpecified, "ExtractorHoodEndpoint::Init: Failed to initialize PercentCurrent");
return CHIP_ERROR_INTERNAL;
}

return CHIP_NO_ERROR;
}

/**
* @brief Get the PercentSetting attribute.
* The caller MUST hold the CHIP stack lock before calling this function,
* unless calling from a CHIP task context where the lock is already held.
*/
DataModel::Nullable<Percent> ExtractorHoodEndpoint::GetPercentSetting() const
{
DataModel::Nullable<Percent> percentSetting;

Status status = Clusters::FanControl::Attributes::PercentSetting::Get(mEndpointId, percentSetting);

VerifyOrReturnValue(status == Status::Success, DataModel::Nullable<Percent>(),
ChipLogError(NotSpecified,
"ExtractorHoodEndpoint::GetPercentSetting: failed to get PercentSetting attribute: %d",
to_underlying(status)));
return percentSetting;
}

/* The caller MUST hold the CHIP stack lock before calling this function,
* unless calling from a CHIP task context where the lock is already held.
*/
Status ExtractorHoodEndpoint::GetFanMode(FanControl::FanModeEnum & fanMode) const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a Datamodel Command? if so why is this here? If not then this should be a ChipError not a IM::Status

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check the current FanMode while button event based on which FanMode will be updated. So added GetFanMode function to read directly from server.

{
Status status = FanControl::Attributes::FanMode::Get(mEndpointId, &fanMode);

VerifyOrReturnValue(status == Status::Success, status,
ChipLogError(NotSpecified, "ExtractorHoodEndpoint::GetFanMode: failed to get FanMode attribute: %d",
to_underlying(status)));

return Status::Success;
}

/**
* The caller MUST hold the CHIP stack lock before calling this function,
* unless calling from a CHIP task context where the lock is already held.
*/
Status ExtractorHoodEndpoint::SetPercentCurrent(Percent newPercentSetting)
{
Percent currentPercentCurrent = 0;

Status getStatus = FanControl::Attributes::PercentCurrent::Get(mEndpointId, &currentPercentCurrent);

// Return error if we can't read current value
VerifyOrReturnValue(getStatus == Status::Success, getStatus,
ChipLogError(NotSpecified,
"ExtractorHoodEndpoint::HandlePercentSettingChange: failed to get currentPercentCurrent: %d",
to_underlying(getStatus)));

// No update needed if value is unchanged
VerifyOrReturnValue(newPercentSetting != currentPercentCurrent, Status::Success);

Status setStatus = FanControl::Attributes::PercentCurrent::Set(mEndpointId, newPercentSetting);

VerifyOrReturnValue(setStatus == Status::Success, Status::Failure,
ChipLogError(NotSpecified,
"ExtractorHoodEndpoint::SetPercentCurrent: failed to update PercentCurrent attribute: %d",
to_underlying(setStatus)));
return Status::Success;
}

/**
* @brief Handle a change to the PercentSetting attribute, updating PercentCurrent as needed.
* The caller MUST hold the CHIP stack lock before calling this function,
* unless calling from a CHIP task context where the lock is already held.
*/
Status ExtractorHoodEndpoint::HandlePercentSettingChange(Percent newPercentSetting)
{
ChipLogDetail(NotSpecified, "ExtractorHoodEndpoint::HandlePercentSettingChange: %d", newPercentSetting);
// Get current PercentCurrent to check if it's different
Percent currentPercentCurrent = 0;

Status getStatus = FanControl::Attributes::PercentCurrent::Get(mEndpointId, &currentPercentCurrent);

// Return error if we can't read current value
VerifyOrReturnValue(getStatus == Status::Success, getStatus,
ChipLogError(NotSpecified,
"ExtractorHoodEndpoint::HandlePercentSettingChange: failed to get PercentCurrent: %d",
to_underlying(getStatus)));
// No update needed if value is unchanged
VerifyOrReturnValue(newPercentSetting != currentPercentCurrent, Status::Success);

// Get current fan mode to check if it's Auto
FanControl::FanModeEnum currentFanMode;

Status fanModeStatus = FanControl::Attributes::FanMode::Get(mEndpointId, &currentFanMode);

// Fail if we can't read fan mode
VerifyOrReturnValue(fanModeStatus == Status::Success, Status::Failure,
ChipLogError(NotSpecified, "ExtractorHoodEndpoint::HandlePercentSettingChange: failed to get FanMode: %d",
to_underlying(fanModeStatus)));

// Don't update PercentCurrent if fan mode is Auto
VerifyOrReturnValue(currentFanMode != FanControl::FanModeEnum::kAuto, Status::Success);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Smart Mode: Automatic Control Broken

The HandlePercentSettingChange function only checks for kAuto mode before updating PercentCurrent, but HandleFanModeChange treats both kAuto and kSmart modes identically. This inconsistency means PercentCurrent will be updated when in kSmart mode even though it shouldn't be, since Smart mode should control the fan speed automatically like Auto mode.

Fix in Cursor Fix in Web


// Update PercentCurrent to match PercentSetting
Status setStatus = FanControl::Attributes::PercentCurrent::Set(mEndpointId, newPercentSetting);

VerifyOrReturnValue(
setStatus == Status::Success, Status::Failure,
ChipLogError(NotSpecified,
"ExtractorHoodEndpoint::HandlePercentSettingChange: failed to update PercentCurrent attribute: %d",
to_underlying(setStatus)));
return Status::Success;
}

Status ExtractorHoodEndpoint::HandleFanModeChange(chip::app::Clusters::FanControl::FanModeEnum newFanMode)
{
ChipLogDetail(NotSpecified, "ExtractorHoodEndpoint::HandleFanModeChange: %d", (uint8_t) newFanMode);

switch (newFanMode)
{
case FanControl::FanModeEnum::kOff: {
return SetPercentCurrent(mFanModeOffPercent);
}
case FanControl::FanModeEnum::kLow: {
return SetPercentCurrent(mFanModeLowPercent);
}
case FanControl::FanModeEnum::kMedium: {
return SetPercentCurrent(mFanModeMediumPercent);
}
case FanControl::FanModeEnum::kOn:
case FanControl::FanModeEnum::kHigh: {
return SetPercentCurrent(mFanModeHighPercent);
}
case FanControl::FanModeEnum::kSmart:
case FanControl::FanModeEnum::kAuto: {
// For Auto/Smart modes, update the FanMode attribute to reflect the current mode
return UpdateFanModeAttribute(newFanMode);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Improve Fan Mode Attribute Update Efficiency

For Auto and Smart fan modes, UpdateFanModeAttribute is called to set the FanMode attribute to the value it was just changed to (since this code runs in the attribute change callback). This redundantly writes the same value back to the attribute, which is wasteful and illogical. The air-purifier-app implementation simply breaks without re-setting the attribute for these modes.

Fix in Cursor Fix in Web

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Fan Mode Attribute: Redundant Write Issue

For kAuto and kSmart fan modes, HandleFanModeChange calls UpdateFanModeAttribute to set the attribute to the value it was just changed to. Since HandleFanModeChange is invoked from MatterPostAttributeChangeCallback after the attribute has already been updated, this redundantly writes the same value back to the attribute, which is unnecessary and could potentially trigger another callback depending on the Matter stack implementation.

Fix in Cursor Fix in Web

case FanControl::FanModeEnum::kUnknownEnumValue: {
ChipLogProgress(NotSpecified, "ExtractorHoodEndpoint::HandleFanModeChange: Unknown");
return Status::Success; // Don't treat unknown as error
}
default:
return Status::Success;
}
}

/**
* @brief Update the FanMode attribute.
* The caller MUST hold the CHIP stack lock before calling this function,
* unless calling from a CHIP task context where the lock is already held.
*/
Status ExtractorHoodEndpoint::UpdateFanModeAttribute(FanControl::FanModeEnum newFanMode)
{
Status setStatus = FanControl::Attributes::FanMode::Set(mEndpointId, newFanMode);

VerifyOrReturnValue(setStatus == Status::Success, Status::Failure,
ChipLogError(NotSpecified,
"ExtractorHoodEndpoint::UpdateFanModeAttribute: failed to update FanMode attribute: %d",
to_underlying(setStatus)));
return Status::Success;
}

/**
* @brief Toggle fan mode between Off and High.
* This is used for button press toggles.
*/
Status ExtractorHoodEndpoint::ToggleFanMode()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cluster implementation does not have any setters or getter functions to read/write the attributes accordingly, so everything implemented in application. For example, each fan mode change, the percentcurrent will be set to values form 0 to 100.

{
FanControl::FanModeEnum currentFanMode = FanControl::FanModeEnum::kUnknownEnumValue;
Status getStatus = GetFanMode(currentFanMode);

if (getStatus != Status::Success || currentFanMode == FanControl::FanModeEnum::kUnknownEnumValue)
{
ChipLogError(NotSpecified, "ExtractorHoodEndpoint::ToggleFanMode: failed to get current fan mode");
return Status::Failure;
}

FanControl::FanModeEnum target =
(currentFanMode == FanControl::FanModeEnum::kOff) ? FanControl::FanModeEnum::kHigh : FanControl::FanModeEnum::kOff;

return UpdateFanModeAttribute(target);
}
Loading
Loading