Skip to content

Commit 3de35fd

Browse files
committed
update subscribe for main driver and camera subdriver
1 parent 2a377a1 commit 3de35fd

File tree

14 files changed

+348
-426
lines changed

14 files changed

+348
-426
lines changed

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

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ function SwitchLifecycleHandlers.do_configure(driver, device)
4949
if device.network_type == device_lib.NETWORK_TYPE_MATTER and not switch_utils.detect_bridge(device) then
5050
switch_cfg.set_device_control_options(device)
5151
device_cfg.match_profile(driver, device)
52+
elseif device.network_type == device_lib.NETWORK_TYPE_CHILD then
53+
-- because get_parent_device() may cause race conditions if used in init, an initial child subscribe is handled in doConfigure.
54+
-- all future calls to subscribe will be handled by the parent device in init
55+
device:subscribe()
5256
end
5357
end
5458

@@ -68,7 +72,7 @@ function SwitchLifecycleHandlers.info_changed(driver, device, event, args)
6872
device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH})
6973
)
7074
elseif device.network_type == device_lib.NETWORK_TYPE_CHILD then
71-
switch_utils.update_subscriptions(device:get_parent_device()) -- parent device required to scan through EPs and update subscriptions
75+
device:get_parent_device():subscribe() -- parent device required to send subscription requests
7276
end
7377
end
7478

@@ -87,7 +91,8 @@ function SwitchLifecycleHandlers.device_init(driver, device)
8791
if device:get_field(fields.IS_PARENT_CHILD_DEVICE) then
8892
device:set_find_child(switch_utils.find_child)
8993
end
90-
switch_utils.update_subscriptions(device)
94+
device:extend_device("subscribe", switch_utils.subscribe)
95+
device:subscribe()
9196

9297
-- device energy reporting must be handled cumulatively, periodically, or by both simulatanously.
9398
-- To ensure a single source of truth, we only handle a device's periodic reporting if cumulative reporting is not supported.
@@ -290,7 +295,40 @@ local matter_driver_template = {
290295
[capabilities.valve.commands.open.NAME] = capability_handlers.handle_valve_open,
291296
},
292297
},
293-
supported_capabilities = fields.supported_capabilities,
298+
supported_capabilities = {
299+
capabilities.audioMute,
300+
capabilities.audioRecording,
301+
capabilities.audioVolume,
302+
capabilities.battery,
303+
capabilities.batteryLevel,
304+
capabilities.button,
305+
capabilities.cameraPrivacyMode,
306+
capabilities.cameraViewportSettings,
307+
capabilities.colorControl,
308+
capabilities.colorTemperature,
309+
capabilities.energyMeter,
310+
capabilities.fanMode,
311+
capabilities.fanSpeedPercent,
312+
capabilities.hdr,
313+
capabilities.illuminanceMeasurement,
314+
capabilities.imageControl,
315+
capabilities.level,
316+
capabilities.localMediaStorage,
317+
capabilities.mechanicalPanTiltZoom,
318+
capabilities.motionSensor,
319+
capabilities.nightVision,
320+
capabilities.powerMeter,
321+
capabilities.powerConsumptionReport,
322+
capabilities.relativeHumidityMeasurement,
323+
capabilities.sounds,
324+
capabilities.switch,
325+
capabilities.switchLevel,
326+
capabilities.temperatureMeasurement,
327+
capabilities.valve,
328+
capabilities.videoStreamSettings,
329+
capabilities.webrtc,
330+
capabilities.zoneManagement
331+
},
294332
sub_drivers = {
295333
switch_utils.lazy_load_if_possible("sub_drivers.aqara_cube"),
296334
switch_utils.lazy_load("sub_drivers.camera"),

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

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ function CameraUtils.subscribe(device)
193193
clusters.CameraAvStreamManagement.attributes.StatusLightBrightness
194194
},
195195
[capabilities.switch.ID] = {
196-
clusters.CameraAvStreamManagement.attributes.StatusLightEnabled
196+
clusters.CameraAvStreamManagement.attributes.StatusLightEnabled,
197+
clusters.OnOff.attributes.OnOff
197198
},
198199
[capabilities.videoStreamSettings.ID] = {
199200
clusters.CameraAvStreamManagement.attributes.RateDistortionTradeOffPoints,
@@ -223,8 +224,26 @@ function CameraUtils.subscribe(device)
223224
},
224225
[capabilities.motionSensor.ID] = {
225226
clusters.OccupancySensing.attributes.Occupancy
226-
}
227+
},
228+
[capabilities.switchLevel.ID] = {
229+
clusters.LevelControl.attributes.CurrentLevel,
230+
clusters.LevelControl.attributes.MaxLevel,
231+
clusters.LevelControl.attributes.MinLevel,
232+
},
233+
[capabilities.colorControl.ID] = {
234+
clusters.ColorControl.attributes.ColorMode,
235+
clusters.ColorControl.attributes.CurrentHue,
236+
clusters.ColorControl.attributes.CurrentSaturation,
237+
clusters.ColorControl.attributes.CurrentX,
238+
clusters.ColorControl.attributes.CurrentY,
239+
},
240+
[capabilities.colorTemperature.ID] = {
241+
clusters.ColorControl.attributes.ColorTemperatureMireds,
242+
clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds,
243+
clusters.ColorControl.attributes.ColorTempPhysicalMinMireds,
244+
},
227245
}
246+
228247
local camera_subscribed_events = {
229248
[capabilities.zoneManagement.ID] = {
230249
clusters.ZoneManagement.events.ZoneTriggered,
@@ -238,56 +257,26 @@ function CameraUtils.subscribe(device)
238257
}
239258
}
240259

