Skip to content

Support additional Camera device types#2590

Open
nickolas-deboom wants to merge 13 commits intomainfrom
support-additional-camera-device-types
Open

Support additional Camera device types#2590
nickolas-deboom wants to merge 13 commits intomainfrom
support-additional-camera-device-types

Conversation

@nickolas-deboom
Copy link
Contributor

@nickolas-deboom nickolas-deboom commented Dec 1, 2025

This adds support for the following device types:

  • 0x0140 Intercom
  • 0x0141 Audio Doorbell
  • 0x0142 Camera
  • 0x0143 Video Doorbell
  • 0x0144 Floodlight Camera
  • 0x0145 Snapshot Camera
  • 0x0146 Chime
  • 0x0148 DoorBell

@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Duplicate profile check: Passed - no duplicate profiles detected.

@github-actions
Copy link

github-actions bot commented Dec 1, 2025

@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Test Results

   72 files    492 suites   0s ⏱️
2 692 tests 2 692 ✅ 0 💤 0 ❌
4 553 runs  4 553 ✅ 0 💤 0 ❌

Results for commit a19ba8c.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Dec 1, 2025

File Coverage
All files 93%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua 98%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/device_configuration.lua 98%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_handlers/capability_handlers.lua 82%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_handlers/attribute_handlers.lua 96%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua 92%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/embedded_cluster_utils.lua 38%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua 96%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/eve_energy/init.lua 91%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/third_reality_mk1/init.lua 95%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_handlers/event_handlers.lua 97%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua 90%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua 84%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua 97%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/aqara_cube/init.lua 96%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua 89%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/init.lua 97%
/home/runner/work/SmartThingsEdgeDrivers/SmartThingsEdgeDrivers/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua 93%

Minimum allowed coverage is 90%

Generated by 🐒 cobertura-action against a19ba8c

Copy link
Contributor

@samadDotDev samadDotDev left a comment

Choose a reason for hiding this comment

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

Will need to re-evaluate after rebase

for _, ep in ipairs(device.endpoints) do
for _, dt in ipairs(ep.device_types) do
if dt.device_type_id == device_type_id then
if type(device_type_id) == "table" then
Copy link
Contributor

Choose a reason for hiding this comment

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

this function will have to be rebased a bit carefully- there have been updates here since this PR was created.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the callout - I updated this function a bit in the last force push

@nickolas-deboom nickolas-deboom force-pushed the support-additional-camera-device-types branch from 96ccae6 to 67332b9 Compare February 10, 2026 21:19
@nickolas-deboom nickolas-deboom force-pushed the support-additional-camera-device-types branch 2 times, most recently from e012e2a to a25f0c7 Compare March 3, 2026 17:45
end

-- get a list of endpoints for a specified device type.
-- get a list of endpoints for a specified device type or list of device types
Copy link
Contributor

Choose a reason for hiding this comment

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

can we add a proper lua doc here, like:

--- @param device ...
--- @param device_type_id number|table a specified device type or list of device types
--- @param opts ....
--- @return table ...

I think there's enough variation in return and input here to warrant proper use docs.

Copy link
Contributor Author

@nickolas-deboom nickolas-deboom Mar 5, 2026

Choose a reason for hiding this comment

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

Done in 5e52d8a

table.insert(dt_eps, ep)
else
table.insert(dt_eps, ep.endpoint_id)
if type(device_type_id) == "table" then
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than check for a table in the loop body, maybe at the top do something like:

if device_type_id == nil then return end
device_type_id = device_type_id and type(device_type_id) == "table" or { device_type_id }

Then we can ensure the type + an added nil check before we event start the loop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 5e52d8a!

Comment on lines +316 to +318
camera_subscribed_attributes[capabilities.audioMute.ID] = {
clusters.Chime.attributes.Enabled
}
Copy link
Contributor

Choose a reason for hiding this comment

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

can we just add this to the table?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding this to the table revealed an issue in populate_subscribe_request_for_device. I noticed that the standalone Chime mock device in test_matter_camera.lua was subscribing to the other attributes listed for audioMute in the camera_subscribed_attributes table (clusters.CameraAvStreamManagement.attributes.SpeakerMuted & clusters.CameraAvStreamManagement.attributes.MicrophoneMuted), even though the mock device does not support the CAVSM cluster.

The issue is that populate_subscribe_request_for_device does not perform a check to make sure the device supports a given cluster before subscribing to the attributes like add_subscribed_attribute does. I added a check in 0542fb3. Let me know if you have thoughts on this approach!

