diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_handlers/attribute_handlers.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_handlers/attribute_handlers.lua index 484e4c7a15..df99d50f94 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_handlers/attribute_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_handlers/attribute_handlers.lua @@ -113,9 +113,6 @@ end function CameraAttributeHandlers.rate_distortion_trade_off_points_handler(driver, device, ib, response) if not ib.data.elements then return end local resolutions = {} - local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE) - local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND) - local emit_capability = max_encoded_pixel_rate ~= nil and max_fps ~= nil for _, v in ipairs(ib.data.elements) do local rate_distortion_trade_off_points = v.elements local width = rate_distortion_trade_off_points.resolution.elements.width.value @@ -124,77 +121,63 @@ function CameraAttributeHandlers.rate_distortion_trade_off_points_handler(driver width = width, height = height }) - if emit_capability then - local fps = camera_utils.compute_fps(max_encoded_pixel_rate, width, height, max_fps) - if fps > 0 then - resolutions[#resolutions].fps = fps - end - end - end - if emit_capability then - device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(resolutions)) end device:set_field(camera_fields.SUPPORTED_RESOLUTIONS, resolutions) + local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE) + local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND) + if max_encoded_pixel_rate and max_fps and device:get_field(camera_fields.MAX_RESOLUTION) and device:get_field(camera_fields.MIN_RESOLUTION) then + local supported_resolutions = camera_utils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps) + device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions)) + end end function CameraAttributeHandlers.max_encoded_pixel_rate_handler(driver, device, ib, response) - local resolutions = device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) + device:set_field(camera_fields.MAX_ENCODED_PIXEL_RATE, ib.data.value) local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND) - local emit_capability = resolutions ~= nil and max_fps ~= nil - if emit_capability then - for _, v in pairs(resolutions or {}) do - local fps = camera_utils.compute_fps(ib.data.value, v.width, v.height, max_fps) - if fps > 0 then - v.fps = fps - end - end - device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(resolutions)) + if max_fps and device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) and device:get_field(camera_fields.MAX_RESOLUTION) and device:get_field(camera_fields.MIN_RESOLUTION) then + local supported_resolutions = camera_utils.build_supported_resolutions(device, ib.data.value, max_fps) + device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions)) end - device:set_field(camera_fields.MAX_ENCODED_PIXEL_RATE, ib.data.value) end function CameraAttributeHandlers.video_sensor_parameters_handler(driver, device, ib, response) if not ib.data.elements then return end - local resolutions = device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) + local sensor_width = ib.data.elements.sensor_width.value + local sensor_height = ib.data.elements.sensor_height.value + local max_fps = ib.data.elements.max_fps.value + device:set_field(camera_fields.MAX_RESOLUTION, { + width = sensor_width, + height = sensor_height + }) + device:set_field(camera_fields.MAX_FRAMES_PER_SECOND, max_fps) + device:emit_event_for_endpoint(ib, capabilities.cameraViewportSettings.videoSensorParameters({ + width = sensor_width, + height = sensor_height, + maxFPS = max_fps + })) local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE) - local emit_capability = resolutions ~= nil and max_encoded_pixel_rate ~= nil - local sensor_width, sensor_height, max_fps - for _, v in pairs(ib.data.elements) do - if v.field_id == 0 then - sensor_width = v.value - elseif v.field_id == 1 then - sensor_height = v.value - elseif v.field_id == 2 then - max_fps = v.value - end - end - - if max_fps then - if sensor_width and sensor_height then - device:emit_event_for_endpoint(ib, capabilities.cameraViewportSettings.videoSensorParameters({ - width = sensor_width, - height = sensor_height, - maxFPS = max_fps - })) - end - if emit_capability then - for _, v in pairs(resolutions or {}) do - local fps = camera_utils.compute_fps(max_encoded_pixel_rate, v.width, v.height, max_fps) - if fps > 0 then - v.fps = fps - end - end - device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(resolutions)) - end - device:set_field(camera_fields.MAX_FRAMES_PER_SECOND, max_fps) + if max_encoded_pixel_rate and max_fps and device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) and device:get_field(camera_fields.MIN_RESOLUTION) then + local supported_resolutions = camera_utils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps) + device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions)) end end function CameraAttributeHandlers.min_viewport_handler(driver, device, ib, response) + if not ib.data.elements then return end device:emit_event_for_endpoint(ib, capabilities.cameraViewportSettings.minViewportResolution({ width = ib.data.elements.width.value, height = ib.data.elements.height.value })) + device:set_field(camera_fields.MIN_RESOLUTION, { + width = ib.data.elements.width.value, + height = ib.data.elements.height.value + }) + local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE) + local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND) + if max_encoded_pixel_rate and max_fps and device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) and device:get_field(camera_fields.MAX_RESOLUTION) then + local supported_resolutions = camera_utils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps) + device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions)) + end end function CameraAttributeHandlers.allocated_video_streams_handler(driver, device, ib, response) diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/fields.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/fields.lua index 677e2d5dd6..7598b89893 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/fields.lua @@ -10,6 +10,8 @@ CameraFields.MAX_FRAMES_PER_SECOND = "__max_frames_per_second" CameraFields.MAX_VOLUME_LEVEL = "__max_volume_level" CameraFields.MIN_VOLUME_LEVEL = "__min_volume_level" CameraFields.SUPPORTED_RESOLUTIONS = "__supported_resolutions" +CameraFields.MAX_RESOLUTION = "__max_resolution" +CameraFields.MIN_RESOLUTION = "__min_resolution" CameraFields.TRIGGERED_ZONES = "__triggered_zones" CameraFields.VIEWPORT = "__viewport" diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua index a93e757c16..1caa9737bb 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua @@ -102,6 +102,38 @@ function CameraUtils.compute_fps(max_encoded_pixel_rate, width, height, max_fps) return math.tointeger(math.floor(fps / fps_step) * fps_step) end +function CameraUtils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps) + local resolutions = {} + local added_resolutions = {} + + local function add_resolution(width, height) + local key = width .. "x" .. height + if not added_resolutions[key] then + local resolution = { width = width, height = height } + resolution.fps = CameraUtils.compute_fps(max_encoded_pixel_rate, width, height, max_fps) + table.insert(resolutions, resolution) + added_resolutions[key] = true + end + end + + local min_resolution = device:get_field(camera_fields.MIN_RESOLUTION) + if min_resolution then + add_resolution(min_resolution.width, min_resolution.height) + end + + local trade_off_resolutions = device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) + for _, v in pairs(trade_off_resolutions or {}) do + add_resolution(v.width, v.height) + end + + local max_resolution = device:get_field(camera_fields.MAX_RESOLUTION) + if max_resolution then + add_resolution(max_resolution.width, max_resolution.height) + end + + return resolutions +end + function CameraUtils.profile_changed(synced_components, prev_components) if #synced_components ~= #prev_components then return true diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua index 1028197590..b15034779d 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua @@ -683,6 +683,18 @@ local function receive_max_encoded_pixel_rate() }) end +local function receive_min_viewport() + test.socket.matter:__queue_receive({ + mock_device.id, + clusters.CameraAvStreamManagement.attributes.MinViewportResolution:build_test_report_data( + mock_device, CAMERA_EP, clusters.CameraAvStreamManagement.types.VideoResolutionStruct({ + width = 1920, + height = 1080 + }) + ) + }) +end + local function receive_video_sensor_params() test.socket.matter:__queue_receive({ mock_device.id, @@ -697,6 +709,15 @@ local function receive_video_sensor_params() }) end +local function emit_min_viewport() + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.cameraViewportSettings.minViewportResolution({ + width = 1920, + height = 1080, + })) + ) +end + local function emit_video_sensor_parameters() test.socket.capability:__expect_send( mock_device:generate_test_message("main", capabilities.cameraViewportSettings.videoSensorParameters({ @@ -719,6 +740,11 @@ local function emit_supported_resolutions() width = 3840, height = 2160, fps = 15 + }, + { + width = 7360, + height = 4912, + fps = 0 } })) ) @@ -730,12 +756,14 @@ end -- videoStreamSettings.supportedResolutions is emitted after all three attributes are received. test.register_coroutine_test( - "Rate Distortion Trade Off Points, MaxEncodedPixelRate, VideoSensorParams reports should generate appropriate events", + "Rate Distortion Trade Off Points, MaxEncodedPixelRate, MinViewport, VideoSensorParams reports should generate appropriate events", function() update_device_profile() test.wait_for_events() receive_rate_distortion_trade_off_points() receive_max_encoded_pixel_rate() + receive_min_viewport() + emit_min_viewport() receive_video_sensor_params() emit_video_sensor_parameters() emit_supported_resolutions() @@ -743,11 +771,13 @@ test.register_coroutine_test( ) test.register_coroutine_test( - "Rate Distortion Trade Off Points, VideoSensorParams, MaxEncodedPixelRate reports should generate appropriate events", + "Rate Distortion Trade Off Points, MinViewport, VideoSensorParams, MaxEncodedPixelRate reports should generate appropriate events", function() update_device_profile() test.wait_for_events() receive_rate_distortion_trade_off_points() + receive_min_viewport() + emit_min_viewport() receive_video_sensor_params() emit_video_sensor_parameters() receive_max_encoded_pixel_rate() @@ -756,11 +786,13 @@ test.register_coroutine_test( ) test.register_coroutine_test( - "MaxEncodedPixelRate, VideoSensorParams, Rate Distortion Trade Off Points reports should generate appropriate events", + "MaxEncodedPixelRate, MinViewport, VideoSensorParams, Rate Distortion Trade Off Points reports should generate appropriate events", function() update_device_profile() test.wait_for_events() receive_max_encoded_pixel_rate() + receive_min_viewport() + emit_min_viewport() receive_video_sensor_params() emit_video_sensor_parameters() receive_rate_distortion_trade_off_points()