241-
for capability, attr_list in pairs(camera_subscribed_attributes) do
242-
if device:supports_capability_by_id(capability) then
243-
for _, attr in pairs(attr_list) do
244-
device:add_subscribed_attribute(attr)
245-
end
246-
end
247-
end
248-
for capability, event_list in pairs(camera_subscribed_events) do
249-
if device:supports_capability_by_id(capability) then
250-
for _, event in pairs(event_list) do
251-
device:add_subscribed_event(event)
252-
end
253-
end
254-
end
260+
local im = require "st.matter.interaction_model"
261+
262+
local subscribe_request = im.InteractionRequest(im.InteractionRequest.RequestType.SUBSCRIBE, {})
263+
local devices_seen, capabilities_seen, attributes_seen, events_seen = {}, {}, {}, {}
255264

256-
-- match_profile is called from the CameraAvStreamManagement AttributeList handler,
257-
-- so the subscription needs to be added here first
258265
if #device:get_endpoints(clusters.CameraAvStreamManagement.ID) > 0 then
259-
device:add_subscribed_attribute(clusters.CameraAvStreamManagement.attributes.AttributeList)
266+
local ib = im.InteractionInfoBlock(nil, clusters.CameraAvStreamManagement.ID, clusters.CameraAvStreamManagement.attributes.AttributeList.ID)
267+
subscribe_request:with_info_block(ib)
260268
end
261269

262-
-- Add subscription for attributes specific to child devices
263-
if device:get_field(fields.IS_PARENT_CHILD_DEVICE) then
264-
for _, ep in ipairs(device.endpoints or {}) do
265-
local id = 0
266-
for _, dt in ipairs(ep.device_types or {}) do
267-
if dt.device_type_id ~= fields.DEVICE_TYPE_ID.GENERIC_SWITCH then
268-
id = math.max(id, dt.device_type_id)
269-
end
270-
end
271-
for _, attr in pairs(fields.device_type_attribute_map[id] or {}) do
272-
device:add_subscribed_attribute(attr)
273-
end
270+
for _, endpoint_info in ipairs(device.endpoints) do
271+
local checked_device = switch_utils.find_child(device, endpoint_info.endpoint_id) or device
272+
if not devices_seen[checked_device.id] then
273+
switch_utils.populate_subscribe_request_for_device(checked_device, subscribe_request, capabilities_seen, attributes_seen, events_seen,
274+
camera_subscribed_attributes, camera_subscribed_events
275+
)
276+
devices_seen[checked_device.id] = true -- only loop through any device once
274277
end
275278
end
276279

277-
local im = require "st.matter.interaction_model"
278-
local subscribed_attributes = device:get_field("__subscribed_attributes") or {}
279-
local subscribed_events = device:get_field("__subscribed_events") or {}
280-
local subscribe_request = im.InteractionRequest(im.InteractionRequest.RequestType.SUBSCRIBE, {})
281-
for _, attributes in pairs(subscribed_attributes) do
282-
for _, ib in pairs(attributes) do
283-
subscribe_request:with_info_block(ib)
284-
end
285-
end
286-
for _, events in pairs(subscribed_events) do
287-
for _, ib in pairs(events) do
288-
subscribe_request:with_info_block(ib)
289-
end
290-
end
291280
if #subscribe_request.info_blocks > 0 then
292281
device:send(subscribe_request)
293282
end

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ end
4949
function CameraLifecycleHandlers.info_changed(driver, device, event, args)
5050
if camera_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then
5151
camera_cfg.initialize_camera_capabilities(device)
52+
device:subscribe()
5253
if #switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.DOORBELL) > 0 then
5354
button_cfg.configure_buttons(device, device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH}))
5455
end
55-
device:subscribe()
5656
end
5757
end
5858

drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ function AttributeHandlers.color_temperature_mireds_handler(driver, device, ib,
139139
end
140140

141141
function AttributeHandlers.current_x_handler(driver, device, ib, response)
142-
if device:get_field(fields.COLOR_MODE) == fields.HUE_SAT_COLOR_MODE then
142+
if device:get_field(fields.COLOR_MODE) == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then
143143
return
144144
end
145145
local y = device:get_field(fields.RECEIVED_Y)
@@ -157,7 +157,7 @@ function AttributeHandlers.current_x_handler(driver, device, ib, response)
157157
end
158158

159159
function AttributeHandlers.current_y_handler(driver, device, ib, response)
160-
if device:get_field(fields.COLOR_MODE) == fields.HUE_SAT_COLOR_MODE then
160+
if device:get_field(fields.COLOR_MODE) == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then
161161
return
162162
end
163163
local x = device:get_field(fields.RECEIVED_X)
@@ -173,15 +173,17 @@ function AttributeHandlers.current_y_handler(driver, device, ib, response)
173173
end
174174

175175
function AttributeHandlers.color_mode_handler(driver, device, ib, response)
176-
if ib.data.value == device:get_field(fields.COLOR_MODE) or (ib.data.value ~= fields.HUE_SAT_COLOR_MODE and ib.data.value ~= fields.X_Y_COLOR_MODE) then
177-
return
176+
if ib.data.value == device:get_field(fields.COLOR_MODE)
177+
or (ib.data.value ~= clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION
178+
and ib.data.value ~= clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY) then
179+
return
178180
end
179181
device:set_field(fields.COLOR_MODE, ib.data.value)
180182
local req = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {})
181-
if ib.data.value == fields.HUE_SAT_COLOR_MODE then
183+
if ib.data.value == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then
182184
req:merge(clusters.ColorControl.attributes.CurrentHue:read())
183185
req:merge(clusters.ColorControl.attributes.CurrentSaturation:read())
184-
elseif ib.data.value == fields.X_Y_COLOR_MODE then
186+
elseif ib.data.value == clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY then
185187
req:merge(clusters.ColorControl.attributes.CurrentX:read())
186188
req:merge(clusters.ColorControl.attributes.CurrentY:read())
187189
end

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

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
-- Copyright © 2025 SmartThings, Inc.
22
-- Licensed under the Apache License, Version 2.0
33

4-
local clusters = require "st.matter.clusters"
5-
local capabilities = require "st.capabilities"
6-
local version = require "version"
7-
8-
-- Include driver-side definitions when lua libs api version is < 11
9-
if version.api < 11 then
10-
clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement"
11-
clusters.ElectricalPowerMeasurement = require "embedded_clusters.ElectricalPowerMeasurement"
12-
end
13-
144
local SwitchFields = {}
155

16-
SwitchFields.HUE_SAT_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION
17-
SwitchFields.X_Y_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY
18-
196
SwitchFields.MOST_RECENT_TEMP = "mostRecentTemp"
207
SwitchFields.RECEIVED_X = "receivedX"
218
SwitchFields.RECEIVED_Y = "receivedY"
229
SwitchFields.HUESAT_SUPPORT = "huesatSupport"
2310

24-
2511
SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT = 1000000
2612

2713
-- These values are a "sanity check" to check that values we are getting are reasonable
@@ -192,119 +178,4 @@ SwitchFields.TRANSITION_TIME = 0 --1/10ths of a second
192178
SwitchFields.OPTIONS_MASK = 0x01
193179
SwitchFields.OPTIONS_OVERRIDE = 0x01
194180