Note that adding this check lead to failures in test_matter_sensor_offset_preferences.lua because it was missing some endpoint definitions in its mock device and therefore no longer subscribing to certain attributes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah this works! Maybe let's also add an else log like else log.warn_with({ hub_logs = true }, string.format("Device does not support cluster 0x%04X, not adding subscribed event", cluster_id)) end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in fb72b21!

@@ -6,116 +6,166 @@ local dkjson = require "dkjson"
local clusters = require "st.matter.clusters"

local mock_device = test.mock_device.build_test_matter_device({
Copy link
Contributor Author

@nickolas-deboom nickolas-deboom Mar 5, 2026

Choose a reason for hiding this comment

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

Mentioned elsewhere, but after making the changes in 0542fb3 the tests here began to fail because they expect subscriptions to the button events yet no endpoints with the Switch cluster were present in the mock device. I added the endpoints and also fixed the tabbing in this file which is why the diff looks so large

@nickolas-deboom nickolas-deboom force-pushed the support-additional-camera-device-types branch 2 times, most recently from 31bd85e to 164095f Compare March 5, 2026 21:21
dpUri: "plugin://com.samsung.android.plugin.camera"
metadata:
mnmn: SmartThingsEdge
vid: matter-camera
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
vid: matter-camera
vid: matter-av-doorbell

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in b082796

}
})

