Skip to content

Matter Thermostat: Use fanMode capability #2205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -6,6 +6,9 @@ components:
version: 1
- id: thermostatMode
version: 1
- id: fanMode
Copy link
Contributor

Choose a reason for hiding this comment

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

Just calling out that removing thermostatFanMode is a destructive change that could result in automation/routines that users may have set up using thermostatFanMode to no longer work.

One workaround for this is to just keep thermostatFanMode here and then add fanMode, and make it so that future devices use fanMode when onboarding to this profile, but existing ones can still use thermostatFanMode. I could be convinced that it would be better to just remove thermostatFanMode though, let's discuss offline.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, I re-added this capability in the latest commit.

version: 1
optional: true
- id: thermostatFanMode
version: 1
optional: true
Expand Down
291 changes: 104 additions & 187 deletions drivers/SmartThings/matter-thermostat/src/init.lua

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ local mock_device_rock = test.mock_device.build_test_matter_device({
}
},
{
endpoint_id = 1,
clusters = {
{cluster_id = clusters.FanControl.ID, cluster_type = "SERVER"},
{cluster_id = clusters.HepaFilterMonitoring.ID, cluster_type = "SERVER"},
{cluster_id = clusters.ActivatedCarbonFilterMonitoring.ID, cluster_type = "SERVER"},
}
endpoint_id = 1,
clusters = {
{cluster_id = clusters.FanControl.ID, cluster_type = "SERVER"},
{cluster_id = clusters.HepaFilterMonitoring.ID, cluster_type = "SERVER"},
{cluster_id = clusters.ActivatedCarbonFilterMonitoring.ID, cluster_type = "SERVER"},
}
}
}
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- Copyright 2024 SmartThings
-- Copyright 2025 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
Expand All @@ -12,28 +12,29 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
local test = require "integration_test"
test.set_rpc_version(8)
local capabilities = require "st.capabilities"
local t_utils = require "integration_test.utils"
local utils = require "st.utils"
local dkjson = require "dkjson"

local clusters = require "st.matter.clusters"
local version = require "version"

clusters.HepaFilterMonitoring = require "HepaFilterMonitoring"
clusters.ActivatedCarbonFilterMonitoring = require "ActivatedCarbonFilterMonitoring"
clusters.AirQuality = require "AirQuality"
clusters.CarbonMonoxideConcentrationMeasurement = require "CarbonMonoxideConcentrationMeasurement"
clusters.CarbonDioxideConcentrationMeasurement = require "CarbonDioxideConcentrationMeasurement"
clusters.FormaldehydeConcentrationMeasurement = require "FormaldehydeConcentrationMeasurement"
clusters.NitrogenDioxideConcentrationMeasurement = require "NitrogenDioxideConcentrationMeasurement"
clusters.OzoneConcentrationMeasurement = require "OzoneConcentrationMeasurement"
clusters.Pm1ConcentrationMeasurement = require "Pm1ConcentrationMeasurement"
clusters.Pm10ConcentrationMeasurement = require "Pm10ConcentrationMeasurement"
clusters.Pm25ConcentrationMeasurement = require "Pm25ConcentrationMeasurement"
clusters.RadonConcentrationMeasurement = require "RadonConcentrationMeasurement"
clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "TotalVolatileOrganicCompoundsConcentrationMeasurement"

test.set_rpc_version(8)
if version.api < 10 then
clusters.HepaFilterMonitoring = require "HepaFilterMonitoring"
clusters.ActivatedCarbonFilterMonitoring = require "ActivatedCarbonFilterMonitoring"
clusters.AirQuality = require "AirQuality"
clusters.CarbonMonoxideConcentrationMeasurement = require "CarbonMonoxideConcentrationMeasurement"
clusters.CarbonDioxideConcentrationMeasurement = require "CarbonDioxideConcentrationMeasurement"
clusters.FormaldehydeConcentrationMeasurement = require "FormaldehydeConcentrationMeasurement"
clusters.NitrogenDioxideConcentrationMeasurement = require "NitrogenDioxideConcentrationMeasurement"
clusters.OzoneConcentrationMeasurement = require "OzoneConcentrationMeasurement"
clusters.Pm1ConcentrationMeasurement = require "Pm1ConcentrationMeasurement"
clusters.Pm10ConcentrationMeasurement = require "Pm10ConcentrationMeasurement"
clusters.Pm25ConcentrationMeasurement = require "Pm25ConcentrationMeasurement"
clusters.RadonConcentrationMeasurement = require "RadonConcentrationMeasurement"
clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement = require "TotalVolatileOrganicCompoundsConcentrationMeasurement"
end

