Summary
videoStreamSettings.supportedResolutions is currently calculated from the camera's reported pixel-rate/FPS limits, but the stream allocator separately caps VideoStreamAllocate at the driver's max_frame_rate default of 60 fps.
This can make SmartThings advertise a frame rate that the same driver will never request.
The G350 exposed the mismatch directly: SmartThings advertised 120 fps resolutions while the allocation code was capped at 60 fps.
Environment / verification
Verified against SmartThingsCommunity/SmartThingsEdgeDrivers main at commit
60bbf2716412fdf04fd59f967bdd37be6995d664 (2026-08-12).
Real-device validation used:
- Aqara Camera Hub G350
- VID/PID:
0x115F / 0x3013
- firmware:
4.5.70 / 4005070
- reported Matter SpecificationVersion:
1.5.1
- Camera device type:
0x0142
- Camera endpoint in this device: endpoint 2
The proposed changes are generic Matter-camera changes. No Aqara VID/PID fingerprint or proprietary Aqara cluster handling is required.
Current behavior
Supported resolution FPS is calculated using the device-reported max_fps:
There is no driver-allocation ceiling applied in build_supported_resolutions().
The driver's stream defaults define:
min_frame_rate = 30
max_frame_rate = 60
When a stream is reallocated, the code caps max frame rate at that 60 fps default:
It also uses cmd.args.resolution.width and .height but does not use an explicitly selected cmd.args.resolution.fps to constrain the new allocation.
Reproduction
The G350 reported:
VideoSensorParams:
width: 1920
height: 1080
maxFPS: 120
SmartThings consequently advertised:
"supportedResolutions": [
{
"width": 640,
"height": 480,
"fps": 120
},
{
"width": 1920,
"height": 1080,
"fps": 120
}
]
However, VideoStreamAllocate uses:
math.min(device_max_fps, camera_fields.video_stream_defaults.max_frame_rate)
where the default maximum is 60.
Therefore the UI/API advertises 120 fps while the allocator cannot request more than 60 fps.
Expected behavior
The advertised supported resolution/FPS tuples and the actual allocation path should agree.
Two reasonable implementations are possible.
Option A: conservative advertisement
Clamp advertised FPS to the same driver/platform allocation ceiling:
max_fps = math.min(max_fps, camera_fields.video_stream_defaults.max_frame_rate)
before building supportedResolutions.
Option B: honor selected FPS
When cmd.args.resolution.fps is provided, use it for the allocation, clamped against:
- device maximum;
- platform/driver maximum;
- any other applicable stream constraints.
For consistency, the test build used both principles:
- never advertise more than the allocator is willing to request;
- honor the selected FPS within that valid range.
G350 evidence
Before the fix:
advertised: 120 fps
allocator ceiling: 60 fps
After applying the conservative advertisement logic in the test driver, the SmartThings capability no longer claimed an FPS above the driver's allocation ceiling.
The G350 continued to stream normally.
Affected files
src/sub_drivers/camera/camera_utils/utils.lua
src/sub_drivers/camera/camera_handlers/capability_handlers.lua
src/test/test_matter_camera.lua
Potentially:
src/sub_drivers/camera/camera_utils/fields.lua if the allocation ceiling is refactored.
Suggested regression coverage
Use a camera fixture with:
device max FPS = 120
driver max FPS = 60
Verify:
supportedResolutions[*].fps <= 60;
- stream allocation never requests a value higher than the advertised value;
- when the user selects a supported FPS, the allocation request honors it;
- lower-FPS cameras are unaffected.
Minimal suggested diff
Applies to SmartThingsCommunity/SmartThingsEdgeDrivers commit
60bbf2716412fdf04fd59f967bdd37be6995d664.
diff --git a/src/sub_drivers/camera/camera_handlers/capability_handlers.lua b/src/sub_drivers/camera/camera_handlers/capability_handlers.lua
index a26afd0..dd2e5ad 100644
--- a/src/sub_drivers/camera/camera_handlers/capability_handlers.lua
+++ b/src/sub_drivers/camera/camera_handlers/capability_handlers.lua
@@ -469,14 +469,25 @@ function CameraCapabilityHandlers.handle_set_stream(driver, device, cmd)
on_screen_display_enabled = camera_fields.video_stream_defaults.on_screen_display_enabled
end
+ local max_supported_fps = math.min(
+ device:get_field(camera_fields.MAX_FRAMES_PER_SECOND) or camera_fields.video_stream_defaults.max_frame_rate,
+ camera_fields.video_stream_defaults.max_frame_rate
+ )
+ local min_frame_rate = camera_fields.video_stream_defaults.min_frame_rate
+ local max_frame_rate = max_supported_fps
+ if cmd.args.resolution ~= nil and cmd.args.resolution.fps ~= nil then
+ local requested_fps = utils.clamp_value(cmd.args.resolution.fps, 1, max_supported_fps)
+ min_frame_rate = requested_fps
+ max_frame_rate = requested_fps
+ end
+
-- Use the same resolution (if available) for MinResolution and MaxResolution to force the server to allocate the
-- stream with the desired resolution.
device:send(clusters.CameraAvStreamManagement.server.commands.VideoStreamAllocate(device, endpoint_id,
stream_usage,
camera_fields.video_stream_defaults.codec,
- camera_fields.video_stream_defaults.min_frame_rate,
- math.min(device:get_field(camera_fields.MAX_FRAMES_PER_SECOND) or camera_fields.video_stream_defaults.max_frame_rate,
- camera_fields.video_stream_defaults.max_frame_rate),
+ min_frame_rate,
+ max_frame_rate,
min_resolution or device:get_field(camera_fields.MIN_RESOLUTION) or camera_fields.video_stream_defaults.min_resolution,
max_resolution or device:get_field(camera_fields.MAX_RESOLUTION) or camera_fields.video_stream_defaults.max_resolution,
camera_fields.video_stream_defaults.min_bitrate,
diff --git a/src/sub_drivers/camera/camera_utils/utils.lua b/src/sub_drivers/camera/camera_utils/utils.lua
index 78792b9..031bb92 100644
--- a/src/sub_drivers/camera/camera_utils/utils.lua
+++ b/src/sub_drivers/camera/camera_utils/utils.lua
@@ -103,6 +103,8 @@ function CameraUtils.compute_fps(max_encoded_pixel_rate, width, height, max_fps)
end
function CameraUtils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps)
+ -- Keep advertised FPS within the same ceiling used by VideoStreamAllocate.
+ max_fps = math.min(max_fps, camera_fields.video_stream_defaults.max_frame_rate)
local resolutions = {}
local added_resolutions = {}
Summary
videoStreamSettings.supportedResolutionsis currently calculated from the camera's reported pixel-rate/FPS limits, but the stream allocator separately capsVideoStreamAllocateat the driver'smax_frame_ratedefault of 60 fps.This can make SmartThings advertise a frame rate that the same driver will never request.
The G350 exposed the mismatch directly: SmartThings advertised 120 fps resolutions while the allocation code was capped at 60 fps.
Environment / verification
Verified against
SmartThingsCommunity/SmartThingsEdgeDriversmainat commit60bbf2716412fdf04fd59f967bdd37be6995d664(2026-08-12).Real-device validation used:
0x115F / 0x30134.5.70/40050701.5.10x0142The proposed changes are generic Matter-camera changes. No Aqara VID/PID fingerprint or proprietary Aqara cluster handling is required.
Current behavior
Supported resolution FPS is calculated using the device-reported
max_fps:camera_utils/utils.lualines 100-132There is no driver-allocation ceiling applied in
build_supported_resolutions().The driver's stream defaults define:
camera_utils/fields.lualines 55-68When a stream is reallocated, the code caps max frame rate at that 60 fps default:
camera_handlers/capability_handlers.lualines 450-487It also uses
cmd.args.resolution.widthand.heightbut does not use an explicitly selectedcmd.args.resolution.fpsto constrain the new allocation.Reproduction
The G350 reported:
SmartThings consequently advertised:
However,
VideoStreamAllocateuses:where the default maximum is 60.
Therefore the UI/API advertises 120 fps while the allocator cannot request more than 60 fps.
Expected behavior
The advertised supported resolution/FPS tuples and the actual allocation path should agree.
Two reasonable implementations are possible.
Option A: conservative advertisement
Clamp advertised FPS to the same driver/platform allocation ceiling:
before building
supportedResolutions.Option B: honor selected FPS
When
cmd.args.resolution.fpsis provided, use it for the allocation, clamped against:For consistency, the test build used both principles:
G350 evidence
Before the fix:
After applying the conservative advertisement logic in the test driver, the SmartThings capability no longer claimed an FPS above the driver's allocation ceiling.
The G350 continued to stream normally.
Affected files
src/sub_drivers/camera/camera_utils/utils.luasrc/sub_drivers/camera/camera_handlers/capability_handlers.luasrc/test/test_matter_camera.luaPotentially:
src/sub_drivers/camera/camera_utils/fields.luaif the allocation ceiling is refactored.Suggested regression coverage
Use a camera fixture with:
Verify:
supportedResolutions[*].fps <= 60;Minimal suggested diff