local mock_device_doorbell = test.mock_device.build_test_matter_device({
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably worth creating one for video doorbell too due to the superset device type nuances

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 5a1631d!

if status_light_brightness_present then
table.insert(status_led_component_capabilities, capabilities.mode.ID)
end
if #device:get_endpoints(clusters.Chime.ID, {cluster_type = "SERVER"}) > 0 then
Copy link
Contributor

Choose a reason for hiding this comment

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

SERVER or BOTH? Basically reuse logic similar to has_server_cluster_type()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in b082796, does that look alright? It uses the first endpoint containing the Chime cluster, but I wasn't sure if the client and server clusters would necessarily be on the same endpoint or not

@nickolas-deboom nickolas-deboom force-pushed the support-additional-camera-device-types branch 2 times, most recently from b1faa74 to 297c2e3 Compare March 9, 2026 20:57
--- @param subscribed_events table key-value pairs mapping capability ids to subscribed events
function utils.populate_subscribe_request_for_device(checked_device, subscribe_request, capabilities_seen, attributes_seen, events_seen, subscribed_attributes, subscribed_events)
for _, component in pairs(checked_device.st_store.profile.components) do
function utils.populate_subscribe_request_for_device(checked_device, parent_device, subscribe_request, capabilities_seen, attributes_seen, events_seen, subscribed_attributes, subscribed_events)
Copy link
Contributor

Choose a reason for hiding this comment

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

Before merging, let's document a little testing here, since this change does affect our subscription logic. LGTM, but I think it's still worth ensuring we haven't missed something

BRIDGED_NODE = 0x0013,
CAMERA = 0x0142,
CAMERA = {
INTERCOM = 0x0140,
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't a camera really?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Intercoms are grouped with Camera in the spec and since intercoms create an audio connection they use CAVSM and other camera related clusters I think it makes sense to leave it here. (unlike doorbell and chime, neither of which include camera related clusters)

AUDIO_DOORBELL = 0x0141,
CAMERA = 0x0142,
VIDEO_DOORBELL = 0x0143,
FLOODLIGHT_CAMERA = 0x0144,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to define Floodlight camera now? Since this is a composite device type is there any unique handling that would require this device type?

Copy link
Contributor

@hcarter-775 hcarter-775 Mar 9, 2026

Choose a reason for hiding this comment

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

Similar question for VIDEO_DOORBELL? It appears that all current handling exist by separating out CAMERA and DOORBELL explicitly?

I guess I'm not sure what should be done, but to me it feels like it would be good to clarify and either 1. just remove this/these references to device types that we don't use, or 2. add explicit handling for these device type configurations instead of handling a "if one exists but not the other, elseif " case-by-case basis kind of thing.

Copy link
Contributor Author

@nickolas-deboom nickolas-deboom Mar 10, 2026

Choose a reason for hiding this comment

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

For video doorbell, we need the device type listed here in order to differentiate between CAMERA and VIDEO_DOORBELL so we match to the proper profile (av-doorbell). And same with Audio DoorBell. We don't necessarily need Floodlight Camera since it would currently use the camera profile rather than a distinct one, but I don't see harm in leaving it here since we may create a new category/icon and profile for it eventually

Copy link
Contributor

Choose a reason for hiding this comment

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

Due to the device type requirements being different between floodlight camera (composed of on/off light and camera device type) and the snapshot camera (a standalone device type - doesn't contain camera device type), I think we should just avoid clustering these together. This also makes it less confusing when trying to access CAMERA.CAMERA for the base camera device type id

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 17d5bb3

CAMERA = 0x0142,
CAMERA = {
INTERCOM = 0x0140,
AUDIO_DOORBELL = 0x0141,
Copy link
Contributor

Choose a reason for hiding this comment

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

This also isn't really a camera?

-- Licensed under the Apache License, Version 2.0

local clusters = require "st.matter.clusters"
local switch_fields = require "switch_utils.fields"
Copy link
Contributor

Choose a reason for hiding this comment

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

remove, unused

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@samadDotDev samadDotDev self-requested a review March 10, 2026 16:53
AUDIO_DOORBELL = 0x0141,
CAMERA = 0x0142,
VIDEO_DOORBELL = 0x0143,
FLOODLIGHT_CAMERA = 0x0144,
Copy link
Contributor

Choose a reason for hiding this comment

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

Due to the device type requirements being different between floodlight camera (composed of on/off light and camera device type) and the snapshot camera (a standalone device type - doesn't contain camera device type), I think we should just avoid clustering these together. This also makes it less confusing when trying to access CAMERA.CAMERA for the base camera device type id


if #camera_endpoints > 0 then
local camera_ep = switch_utils.get_endpoint_info(device, camera_endpoints[1])
local camera_ep = switch_utils.get_endpoint_info(device, av_stream_ep_ids[1] or camera_endpoints[1])
Copy link
Contributor

Choose a reason for hiding this comment

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

Unless a separate category is needed based on device types (which is already covered later by keyeing off doorbell), the endpoints look up here should be specific to the cluster itself to avoid undefined scenarios where device type doesn't match and the cluster could be on a separate endpoint.

Suggested change
local camera_ep = switch_utils.get_endpoint_info(device, av_stream_ep_ids[1] or camera_endpoints[1])
local av_stream_ep = switch_utils.get_endpoint_info(device, av_stream_ep_ids[1])

You'll also need to do the same for CameraAvSettingsUserLevelManagement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, see 17d5bb3. This helped clean up some of the profiling logic as well

end
if clus_has_feature(clusters.CameraAvStreamManagement.types.Feature.VIDEO) then
if switch_utils.find_cluster_on_ep(camera_ep, clusters.PushAvStreamTransport.ID, "SERVER") then
if #device:get_endpoints(clusters.PushAvStreamTransport.ID, {cluster_type = "SERVER"}) > 0 then
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing is needed for the PushAV gating of audioRecording

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

@samadDotDev samadDotDev self-requested a review March 10, 2026 16:54
nickolas-deboom and others added 11 commits March 12, 2026 14:58
This adds support for the following device types:
 *  0x0140 Intercom
 *  0x0141 Audio Doorbell
 *  0x0142 Camera
 *  0x0143 Video Doorbell
 *  0x0144 Floodlight Camera
 *  0x0145 Snapshot Camera
 *  0x0146 Chime
 *  0x0148 DoorBell
Fix populate_subscribe_request_for_device incorrectly filtering out cluster subscriptions for child devices. The function previously derived parent_device internally via checked_device.parent_device or checked_device, which falls back to the child itself when .parent_device is nil, as is the case with mock child devices in tests. This caused the #device:get_endpoints(cluster_id) > 0 check (introduced in the previous commit) to drop subscriptions for clusters that only exist on the parent's endpoints.

Also fix test_matter_sensor_offset_preferences.lua, which had an incomplete mock device endpoint table.
On devices with multiple camera related endpoints (e.g. a Video Doorbell
with separate Video Doorbell (0x143) and Camera (0x142) device type
endpoints), camera_endpoints[1] could resolve to the Video Doorbell
endpoint, which does not contain the CAVSM cluster, causing the
cluster feature inspection loop to find nothing and leave
main_component_capabilities empty.

Fix by resolving the camera endpoint via device:get_endpoints() on the
CAVSM cluster directly.
PushAvStreamTransport may not be on the same endpoint as CAVSM so we
should check the fill device rather than just the CAVSM endpoint for
this cluster.
@nickolas-deboom nickolas-deboom force-pushed the support-additional-camera-device-types branch from 1cd867b to d845bcb Compare March 12, 2026 19:58
@nickolas-deboom nickolas-deboom force-pushed the support-additional-camera-device-types branch from 63e0a39 to 17d5bb3 Compare March 12, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants