Skip to content

Commit dc262d8

Browse files
authored
Merge pull request #2810 from SmartThingsCommunity/hotfix/2-25-26
Matter Driver Hotfix (main -> beta)
2 parents 942c878 + 35a98ca commit dc262d8

10 files changed

Lines changed: 45 additions & 149 deletions

File tree

drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ local MIN_EPOCH_S = 0
2222
local MAX_EPOCH_S = 0xffffffff
2323
local THIRTY_YEARS_S = 946684800 -- 1970-01-01T00:00:00 ~ 2000-01-01T00:00:00
2424

25+
local MODULAR_PROFILE_UPDATED = "__MODULAR_PROFILE_UPDATED"
26+
2527
local RESPONSE_STATUS_MAP = {
2628
[DoorLock.types.DlStatus.SUCCESS] = "success",
2729
[DoorLock.types.DlStatus.FAILURE] = "failure",
@@ -201,6 +203,7 @@ local function match_profile_modular(driver, device)
201203

202204
table.insert(enabled_optional_component_capability_pairs, {"main", main_component_capabilities})
203205
device:try_update_metadata({profile = modular_profile_name, optional_component_capabilities = enabled_optional_component_capability_pairs})
206+
device:set_field(MODULAR_PROFILE_UPDATED, true)
204207
end
205208

206209
local function match_profile_switch(driver, device)
@@ -238,37 +241,11 @@ local function match_profile_switch(driver, device)
238241
device:try_update_metadata({profile = profile_name})
239242
end
240243

241-
local function profile_changed(latest_profile, previous_profile)
242-
if latest_profile.id ~= previous_profile.id then
243-
return true
244-
end
245-
for component_id, synced_component in pairs(latest_profile.components or {}) do
246-
local prev_component = previous_profile.components[component_id]
247-
if prev_component == nil then
248-
return true
249-
end
250-
if #synced_component.capabilities ~= #prev_component.capabilities then
251-
return true
252-
end
253-
-- Build a table of capability IDs from the previous component. Then, use this map to check
254-
-- that all capabilities in the synced component existed in the previous component.
255-
local prev_cap_ids = {}
256-
for _, capability in ipairs(prev_component.capabilities or {}) do
257-
prev_cap_ids[capability.id] = true
258-
end
259-
for _, capability in ipairs(synced_component.capabilities or {}) do
260-
if not prev_cap_ids[capability.id] then
261-
return true
262-
end
263-
end
264-
end
265-
return false
266-
end
267-
268244
local function info_changed(driver, device, event, args)
269-
if not profile_changed(device.profile, args.old_st_store.profile) then
245+
if device.profile.id == args.old_st_store.profile.id and not device:get_field(MODULAR_PROFILE_UPDATED) then
270246
return
271247
end
248+
device:set_field(MODULAR_PROFILE_UPDATED, nil)
272249
for cap_id, attributes in pairs(subscribed_attributes) do
273250
if device:supports_capability_by_id(cap_id) then
274251
for _, attr in ipairs(attributes) do

drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,32 +77,22 @@ function AirQualitySensorUtils.set_supported_health_concern_values(device)
7777
end
7878
end
7979

80-
function AirQualitySensorUtils.profile_changed(latest_profile, previous_profile)
81-
if latest_profile.id ~= previous_profile.id then
80+
function AirQualitySensorUtils.profile_changed(synced_components, prev_components)
81+
if #synced_components ~= #prev_components then
8282
return true
8383
end
84-
for component_id, synced_component in pairs(latest_profile.components or {}) do
85-
local prev_component = previous_profile.components[component_id]
86-
if prev_component == nil then
84+
for _, component in pairs(synced_components or {}) do
85+
if (prev_components[component.id] == nil) or
86+
(#component.capabilities ~= #prev_components[component.id].capabilities) then
8787
return true
8888
end
89-
if #synced_component.capabilities ~= #prev_component.capabilities then
90-
return true
91-
end
92-
-- Build a table of capability IDs from the previous component. Then, use this map to check
93-
-- that all capabilities in the synced component existed in the previous component.
94-
local prev_cap_ids = {}
95-
for _, capability in ipairs(prev_component.capabilities or {}) do
96-
prev_cap_ids[capability.id] = true
97-
end
98-
for _, capability in ipairs(synced_component.capabilities or {}) do
99-
if not prev_cap_ids[capability.id] then
89+
for _, capability in pairs(component.capabilities or {}) do
90+
if prev_components[component.id][capability.id] == nil then
10091
return true
10192
end
10293
end
10394
end
10495
return false
10596
end
10697

107-
10898
return AirQualitySensorUtils

drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/init.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ function AirQualitySensorLifecycleHandlers.device_init(driver, device)
6666
end
6767

6868
function AirQualitySensorLifecycleHandlers.info_changed(driver, device, event, args)
69-
if aqs_utils.profile_changed(device.profile, args.old_st_store.profile) then
69+
if device.profile.id ~= args.old_st_store.profile.id or
70+
aqs_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then
7071
if device:get_field(fields.SUPPORTED_COMPONENT_CAPABILITIES) then
7172
--re-up subscription with new capabilities using the modular supports_capability override
7273
device:extend_device("supports_capability_by_id", aqs_utils.supports_capability_by_id_modular)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ function SwitchLifecycleHandlers.driver_switched(driver, device)
6464
end
6565

6666
function SwitchLifecycleHandlers.info_changed(driver, device, event, args)
67-
if switch_utils.profile_changed(device.profile, args.old_st_store.profile) then
67+
if device.profile.id ~= args.old_st_store.profile.id or device:get_field(fields.MODULAR_PROFILE_UPDATED) then
68+
device:set_field(fields.MODULAR_PROFILE_UPDATED, nil)
6869
if device.network_type == device_lib.NETWORK_TYPE_MATTER then
6970
device:subscribe()
7071
button_cfg.configure_buttons(device,

drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,24 @@ function CameraUtils.build_supported_resolutions(device, max_encoded_pixel_rate,
134134
return resolutions
135135
end
136136

137+
function CameraUtils.profile_changed(synced_components, prev_components)
138+
if #synced_components ~= #prev_components then
139+
return true
140+
end
141+
for _, component in pairs(synced_components or {}) do
142+
if (prev_components[component.id] == nil) or
143+
(#component.capabilities ~= #prev_components[component.id].capabilities) then
144+
return true
145+
end
146+
for _, capability in pairs(component.capabilities or {}) do
147+
if prev_components[component.id][capability.id] == nil then
148+
return true
149+
end
150+
end
151+
end
152+
return false
153+
end
154+
137155
function CameraUtils.optional_capabilities_list_changed(new_component_capability_list, previous_component_capability_list)
138156
local previous_capability_map = {}
139157
local component_sizes = {}

drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function CameraLifecycleHandlers.driver_switched(driver, device)
4747
end
4848

4949
function CameraLifecycleHandlers.info_changed(driver, device, event, args)
50-
if switch_utils.profile_changed(device.profile, args.old_st_store.profile) then
50+
if camera_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then
5151
camera_cfg.initialize_camera_capabilities(device)
5252
device:subscribe()
5353
if #switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.DOORBELL) > 0 then

drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ function DeviceConfiguration.match_profile(driver, device)
238238
local fan_device_type_ep_ids = switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.FAN)
239239
if #fan_device_type_ep_ids > 0 then
240240
updated_profile, optional_component_capabilities = FanDeviceConfiguration.assign_profile_for_fan_ep(device, default_endpoint_id)
241+
device:set_field(fields.MODULAR_PROFILE_UPDATED, true)
241242
end
242243

243244
-- initialize the main device card with buttons if applicable

drivers/SmartThings/matter-switch/src/switch_utils/fields.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ SwitchFields.ELECTRICAL_SENSOR_EPS = "__electrical_sensor_eps"
147147
--- for an Electrical Sensor EP with a "primary" endpoint, used during device profiling.
148148
SwitchFields.ELECTRICAL_TAGS = "__electrical_tags"
149149

150+
SwitchFields.MODULAR_PROFILE_UPDATED = "__modular_profile_updated"
151+
150152
SwitchFields.profiling_data = {
151153
POWER_TOPOLOGY = "__power_topology",
152154
BATTERY_SUPPORT = "__battery_support",

drivers/SmartThings/matter-switch/src/switch_utils/utils.lua

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -325,33 +325,6 @@ function utils.create_multi_press_values_list(size, supportsHeld)
325325
return list
326326
end
327327

328-
function utils.profile_changed(latest_profile, previous_profile)
329-
if latest_profile.id ~= previous_profile.id then
330-
return true
331-
end
332-
for component_id, synced_component in pairs(latest_profile.components or {}) do
333-
local prev_component = previous_profile.components[component_id]
334-
if prev_component == nil then
335-
return true
336-
end
337-
if #synced_component.capabilities ~= #prev_component.capabilities then
338-
return true
339-
end
340-
-- Build a table of capability IDs from the previous component. Then, use this map to check
341-
-- that all capabilities in the synced component existed in the previous component.
342-
local prev_cap_ids = {}
343-
for _, capability in ipairs(prev_component.capabilities or {}) do
344-
prev_cap_ids[capability.id] = true
345-
end
346-
for _, capability in ipairs(synced_component.capabilities or {}) do
347-
if not prev_cap_ids[capability.id] then
348-
return true
349-
end
350-
end
351-
end
352-
return false
353-
end
354-
355328
function utils.detect_bridge(device)
356329
return #utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.AGGREGATOR) > 0
357330
end

drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua

Lines changed: 7 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ local mock_device_ep2 = 2
1616

1717
local mock_device = test.mock_device.build_test_matter_device({
1818
label = "Matter Fan Light",
19-
profile = t_utils.get_profile_definition("fan-modular.yml",
20-
{enabled_optional_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}}}),
19+
profile = t_utils.get_profile_definition("fan-modular.yml", {}),
2120
manufacturer_info = {
2221
vendor_id = 0x0000,
2322
product_id = 0x0000,
@@ -59,40 +58,6 @@ local mock_device = test.mock_device.build_test_matter_device({
5958
}
6059
})
6160

62-
local mock_device_capabilities_disabled = test.mock_device.build_test_matter_device({
63-
label = "Matter Fan Light",
64-
profile = t_utils.get_profile_definition("fan-modular.yml",
65-
{enabled_optional_capabilities = {{"main", {}}}}),
66-
manufacturer_info = {
67-
vendor_id = 0x0000,
68-
product_id = 0x0000,
69-
},
70-
matter_version = {
71-
software = 1,
72-
hardware = 1,
73-
},
74-
endpoints = {
75-
{
76-
endpoint_id = 0,
77-
clusters = {
78-
{cluster_id = clusters.Basic.ID, cluster_type = "SERVER"},
79-
},
80-
device_types = {
81-
{device_type_id = 0x0016, device_type_revision = 1} -- RootNode
82-
}
83-
},
84-
{
85-
endpoint_id = mock_device_ep2,
86-
clusters = {
87-
{cluster_id = clusters.FanControl.ID, cluster_type = "SERVER", feature_map = 15},
88-
},
89-
device_types = {
90-
{device_type_id = 0x002B, device_type_revision = 1,} -- Fan
91-
}
92-
}
93-
}
94-
})
95-
9661
local CLUSTER_SUBSCRIBE_LIST ={
9762
clusters.OnOff.attributes.OnOff,
9863
clusters.LevelControl.attributes.CurrentLevel,
@@ -145,48 +110,16 @@ local function test_init()
145110
})
146111
mock_device:expect_metadata_update({ profile = "fan-modular", optional_component_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}} })
147112
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
113+
114+
local updated_device_profile = t_utils.get_profile_definition("fan-modular.yml",
115+
{enabled_optional_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}}}
116+
)
117+
test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ profile = updated_device_profile }))
118+
test.socket.matter:__expect_send({mock_device.id, subscribe_request})
148119
end
149120

