Skip to content

Commit e6561f4

Browse files
Use fanSpeedPercent for thermostats
This change adds the fanSpeedPercent capability to the thermostat-modular profile. Additionally, PercentSetting is subscribed in addition to PercentCurrent to set this capability, since it provides an accurate representation of the speed of the fan while helping avoid the following situation: (1) The fan speed is changed in the app and PercentSetting is routed to the device (2) The fan reports back a value of PercentCurrent that doesn't match PercentSetting because the speed takes a little while to change (3) The fanSpeedPercent capability jumps to the value reported by PercentCurrent PercentCurrent is still subscribed to, but its attribute handler is gated on the current fan mode being AUTO, in which case PercentSetting is NULL on the device side and PercentCurrent is used as a fallback for setting the capability.
1 parent a3b5249 commit e6561f4

9 files changed

+99
-109
lines changed

drivers/SmartThings/matter-thermostat/src/init.lua

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ local subscribed_attributes = {
196196
clusters.FanControl.attributes.FanMode
197197
},
198198
[capabilities.fanSpeedPercent.ID] = {
199-
clusters.FanControl.attributes.PercentCurrent
199+
clusters.FanControl.attributes.PercentCurrent,
200+
clusters.FanControl.attributes.PercentSetting
200201
},
201202
[capabilities.windMode.ID] = {
202203
clusters.FanControl.attributes.WindSupport,
@@ -1575,10 +1576,21 @@ local function fan_mode_sequence_handler(driver, device, ib, response)
15751576
end
15761577

15771578
local function fan_speed_percent_attr_handler(driver, device, ib, response)
1578-
local speed = 0
1579-
if ib.data.value ~= nil then
1580-
speed = utils.clamp_value(ib.data.value, MIN_ALLOWED_PERCENT_VALUE, MAX_ALLOWED_PERCENT_VALUE)
1579+
if ib.data.value == nil then return end
1580+
local thermostat_mode = device:get_latest_state(
1581+
device:endpoint_to_component(ib.endpoint_id),
1582+
capabilities.thermostatMode.ID,
1583+
capabilities.thermostatMode.thermostatMode.NAME
1584+
)
1585+
if thermostat_mode == capabilities.thermostatMode.thermostatMode.auto() then
1586+
local speed = utils.clamp_value(ib.data.value, MIN_ALLOWED_PERCENT_VALUE, MAX_ALLOWED_PERCENT_VALUE)
1587+
device:emit_event_for_endpoint(ib.endpoint_id, capabilities.fanSpeedPercent.percent(speed))
15811588
end
1589+
end
1590+
1591+
local function fan_speed_setting_attr_handler(driver, device, ib, response)
1592+
if ib.data.value == nil then return end
1593+
local speed = utils.clamp_value(ib.data.value, MIN_ALLOWED_PERCENT_VALUE, MAX_ALLOWED_PERCENT_VALUE)
15821594
device:emit_event_for_endpoint(ib.endpoint_id, capabilities.fanSpeedPercent.percent(speed))
15831595
end
15841596

@@ -2075,6 +2087,7 @@ local matter_driver_template = {
20752087
[clusters.FanControl.attributes.FanModeSequence.ID] = fan_mode_sequence_handler,
20762088
[clusters.FanControl.attributes.FanMode.ID] = fan_mode_handler,
20772089
[clusters.FanControl.attributes.PercentCurrent.ID] = fan_speed_percent_attr_handler,
2090+
[clusters.FanControl.attributes.PercentSetting.ID] = fan_speed_setting_attr_handler,
20782091
[clusters.FanControl.attributes.WindSupport.ID] = wind_support_handler,
20792092
[clusters.FanControl.attributes.WindSetting.ID] = wind_setting_handler,
20802093
[clusters.FanControl.attributes.RockSupport.ID] = rock_support_handler,

drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier.lua

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ local cluster_subscribe_list = {
277277
clusters.FanControl.attributes.FanModeSequence,
278278
clusters.FanControl.attributes.FanMode,
279279
clusters.FanControl.attributes.PercentCurrent,
280+
clusters.FanControl.attributes.PercentSetting,
280281
clusters.FanControl.attributes.WindSupport,
281282
clusters.FanControl.attributes.WindSetting,
282283
clusters.HepaFilterMonitoring.attributes.ChangeIndication,
@@ -289,6 +290,7 @@ local cluster_subscribe_list_rock = {
289290
clusters.FanControl.attributes.FanModeSequence,
290291
clusters.FanControl.attributes.FanMode,
291292
clusters.FanControl.attributes.PercentCurrent,
293+
clusters.FanControl.attributes.PercentSetting,
292294
clusters.FanControl.attributes.WindSupport,
293295
clusters.FanControl.attributes.WindSetting,
294296
clusters.FanControl.attributes.RockSupport,
@@ -327,7 +329,8 @@ local cluster_subscribe_list_configured = {
327329
clusters.FanControl.attributes.FanMode
328330
},
329331
[capabilities.fanSpeedPercent.ID] = {
330-
clusters.FanControl.attributes.PercentCurrent
332+
clusters.FanControl.attributes.PercentCurrent,
333+
clusters.FanControl.attributes.PercentSetting
331334
},
332335
[capabilities.windMode.ID] = {
333336
clusters.FanControl.attributes.WindSupport,
@@ -581,7 +584,7 @@ test.register_message_test(
581584
direction = "receive",
582585
message = {
583586
mock_device.id,
584-
clusters.FanControl.attributes.PercentCurrent:build_test_report_data(mock_device, 1, 10)
587+
clusters.FanControl.attributes.PercentSetting:build_test_report_data(mock_device, 1, 10)
585588
}
586589
},
587590
{
@@ -764,26 +767,6 @@ test.register_message_test(
764767
}
765768
)
766769

767-
test.register_message_test(
768-
"Set percent command should clamp invalid percentage values",
769-
{
770-
{
771-
channel = "matter",
772-
direction = "receive",
773-
message = {
774-
mock_device.id,
775-
clusters.FanControl.attributes.PercentCurrent:build_test_report_data(mock_device, 1, 255)
776-
}
777-
},
778-
{
779-
channel = "capability",
780-
direction = "send",
781-
message = mock_device:generate_test_message("main", capabilities.fanSpeedPercent.percent(100))
782-
},
783-
}
784-
)
785-
786-
787770
local supportedFanRock = {
788771
capabilities.fanOscillationMode.fanOscillationMode.off.NAME,
789772
capabilities.fanOscillationMode.fanOscillationMode.horizontal.NAME,

drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_api9.lua

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ local cluster_subscribe_list = {
277277
clusters.FanControl.attributes.FanModeSequence,
278278
clusters.FanControl.attributes.FanMode,
279279
clusters.FanControl.attributes.PercentCurrent,
280+
clusters.FanControl.attributes.PercentSetting,
280281
clusters.FanControl.attributes.WindSupport,
281282
clusters.FanControl.attributes.WindSetting,
282283
clusters.HepaFilterMonitoring.attributes.ChangeIndication,
@@ -289,6 +290,7 @@ local cluster_subscribe_list_rock = {
289290
clusters.FanControl.attributes.FanModeSequence,
290291
clusters.FanControl.attributes.FanMode,
291292
clusters.FanControl.attributes.PercentCurrent,
293+
clusters.FanControl.attributes.PercentSetting,
292294
clusters.FanControl.attributes.WindSupport,
293295
clusters.FanControl.attributes.WindSetting,
294296
clusters.FanControl.attributes.RockSupport,
@@ -327,7 +329,8 @@ local cluster_subscribe_list_configured = {
327329
clusters.FanControl.attributes.FanMode
328330
},
329331
[capabilities.fanSpeedPercent.ID] = {
330-
clusters.FanControl.attributes.PercentCurrent
332+
clusters.FanControl.attributes.PercentCurrent,
333+
clusters.FanControl.attributes.PercentSetting
331334
},
332335
[capabilities.windMode.ID] = {
333336
clusters.FanControl.attributes.WindSupport,
@@ -581,7 +584,7 @@ test.register_message_test(
581584
direction = "receive",
582585
message = {
583586
mock_device.id,
584-
clusters.FanControl.attributes.PercentCurrent:build_test_report_data(mock_device, 1, 10)
587+
clusters.FanControl.attributes.PercentSetting:build_test_report_data(mock_device, 1, 10)
585588
}
586589
},
587590
{
@@ -765,26 +768,6 @@ test.register_message_test(
765768
}
766769
)
767770

768-
test.register_message_test(
769-
"Set percent command should clamp invalid percentage values",
770-
{
771-
{
772-
channel = "matter",
773-
direction = "receive",
774-
message = {
775-
mock_device.id,
776-
clusters.FanControl.attributes.PercentCurrent:build_test_report_data(mock_device, 1, 255)
777-
}
778-
},
779-
{
780-
channel = "capability",
781-
direction = "send",
782-
message = mock_device:generate_test_message("main", capabilities.fanSpeedPercent.percent(100))
783-
}
784-
}
785-
)
786-
787-
788771
local supportedFanRock = {
789772
capabilities.fanOscillationMode.fanOscillationMode.off.NAME,
790773
capabilities.fanOscillationMode.fanOscillationMode.horizontal.NAME,

drivers/SmartThings/matter-thermostat/src/test/test_matter_air_purifier_modular.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ local cluster_subscribe_list = {
143143
clusters.FanControl.attributes.FanModeSequence,
144144
clusters.FanControl.attributes.FanMode,
145145
clusters.FanControl.attributes.PercentCurrent,
146+
clusters.FanControl.attributes.PercentSetting,
146147
clusters.FanControl.attributes.WindSupport,
147148
clusters.FanControl.attributes.WindSetting,
148149
clusters.HepaFilterMonitoring.attributes.ChangeIndication,
@@ -179,7 +180,8 @@ local cluster_subscribe_list_configured = {
179180
clusters.FanControl.attributes.FanMode
180181
},
181182
[capabilities.fanSpeedPercent.ID] = {
182-
clusters.FanControl.attributes.PercentCurrent
183+
clusters.FanControl.attributes.PercentCurrent,
184+
clusters.FanControl.attributes.PercentSetting
183185
},
184186
[capabilities.windMode.ID] = {
185187
clusters.FanControl.attributes.WindSupport,

drivers/SmartThings/matter-thermostat/src/test/test_matter_fan.lua

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,79 +13,80 @@
1313
-- limitations under the License.
1414
local test = require "integration_test"
1515
local t_utils = require "integration_test.utils"
16-
1716
local clusters = require "st.matter.clusters"
1817

1918
local mock_device = test.mock_device.build_test_matter_device({
20-
profile = t_utils.get_profile_definition("fan-rock-wind.yml"),
21-
manufacturer_info = {
22-
vendor_id = 0x0000,
23-
product_id = 0x0000,
19+
profile = t_utils.get_profile_definition("fan-rock-wind.yml"),
20+
manufacturer_info = {
21+
vendor_id = 0x0000,
22+
product_id = 0x0000,
23+
},
24+
endpoints = {
25+
{
26+
endpoint_id = 0,
27+
clusters = {
28+
{cluster_id = clusters.Basic.ID, cluster_type = "SERVER"},
29+
},
30+
device_types = {
31+
{device_type_id = 0x0016, device_type_revision = 1,} -- RootNode
32+
}
2433
},
25-
endpoints = {
26-
{
27-
endpoint_id = 0,
28-
clusters = {
29-
{cluster_id = clusters.Basic.ID, cluster_type = "SERVER"},
30-
},
31-
device_types = {
32-
{device_type_id = 0x0016, device_type_revision = 1,} -- RootNode
33-
}
34+
{
35+
endpoint_id = 1,
36+
clusters = {
37+
{cluster_id = clusters.FanControl.ID, cluster_type = "SERVER", feature_map = 15},
3438
},
35-
{
36-
endpoint_id = 1,
37-
clusters = {
38-
{cluster_id = clusters.FanControl.ID, cluster_type = "SERVER", feature_map = 15},
39-
},
40-
device_types = {
41-
{device_type_id = 0x002B, device_type_revision = 1,} -- Fan
42-
}
39+
device_types = {
40+
{device_type_id = 0x002B, device_type_revision = 1,} -- Fan
4341
}
4442
}
43+
}
4544
})
4645

4746
local mock_device_generic = test.mock_device.build_test_matter_device({
48-
profile = t_utils.get_profile_definition("fan-generic.yml"),
49-
manufacturer_info = {
50-
vendor_id = 0x0000,
51-
product_id = 0x0000,
47+
profile = t_utils.get_profile_definition("fan-generic.yml"),
48+
manufacturer_info = {
49+
vendor_id = 0x0000,
50+
product_id = 0x0000,
51+
},
52+
endpoints = {
53+
{
54+
endpoint_id = 0,
55+
clusters = {
56+
{cluster_id = clusters.Basic.ID, cluster_type = "SERVER"},
57+
},
58+
device_types = {
59+
{device_type_id = 0x0016, device_type_revision = 1,} -- RootNode
60+
}
5261
},
53-
endpoints = {
54-
{
55-
endpoint_id = 0,
56-
clusters = {
57-
{cluster_id = clusters.Basic.ID, cluster_type = "SERVER"},
58-
},
59-
device_types = {
60-
{device_type_id = 0x0016, device_type_revision = 1,} -- RootNode
61-
}
62+
{
63+
endpoint_id = 1,
64+
clusters = {
65+
{cluster_id = clusters.FanControl.ID, cluster_type = "SERVER", feature_map = 0},
6266
},
63-
{
64-
endpoint_id = 1,
65-
clusters = {
66-
{cluster_id = clusters.FanControl.ID, cluster_type = "SERVER", feature_map = 0},
67-
},
68-
device_types = {
69-
{device_type_id = 0x002B, device_type_revision = 1,} -- Fan
70-
}
67+
device_types = {
68+
{device_type_id = 0x002B, device_type_revision = 1,} -- Fan
7169
}
7270
}
71+
}
7372
})
7473

7574
local cluster_subscribe_list = {
76-
clusters.FanControl.attributes.FanMode,
77-
clusters.FanControl.attributes.FanModeSequence,
78-
clusters.FanControl.attributes.PercentCurrent,
79-
clusters.FanControl.attributes.WindSupport,
80-
clusters.FanControl.attributes.WindSetting,
81-
clusters.FanControl.attributes.RockSupport,
82-
clusters.FanControl.attributes.RockSetting,
75+
clusters.FanControl.attributes.FanMode,
76+
clusters.FanControl.attributes.FanModeSequence,
77+
clusters.FanControl.attributes.PercentCurrent,
78+
clusters.FanControl.attributes.PercentSetting,
79+
clusters.FanControl.attributes.WindSupport,
80+
clusters.FanControl.attributes.WindSetting,
81+
clusters.FanControl.attributes.RockSupport,
82+
clusters.FanControl.attributes.RockSetting,
8383
}
8484

8585
local cluster_subscribe_list_generic = {
86-
clusters.FanControl.attributes.FanMode,
87-
clusters.FanControl.attributes.FanModeSequence,
88-
clusters.FanControl.attributes.PercentCurrent,
86+
clusters.FanControl.attributes.FanMode,
87+
clusters.FanControl.attributes.FanModeSequence,
88+
clusters.FanControl.attributes.PercentCurrent,
89+
clusters.FanControl.attributes.PercentSetting,
8990
}
9091

9192
local function test_init()

drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac.lua

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ local function test_init()
146146
clusters.FanControl.attributes.FanMode
147147
},
148148
[capabilities.fanSpeedPercent.ID] = {
149-
clusters.FanControl.attributes.PercentCurrent
149+
clusters.FanControl.attributes.PercentCurrent,
150+
clusters.FanControl.attributes.PercentSetting
150151
},
151152
[capabilities.windMode.ID] = {
152153
clusters.FanControl.attributes.WindSupport,
@@ -204,7 +205,8 @@ local function test_init_configure()
204205
clusters.FanControl.attributes.FanMode
205206
},
206207
[capabilities.fanSpeedPercent.ID] = {
207-
clusters.FanControl.attributes.PercentCurrent
208+
clusters.FanControl.attributes.PercentCurrent,
209+
clusters.FanControl.attributes.PercentSetting
208210
},
209211
[capabilities.windMode.ID] = {
210212
clusters.FanControl.attributes.WindSupport,
@@ -259,7 +261,8 @@ local function test_init_nostate()
259261
clusters.FanControl.attributes.FanMode
260262
},
261263
[capabilities.fanSpeedPercent.ID] = {
262-
clusters.FanControl.attributes.PercentCurrent
264+
clusters.FanControl.attributes.PercentCurrent,
265+
clusters.FanControl.attributes.PercentSetting
263266
},
264267
}
265268
local subscribe_request = nil
@@ -324,7 +327,7 @@ test.register_message_test(
324327
direction = "receive",
325328
message = {
326329
mock_device.id,
327-
clusters.FanControl.attributes.PercentCurrent:build_test_report_data(mock_device, 1, 10)
330+
clusters.FanControl.attributes.PercentSetting:build_test_report_data(mock_device, 1, 10)
328331
}
329332
},
330333
{

drivers/SmartThings/matter-thermostat/src/test/test_matter_room_ac_modular.lua

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ local function test_init_basic()
154154
clusters.FanControl.attributes.FanMode
155155
},
156156
[capabilities.fanSpeedPercent.ID] = {
157-
clusters.FanControl.attributes.PercentCurrent
157+
clusters.FanControl.attributes.PercentCurrent,
158+
clusters.FanControl.attributes.PercentSetting
158159
},
159160
[capabilities.windMode.ID] = {
160161
clusters.FanControl.attributes.WindSupport,
@@ -203,7 +204,8 @@ local subscribed_attributes_no_state = {
203204
clusters.FanControl.attributes.FanMode
204205
},
205206
[capabilities.fanSpeedPercent.ID] = {
206-
clusters.FanControl.attributes.PercentCurrent
207+
clusters.FanControl.attributes.PercentCurrent,
208+
clusters.FanControl.attributes.PercentSetting
207209
},
208210
[capabilities.windMode.ID] = {
209211
clusters.FanControl.attributes.WindSupport,
@@ -259,7 +261,8 @@ local function test_init_no_state()
259261
clusters.FanControl.attributes.FanMode
260262
},
261263
[capabilities.fanSpeedPercent.ID] = {
262-
clusters.FanControl.attributes.PercentCurrent
264+
clusters.FanControl.attributes.PercentCurrent,
265+
clusters.FanControl.attributes.PercentSetting
263266
},
264267
[capabilities.windMode.ID] = {
265268
clusters.FanControl.attributes.WindSupport,

drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ local new_cluster_subscribe_list = {
233233
clusters.FanControl.attributes.FanMode,
234234
clusters.FanControl.attributes.FanModeSequence,
235235
clusters.FanControl.attributes.PercentCurrent,
236+
clusters.FanControl.attributes.PercentSetting,
236237
clusters.FanControl.attributes.RockSupport, -- These two attributes will be subscribed to following the profile
237238
clusters.FanControl.attributes.RockSetting, -- change since the fanOscillationMode capability will be enabled.
238239
}

0 commit comments

Comments
 (0)