diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/device_configuration.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/device_configuration.lua index 6626729c78..c84337736d 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/device_configuration.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/device_configuration.lua @@ -121,8 +121,6 @@ function CameraDeviceConfiguration.match_profile(device, status_light_enabled_pr local doorbell_endpoints = switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.DOORBELL) if #doorbell_endpoints > 0 then table.insert(doorbell_component_capabilities, capabilities.button.ID) - CameraDeviceConfiguration.update_doorbell_component_map(device, doorbell_endpoints[1]) - button_cfg.configure_buttons(device, device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH})) end if status_light_enabled_present then table.insert(status_led_component_capabilities, capabilities.switch.ID) @@ -147,6 +145,10 @@ function CameraDeviceConfiguration.match_profile(device, status_light_enabled_pr if camera_utils.optional_capabilities_list_changed(optional_supported_component_capabilities, device.profile.components) then device:try_update_metadata({profile = "camera", optional_component_capabilities = optional_supported_component_capabilities}) + if #doorbell_endpoints > 0 then + CameraDeviceConfiguration.update_doorbell_component_map(device, doorbell_endpoints[1]) + button_cfg.configure_buttons(device, device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH})) + end end end 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 dcdd936950..e83393b0a6 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 @@ -120,28 +120,49 @@ function CameraUtils.profile_changed(synced_components, prev_components) return false end -function CameraUtils.optional_capabilities_list_changed(optional_capabilities, prev_component_list) - local prev_optional_capabilities = {} - for idx, comp in pairs(prev_component_list or {}) do - local cap_list = {} - for _, capability in pairs(comp.capabilities or {}) do - table.insert(cap_list, capability.id) +function CameraUtils.optional_capabilities_list_changed(new_component_capability_list, previous_component_capability_list) + local previous_capability_map = {} + local component_sizes = {} + + local previous_component_count = 0 + for component_name, component in pairs(previous_component_capability_list or {}) do + previous_capability_map[component_name] = {} + component_sizes[component_name] = 0 + for _, capability in pairs(component.capabilities or {}) do + if capability.id ~= "firmwareUpdate" and capability.id ~= "refresh" then + previous_capability_map[component_name][capability.id] = true + component_sizes[component_name] = component_sizes[component_name] + 1 + end end - table.insert(prev_optional_capabilities, {idx, cap_list}) - end - if #optional_capabilities ~= #prev_optional_capabilities then - return true + previous_component_count = previous_component_count + 1 end - for _, capability in pairs(optional_capabilities or {}) do - if not switch_utils.tbl_contains(prev_optional_capabilities, capability) then + + local number_of_components_counted = 0 + for _, new_component_capabilities in pairs(new_component_capability_list or {}) do + local component_name = new_component_capabilities[1] + local capability_list = new_component_capabilities[2] + + number_of_components_counted = number_of_components_counted + 1 + + if previous_capability_map[component_name] == nil then return true end - end - for _, capability in pairs(prev_optional_capabilities or {}) do - if not switch_utils.tbl_contains(optional_capabilities, capability) then + + for _, capability in ipairs(capability_list) do + if previous_capability_map[component_name][capability] == nil then + return true + end + end + + if #capability_list ~= component_sizes[component_name] then return true end end + + if number_of_components_counted ~= previous_component_count then + return true + end + return false end 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 95bef23644..c1d39cf204 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua @@ -5,6 +5,7 @@ local capabilities = require "st.capabilities" local clusters = require "st.matter.clusters" local t_utils = require "integration_test.utils" local test = require "integration_test" +local uint32 = require "st.matter.data_types.Uint32" test.disable_startup_messages() @@ -155,7 +156,6 @@ test.set_test_init_function(test_init) local function update_device_profile() test.socket.matter:__set_channel_ordering("relaxed") - local uint32 = require "st.matter.data_types.Uint32" local expected_metadata = { optional_component_capabilities = { { @@ -214,8 +214,9 @@ local function update_device_profile() uint32(clusters.CameraAvStreamManagement.attributes.StatusLightBrightness.ID) }) }) - test.socket.matter:__expect_send({mock_device.id, clusters.Switch.attributes.MultiPressMax:read(mock_device, DOORBELL_EP)}) mock_device:expect_metadata_update(expected_metadata) + test.socket.matter:__expect_send({mock_device.id, clusters.Switch.attributes.MultiPressMax:read(mock_device, DOORBELL_EP)}) + test.wait_for_events() local updated_device_profile = t_utils.get_profile_definition( "camera.yml", {enabled_optional_capabilities = expected_metadata.optional_component_capabilities} ) @@ -1816,5 +1817,59 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Camera profile should not update for an unchanged Status Light AttributeList report", + function() + update_device_profile() + test.wait_for_events() + test.socket.matter:__queue_receive({ + mock_device.id, + clusters.CameraAvStreamManagement.attributes.AttributeList:build_test_report_data(mock_device, CAMERA_EP, { + uint32(clusters.CameraAvStreamManagement.attributes.StatusLightEnabled.ID), + uint32(clusters.CameraAvStreamManagement.attributes.StatusLightBrightness.ID) + }) + }) + end +) + +test.register_coroutine_test( + "Camera profile should update for a changed Status Light AttributeList report", + function() + update_device_profile() + test.wait_for_events() + test.socket.matter:__queue_receive({ + mock_device.id, + clusters.CameraAvStreamManagement.attributes.AttributeList:build_test_report_data(mock_device, CAMERA_EP, { + uint32(clusters.CameraAvStreamManagement.attributes.StatusLightEnabled.ID) + }) + }) + local expected_metadata = { + optional_component_capabilities = { + { "main", + { "videoCapture2", "cameraViewportSettings", "localMediaStorage", "audioRecording", "cameraPrivacyMode", + "imageControl", "hdr", "nightVision", "mechanicalPanTiltZoom", "videoStreamSettings", "zoneManagement", + "webrtc", "motionSensor", "sounds", } + }, + { "statusLed", + { "switch" } -- only switch capability remains + }, + { "speaker", + { "audioMute", "audioVolume" } + }, + { "microphone", + { "audioMute", "audioVolume" } + }, + { "doorbell", + { "button" } + } + }, + profile = "camera" + } + mock_device:expect_metadata_update(expected_metadata) + test.socket.matter:__expect_send({mock_device.id, clusters.Switch.attributes.MultiPressMax:read(mock_device, DOORBELL_EP)}) + test.socket.capability:__expect_send(mock_device:generate_test_message("doorbell", capabilities.button.button.pushed({state_change = false}))) + end +) + -- run the tests test.run_registered_tests()