150121
test.set_test_init_function(test_init)
151122

152-
test.register_coroutine_test(
153-
"Component-capability update without profile ID update should cause re-subscribe in infoChanged handler", function()
154-
local cluster_subscribe_list ={
155-
clusters.FanControl.attributes.FanModeSequence,
156-
clusters.FanControl.attributes.FanMode,
157-
clusters.FanControl.attributes.PercentCurrent,
158-
}
159-
local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_capabilities_disabled)
160-
for i, clus in ipairs(cluster_subscribe_list) do
161-
if i > 1 then subscribe_request:merge(clus:subscribe(mock_device_capabilities_disabled)) end
162-
end
163-
test.socket.device_lifecycle:__queue_receive(mock_device_capabilities_disabled:generate_info_changed(
164-
{profile = {id = "00000000-1111-2222-3333-000000000004", components = { main = {capabilities={{id="fanSpeedPercent", version=1}, {id="fanMode", version=1}, {id="firmwareUpdate", version=1}, {id="refresh", version=1}}}}}})
165-
)
166-
test.socket.matter:__expect_send({mock_device_capabilities_disabled.id, subscribe_request})
167-
end,
168-
{ test_init = function() test.mock_device.add_test_device(mock_device_capabilities_disabled) end }
169-
)
170-
171-
test.register_coroutine_test(
172-
"No component-capability update an no profile ID update should not cause a re-subscribe in infoChanged handler", function()
173-
local cluster_subscribe_list ={
174-
clusters.FanControl.attributes.FanModeSequence,
175-
clusters.FanControl.attributes.FanMode,
176-
clusters.FanControl.attributes.PercentCurrent,
177-
}
178-
local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_capabilities_disabled)
179-
for i, clus in ipairs(cluster_subscribe_list) do
180-
if i > 1 then subscribe_request:merge(clus:subscribe(mock_device_capabilities_disabled)) end
181-
end
182-
test.socket.device_lifecycle:__queue_receive(mock_device_capabilities_disabled:generate_info_changed(
183-
{profile = {id = "00000000-1111-2222-3333-000000000004", components = { main = {capabilities={{id="firmwareUpdate", version=1}, {id="refresh", version=1}}}}}})
184-
)
185-
end,
186-
{ test_init = function() test.mock_device.add_test_device(mock_device_capabilities_disabled) end }
187-
)
188-
189-
190123
test.register_coroutine_test(
191124
"Switch capability should send the appropriate commands", function()
192125
test.socket.capability:__queue_receive(

0 commit comments

Comments
 (0)