local mock_device_basic = test.mock_device.build_test_matter_device({
profile = t_utils.get_profile_definition("air-purifier-hepa-ac-wind.yml"),
Expand Down Expand Up @@ -162,7 +163,7 @@ local cluster_subscribe_list_configured = {
clusters.Thermostat.attributes.SystemMode,
clusters.Thermostat.attributes.ControlSequenceOfOperation
},
[capabilities.thermostatFanMode.ID] = {
[capabilities.fanMode.ID] = {
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.FanMode
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- Copyright 2024 SmartThings
-- Copyright 2025 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -74,6 +74,7 @@ local mock_device_generic = test.mock_device.build_test_matter_device({

local cluster_subscribe_list = {
clusters.FanControl.attributes.FanMode,
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.PercentCurrent,
clusters.FanControl.attributes.WindSupport,
clusters.FanControl.attributes.WindSetting,
Expand All @@ -83,6 +84,7 @@ local cluster_subscribe_list = {

local cluster_subscribe_list_generic = {
clusters.FanControl.attributes.FanMode,
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.PercentCurrent,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ local function test_init()
clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit
},
[capabilities.airConditionerFanMode.ID] = {
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.FanMode
},
[capabilities.fanSpeedPercent.ID] = {
Expand Down Expand Up @@ -199,6 +200,7 @@ local function test_init_configure()
clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit
},
[capabilities.airConditionerFanMode.ID] = {
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.FanMode
},
[capabilities.fanSpeedPercent.ID] = {
Expand Down Expand Up @@ -253,6 +255,7 @@ local function test_init_nostate()
clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit
},
[capabilities.airConditionerFanMode.ID] = {
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.FanMode
},
[capabilities.fanSpeedPercent.ID] = {
Expand Down Expand Up @@ -401,4 +404,82 @@ test.register_message_test(
}
)

test.register_message_test(
"Test fan mode handler",
{
{
channel = "matter",
direction = "receive",
message = {
mock_device.id,
clusters.FanControl.attributes.FanMode:build_test_report_data(mock_device, 1, clusters.FanControl.attributes.FanMode.OFF)
}
},
{
channel = "capability",
direction = "send",
message = mock_device:generate_test_message("main", capabilities.airConditionerFanMode.fanMode("off"))
},
{
channel = "matter",
direction = "receive",
message = {
mock_device.id,
clusters.FanControl.attributes.FanMode:build_test_report_data(mock_device, 1, clusters.FanControl.attributes.FanMode.LOW)
}
},
{
channel = "capability",
direction = "send",
message = mock_device:generate_test_message("main", capabilities.airConditionerFanMode.fanMode("low"))
},
{
channel = "matter",
direction = "receive",
message = {
mock_device.id,
clusters.FanControl.attributes.FanMode:build_test_report_data(mock_device, 1, clusters.FanControl.attributes.FanMode.HIGH)
}
},
{
channel = "capability",
direction = "send",
message = mock_device:generate_test_message("main", capabilities.airConditionerFanMode.fanMode("high"))
}
}
)

local FanModeSequence = clusters.FanControl.attributes.FanModeSequence
test.register_message_test(
"Room AC fan mode sequence reports should generate the appropriate supported modes",
{
{
channel = "matter",
direction = "receive",
message = {
mock_device.id,
FanModeSequence:build_test_report_data(mock_device, 1, FanModeSequence.OFF_ON)
}
},
{
channel = "capability",
direction = "send",
message = mock_device:generate_test_message("main", capabilities.airConditionerFanMode.supportedAcFanModes({"off", "high"}, {visibility={displayed=false}}))
},
{
channel = "matter",
direction = "receive",
message = {
mock_device.id,
FanModeSequence:build_test_report_data(mock_device, 1, FanModeSequence.OFF_LOW_MED_HIGH_AUTO)
}
},
{
channel = "capability",
direction = "send",
message = mock_device:generate_test_message("main", capabilities.airConditionerFanMode.supportedAcFanModes({"off", "low", "medium", "high", "auto"}, {visibility={displayed=false}}))
}
}
)

test.run_registered_tests()
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ local function test_init_basic()
clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit
},
[capabilities.airConditionerFanMode.ID] = {
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.FanMode
},
[capabilities.fanSpeedPercent.ID] = {
Expand Down Expand Up @@ -180,6 +181,7 @@ local subscribed_attributes_no_state = {
clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit
},
[capabilities.airConditionerFanMode.ID] = {
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.FanMode
},
[capabilities.fanSpeedPercent.ID] = {
Expand Down Expand Up @@ -235,6 +237,7 @@ local function test_init_no_state()
clusters.Thermostat.attributes.AbsMaxHeatSetpointLimit
},
[capabilities.airConditionerFanMode.ID] = {
clusters.FanControl.attributes.FanModeSequence,
clusters.FanControl.attributes.FanMode
},
[capabilities.fanSpeedPercent.ID] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- Copyright 2023 SmartThings
-- Copyright 2025 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -110,7 +110,7 @@ local expected_metadata = {
"main",
{
"relativeHumidityMeasurement",
"thermostatFanMode",
"fanMode",
"thermostatHeatingSetpoint",
"thermostatCoolingSetpoint"
},
Expand Down
Loading