195-
196-
SwitchFields.supported_capabilities = {
197-
capabilities.audioMute,
198-
capabilities.audioRecording,
199-
capabilities.audioVolume,
200-
capabilities.battery,
201-
capabilities.batteryLevel,
202-
capabilities.button,
203-
capabilities.cameraPrivacyMode,
204-
capabilities.cameraViewportSettings,
205-
capabilities.colorControl,
206-
capabilities.colorTemperature,
207-
capabilities.energyMeter,
208-
capabilities.fanMode,
209-
capabilities.fanSpeedPercent,
210-
capabilities.hdr,
211-
capabilities.illuminanceMeasurement,
212-
capabilities.imageControl,
213-
capabilities.level,
214-
capabilities.localMediaStorage,
215-
capabilities.mechanicalPanTiltZoom,
216-
capabilities.motionSensor,
217-
capabilities.nightVision,
218-
capabilities.powerMeter,
219-
capabilities.powerConsumptionReport,
220-
capabilities.relativeHumidityMeasurement,
221-
capabilities.sounds,
222-
capabilities.switch,
223-
capabilities.switchLevel,
224-
capabilities.temperatureMeasurement,
225-
capabilities.valve,
226-
capabilities.videoStreamSettings,
227-
capabilities.webrtc,
228-
capabilities.zoneManagement
229-
}
230-
231-
SwitchFields.device_type_attribute_map = {
232-
[SwitchFields.DEVICE_TYPE_ID.LIGHT.ON_OFF] = {
233-
clusters.OnOff.attributes.OnOff
234-
},
235-
[SwitchFields.DEVICE_TYPE_ID.LIGHT.DIMMABLE] = {
236-
clusters.OnOff.attributes.OnOff,
237-
clusters.LevelControl.attributes.CurrentLevel,
238-
clusters.LevelControl.attributes.MaxLevel,
239-
clusters.LevelControl.attributes.MinLevel
240-
},
241-
[SwitchFields.DEVICE_TYPE_ID.LIGHT.COLOR_TEMPERATURE] = {
242-
clusters.OnOff.attributes.OnOff,
243-
clusters.LevelControl.attributes.CurrentLevel,
244-
clusters.LevelControl.attributes.MaxLevel,
245-
clusters.LevelControl.attributes.MinLevel,
246-
clusters.ColorControl.attributes.ColorTemperatureMireds,
247-
clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds,
248-
clusters.ColorControl.attributes.ColorTempPhysicalMinMireds
249-
},
250-
[SwitchFields.DEVICE_TYPE_ID.LIGHT.EXTENDED_COLOR] = {
251-
clusters.OnOff.attributes.OnOff,
252-
clusters.LevelControl.attributes.CurrentLevel,
253-
clusters.LevelControl.attributes.MaxLevel,
254-
clusters.LevelControl.attributes.MinLevel,
255-
clusters.ColorControl.attributes.ColorTemperatureMireds,
256-
clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds,
257-
clusters.ColorControl.attributes.ColorTempPhysicalMinMireds,
258-
clusters.ColorControl.attributes.CurrentHue,
259-
clusters.ColorControl.attributes.CurrentSaturation,
260-
clusters.ColorControl.attributes.CurrentX,
261-
clusters.ColorControl.attributes.CurrentY,
262-
clusters.ColorControl.attributes.ColorMode
263-
},
264-
[SwitchFields.DEVICE_TYPE_ID.ON_OFF_PLUG_IN_UNIT] = {
265-
clusters.OnOff.attributes.OnOff
266-
},
267-
[SwitchFields.DEVICE_TYPE_ID.DIMMABLE_PLUG_IN_UNIT] = {
268-
clusters.OnOff.attributes.OnOff,
269-
clusters.LevelControl.attributes.CurrentLevel,
270-
clusters.LevelControl.attributes.MaxLevel,
271-
clusters.LevelControl.attributes.MinLevel
272-
},
273-
[SwitchFields.DEVICE_TYPE_ID.SWITCH.ON_OFF_LIGHT] = {
274-
clusters.OnOff.attributes.OnOff
275-
},
276-
[SwitchFields.DEVICE_TYPE_ID.SWITCH.DIMMER] = {
277-
clusters.OnOff.attributes.OnOff,
278-
clusters.LevelControl.attributes.CurrentLevel,
279-
clusters.LevelControl.attributes.MaxLevel,
280-
clusters.LevelControl.attributes.MinLevel
281-
},
282-
[SwitchFields.DEVICE_TYPE_ID.SWITCH.COLOR_DIMMER] = {
283-
clusters.OnOff.attributes.OnOff,
284-
clusters.LevelControl.attributes.CurrentLevel,
285-
clusters.LevelControl.attributes.MaxLevel,
286-
clusters.LevelControl.attributes.MinLevel,
287-
clusters.ColorControl.attributes.ColorTemperatureMireds,
288-
clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds,
289-
clusters.ColorControl.attributes.ColorTempPhysicalMinMireds,
290-
clusters.ColorControl.attributes.CurrentHue,
291-
clusters.ColorControl.attributes.CurrentSaturation,
292-
clusters.ColorControl.attributes.CurrentX,
293-
clusters.ColorControl.attributes.CurrentY,
294-
clusters.ColorControl.attributes.ColorMode
295-
},
296-
[SwitchFields.DEVICE_TYPE_ID.GENERIC_SWITCH] = {
297-
clusters.PowerSource.attributes.BatPercentRemaining,
298-
clusters.Switch.events.InitialPress,
299-
clusters.Switch.events.LongPress,
300-
clusters.Switch.events.ShortRelease,
301-
clusters.Switch.events.MultiPressComplete
302-
},
303-
[SwitchFields.DEVICE_TYPE_ID.ELECTRICAL_SENSOR] = {
304-
clusters.ElectricalPowerMeasurement.attributes.ActivePower,
305-
clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported,
306-
clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported
307-
}
308-
}
309-
310-
return SwitchFields
181+
return SwitchFields

0 commit comments

Comments
 (0)