From 53aee16daac0bb9eea33e2676284cf3bc5307f4e Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Thu, 12 Feb 2026 16:16:39 -0600 Subject: [PATCH 1/5] Add component changed comparisons for modular profile infoChanged events --- .../matter-lock/src/new-matter-lock/init.lua | 30 +++++++-- .../air_quality_sensor_utils/utils.lua | 22 ++++--- .../SmartThings/matter-switch/src/init.lua | 4 +- .../src/sub_drivers/camera/init.lua | 2 +- .../src/switch_utils/device_configuration.lua | 1 - .../matter-switch/src/switch_utils/fields.lua | 2 - .../matter-switch/src/switch_utils/utils.lua | 24 +++++++ .../src/test/test_matter_light_fan.lua | 62 ++++++++++++++++--- 8 files changed, 121 insertions(+), 26 deletions(-) diff --git a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua index 11aa2b0884..6893a1f90d 100644 --- a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua +++ b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua @@ -22,8 +22,6 @@ local MIN_EPOCH_S = 0 local MAX_EPOCH_S = 0xffffffff local THIRTY_YEARS_S = 946684800 -- 1970-01-01T00:00:00 ~ 2000-01-01T00:00:00 -local MODULAR_PROFILE_UPDATED = "__MODULAR_PROFILE_UPDATED" - local RESPONSE_STATUS_MAP = { [DoorLock.types.DlStatus.SUCCESS] = "success", [DoorLock.types.DlStatus.FAILURE] = "failure", @@ -203,7 +201,6 @@ local function match_profile_modular(driver, device) table.insert(enabled_optional_component_capability_pairs, {"main", main_component_capabilities}) device:try_update_metadata({profile = modular_profile_name, optional_component_capabilities = enabled_optional_component_capability_pairs}) - device:set_field(MODULAR_PROFILE_UPDATED, true) end local function match_profile_switch(driver, device) @@ -241,11 +238,34 @@ local function match_profile_switch(driver, device) device:try_update_metadata({profile = profile_name}) end +local function profile_changed(synced_components, prev_components) + for component_id, synced_component in pairs(synced_components or {}) do + local prev_component = prev_components[component_id] + if prev_component == nil then + return true + end + if #synced_component.capabilities ~= #prev_component.capabilities then + return true + end + -- Build a table of capability IDs from the previous component. Then, use this map to check + -- that all capabilities in the synced component existed in the previous component. + local prev_cap_ids = {} + for _, capability in ipairs(prev_component.capabilities or {}) do + prev_cap_ids[capability.id] = true + end + for _, capability in ipairs(synced_component.capabilities or {}) do + if not prev_cap_ids[capability.id] then + return true + end + end + end + return false +end + local function info_changed(driver, device, event, args) - if device.profile.id == args.old_st_store.profile.id and not device:get_field(MODULAR_PROFILE_UPDATED) then + if device.profile.id == args.old_st_store.profile.id and not profile_changed(device.profile.components, args.old_st_store.profile.components) then return end - device:set_field(MODULAR_PROFILE_UPDATED, nil) for cap_id, attributes in pairs(subscribed_attributes) do if device:supports_capability_by_id(cap_id) then for _, attr in ipairs(attributes) do diff --git a/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua b/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua index 95ca80964c..ab9a1a737c 100644 --- a/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua +++ b/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua @@ -78,16 +78,22 @@ function AirQualitySensorUtils.set_supported_health_concern_values(device) end function AirQualitySensorUtils.profile_changed(synced_components, prev_components) - if #synced_components ~= #prev_components then - return true - end - for _, component in pairs(synced_components or {}) do - if (prev_components[component.id] == nil) or - (#component.capabilities ~= #prev_components[component.id].capabilities) then + for component_id, synced_component in pairs(synced_components or {}) do + local prev_component = prev_components[component_id] + if prev_component == nil then + return true + end + if #synced_component.capabilities ~= #prev_component.capabilities then return true end - for _, capability in pairs(component.capabilities or {}) do - if prev_components[component.id][capability.id] == nil then + -- Build a table of capability IDs from the previous component. Then, use this map to check + -- that all capabilities in the synced component existed in the previous component. + local prev_cap_ids = {} + for _, capability in ipairs(prev_component.capabilities or {}) do + prev_cap_ids[capability.id] = true + end + for _, capability in ipairs(synced_component.capabilities or {}) do + if not prev_cap_ids[capability.id] then return true end end diff --git a/drivers/SmartThings/matter-switch/src/init.lua b/drivers/SmartThings/matter-switch/src/init.lua index cac42e4483..55d63eca06 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -64,8 +64,8 @@ function SwitchLifecycleHandlers.driver_switched(driver, device) end function SwitchLifecycleHandlers.info_changed(driver, device, event, args) - if device.profile.id ~= args.old_st_store.profile.id or device:get_field(fields.MODULAR_PROFILE_UPDATED) then - device:set_field(fields.MODULAR_PROFILE_UPDATED, nil) + if device.profile.id ~= args.old_st_store.profile.id or + switch_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then if device.network_type == device_lib.NETWORK_TYPE_MATTER then device:subscribe() button_cfg.configure_buttons(device, diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua index f13589ff41..dd588ceaca 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua @@ -47,7 +47,7 @@ function CameraLifecycleHandlers.driver_switched(driver, device) end function CameraLifecycleHandlers.info_changed(driver, device, event, args) - if camera_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then + if switch_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then camera_cfg.initialize_camera_capabilities(device) device:subscribe() if #switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.DOORBELL) > 0 then diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua b/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua index 8542972320..98a300924f 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua @@ -238,7 +238,6 @@ function DeviceConfiguration.match_profile(driver, device) local fan_device_type_ep_ids = switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.FAN) if #fan_device_type_ep_ids > 0 then updated_profile, optional_component_capabilities = FanDeviceConfiguration.assign_profile_for_fan_ep(device, default_endpoint_id) - device:set_field(fields.MODULAR_PROFILE_UPDATED, true) end -- initialize the main device card with buttons if applicable diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua index 6eb03b1472..ec620eaa65 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -147,8 +147,6 @@ SwitchFields.ELECTRICAL_SENSOR_EPS = "__electrical_sensor_eps" --- for an Electrical Sensor EP with a "primary" endpoint, used during device profiling. SwitchFields.ELECTRICAL_TAGS = "__electrical_tags" -SwitchFields.MODULAR_PROFILE_UPDATED = "__modular_profile_updated" - SwitchFields.profiling_data = { POWER_TOPOLOGY = "__power_topology", BATTERY_SUPPORT = "__battery_support", diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua index 0592d9a342..11b5481262 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua @@ -325,6 +325,30 @@ function utils.create_multi_press_values_list(size, supportsHeld) return list end +function utils.profile_changed(synced_components, prev_components) + for component_id, synced_component in pairs(synced_components or {}) do + local prev_component = prev_components[component_id] + if prev_component == nil then + return true + end + if #synced_component.capabilities ~= #prev_component.capabilities then + return true + end + -- Build a table of capability IDs from the previous component. Then, use this map to check + -- that all capabilities in the synced component existed in the previous component. + local prev_cap_ids = {} + for _, capability in ipairs(prev_component.capabilities or {}) do + prev_cap_ids[capability.id] = true + end + for _, capability in ipairs(synced_component.capabilities or {}) do + if not prev_cap_ids[capability.id] then + return true + end + end + end + return false +end + function utils.detect_bridge(device) return #utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.AGGREGATOR) > 0 end diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua index e1af3ad52b..2da9b2afbc 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua @@ -16,7 +16,8 @@ local mock_device_ep2 = 2 local mock_device = test.mock_device.build_test_matter_device({ label = "Matter Fan Light", - profile = t_utils.get_profile_definition("fan-modular.yml", {}), + profile = t_utils.get_profile_definition("fan-modular.yml", + {enabled_optional_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}}}), manufacturer_info = { vendor_id = 0x0000, product_id = 0x0000, @@ -58,6 +59,40 @@ local mock_device = test.mock_device.build_test_matter_device({ } }) +local mock_device_capabilities_disabled = test.mock_device.build_test_matter_device({ + label = "Matter Fan Light", + profile = t_utils.get_profile_definition("fan-modular.yml", + {enabled_optional_capabilities = {{"main", {}}}}), + manufacturer_info = { + vendor_id = 0x0000, + product_id = 0x0000, + }, + matter_version = { + software = 1, + hardware = 1, + }, + endpoints = { + { + endpoint_id = 0, + clusters = { + {cluster_id = clusters.Basic.ID, cluster_type = "SERVER"}, + }, + device_types = { + {device_type_id = 0x0016, device_type_revision = 1} -- RootNode + } + }, + { + endpoint_id = mock_device_ep2, + clusters = { + {cluster_id = clusters.FanControl.ID, cluster_type = "SERVER", feature_map = 15}, + }, + device_types = { + {device_type_id = 0x002B, device_type_revision = 1,} -- Fan + } + } + } +}) + local CLUSTER_SUBSCRIBE_LIST ={ clusters.OnOff.attributes.OnOff, clusters.LevelControl.attributes.CurrentLevel, @@ -110,16 +145,29 @@ local function test_init() }) mock_device:expect_metadata_update({ profile = "fan-modular", optional_component_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}} }) mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) - - local updated_device_profile = t_utils.get_profile_definition("fan-modular.yml", - {enabled_optional_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}}} - ) - test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ profile = updated_device_profile })) - test.socket.matter:__expect_send({mock_device.id, subscribe_request}) end test.set_test_init_function(test_init) +test.register_coroutine_test( + "Component-capabilitity update without Profile ID update should cause re-subscribe in infoChanged handler", function() + local cluster_subscribe_list ={ + clusters.FanControl.attributes.FanModeSequence, + clusters.FanControl.attributes.FanMode, + clusters.FanControl.attributes.PercentCurrent, + } + local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_capabilities_disabled) + for i, clus in ipairs(cluster_subscribe_list) do + if i > 1 then subscribe_request:merge(clus:subscribe(mock_device_capabilities_disabled)) end + end + test.socket.device_lifecycle:__queue_receive(mock_device_capabilities_disabled:generate_info_changed( + {profile = {id = "00000000-1111-2222-3333-000000000004", components = { main = {capabilities={{id="fanSpeedPercent", version=1}, {id="fanMode", version=1}, {id="firmwareUpdate", version=1}, {id="refresh", version=1}}}}}}) + ) + test.socket.matter:__expect_send({mock_device_capabilities_disabled.id, subscribe_request}) + end, + { test_init = function() test.mock_device.add_test_device(mock_device_capabilities_disabled) end } +) + test.register_coroutine_test( "Switch capability should send the appropriate commands", function() test.socket.capability:__queue_receive( From 364092e4c6ef0589943c08af895e979793b00004 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Fri, 13 Feb 2026 10:34:43 -0600 Subject: [PATCH 2/5] remove unused function --- .../sub_drivers/camera/camera_utils/utils.lua | 18 ------------------ 1 file changed, 18 deletions(-) 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 1caa9737bb..5f3205d73b 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 @@ -134,24 +134,6 @@ function CameraUtils.build_supported_resolutions(device, max_encoded_pixel_rate, return resolutions end -function CameraUtils.profile_changed(synced_components, prev_components) - if #synced_components ~= #prev_components then - return true - end - for _, component in pairs(synced_components or {}) do - if (prev_components[component.id] == nil) or - (#component.capabilities ~= #prev_components[component.id].capabilities) then - return true - end - for _, capability in pairs(component.capabilities or {}) do - if prev_components[component.id][capability.id] == nil then - return true - end - end - end - return false -end - function CameraUtils.optional_capabilities_list_changed(new_component_capability_list, previous_component_capability_list) local previous_capability_map = {} local component_sizes = {} From 5b23d2a99d9673b7d0fcd4253adaea6ba6f0575c Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Fri, 13 Feb 2026 10:42:14 -0600 Subject: [PATCH 3/5] simplify/clarify profile changed function --- .../matter-lock/src/new-matter-lock/init.lua | 11 +++++++---- .../air_quality_sensor_utils/utils.lua | 10 +++++++--- .../src/sub_drivers/air_quality_sensor/init.lua | 3 +-- drivers/SmartThings/matter-switch/src/init.lua | 3 +-- .../matter-switch/src/sub_drivers/camera/init.lua | 2 +- .../matter-switch/src/switch_utils/utils.lua | 9 ++++++--- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua index 6893a1f90d..34bb443e2a 100644 --- a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua +++ b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua @@ -238,9 +238,12 @@ local function match_profile_switch(driver, device) device:try_update_metadata({profile = profile_name}) end -local function profile_changed(synced_components, prev_components) - for component_id, synced_component in pairs(synced_components or {}) do - local prev_component = prev_components[component_id] +function profile_changed(latest_profile, previous_profile) + if latest_profile.id ~= previous_profile.id then + return true + end + for component_id, synced_component in pairs(latest_profile.components or {}) do + local prev_component = previous_profile.components[component_id] if prev_component == nil then return true end @@ -263,7 +266,7 @@ local function profile_changed(synced_components, prev_components) end local function info_changed(driver, device, event, args) - if device.profile.id == args.old_st_store.profile.id and not profile_changed(device.profile.components, args.old_st_store.profile.components) then + if not profile_changed(device.profile, args.old_st_store.profile) then return end for cap_id, attributes in pairs(subscribed_attributes) do diff --git a/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua b/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua index ab9a1a737c..5c1726d6d8 100644 --- a/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua +++ b/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/air_quality_sensor_utils/utils.lua @@ -77,9 +77,12 @@ function AirQualitySensorUtils.set_supported_health_concern_values(device) end end -function AirQualitySensorUtils.profile_changed(synced_components, prev_components) - for component_id, synced_component in pairs(synced_components or {}) do - local prev_component = prev_components[component_id] +function AirQualitySensorUtils.profile_changed(latest_profile, previous_profile) + if latest_profile.id ~= previous_profile.id then + return true + end + for component_id, synced_component in pairs(latest_profile.components or {}) do + local prev_component = previous_profile.components[component_id] if prev_component == nil then return true end @@ -101,4 +104,5 @@ function AirQualitySensorUtils.profile_changed(synced_components, prev_component return false end + return AirQualitySensorUtils diff --git a/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/init.lua b/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/init.lua index 98b8430c98..41c6514b40 100644 --- a/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/init.lua +++ b/drivers/SmartThings/matter-sensor/src/sub_drivers/air_quality_sensor/init.lua @@ -66,8 +66,7 @@ function AirQualitySensorLifecycleHandlers.device_init(driver, device) end function AirQualitySensorLifecycleHandlers.info_changed(driver, device, event, args) - if device.profile.id ~= args.old_st_store.profile.id or - aqs_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then + if aqs_utils.profile_changed(device.profile, args.old_st_store.profile) then if device:get_field(fields.SUPPORTED_COMPONENT_CAPABILITIES) then --re-up subscription with new capabilities using the modular supports_capability override device:extend_device("supports_capability_by_id", aqs_utils.supports_capability_by_id_modular) diff --git a/drivers/SmartThings/matter-switch/src/init.lua b/drivers/SmartThings/matter-switch/src/init.lua index 55d63eca06..12ee2b3662 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -64,8 +64,7 @@ function SwitchLifecycleHandlers.driver_switched(driver, device) end function SwitchLifecycleHandlers.info_changed(driver, device, event, args) - if device.profile.id ~= args.old_st_store.profile.id or - switch_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then + if switch_utils.profile_changed(device.profile, args.old_st_store.profile) then if device.network_type == device_lib.NETWORK_TYPE_MATTER then device:subscribe() button_cfg.configure_buttons(device, diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua index dd588ceaca..c32e131572 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua @@ -47,7 +47,7 @@ function CameraLifecycleHandlers.driver_switched(driver, device) end function CameraLifecycleHandlers.info_changed(driver, device, event, args) - if switch_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then + if switch_utils.profile_changed(device.profile, args.old_st_store.profile) then camera_cfg.initialize_camera_capabilities(device) device:subscribe() if #switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.DOORBELL) > 0 then diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua index 11b5481262..16d602cfc1 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua @@ -325,9 +325,12 @@ function utils.create_multi_press_values_list(size, supportsHeld) return list end -function utils.profile_changed(synced_components, prev_components) - for component_id, synced_component in pairs(synced_components or {}) do - local prev_component = prev_components[component_id] +function utils.profile_changed(latest_profile, previous_profile) + if latest_profile.id ~= previous_profile.id then + return true + end + for component_id, synced_component in pairs(latest_profile.components or {}) do + local prev_component = previous_profile.components[component_id] if prev_component == nil then return true end From 757c08787456cecd8028d62a95fb841d33a6c119 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Fri, 13 Feb 2026 10:44:17 -0600 Subject: [PATCH 4/5] fix luacheck errors --- drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua index 34bb443e2a..a91790cedb 100644 --- a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua +++ b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua @@ -238,7 +238,7 @@ local function match_profile_switch(driver, device) device:try_update_metadata({profile = profile_name}) end -function profile_changed(latest_profile, previous_profile) +local function profile_changed(latest_profile, previous_profile) if latest_profile.id ~= previous_profile.id then return true end From ff0ca1fed439fe8905a61b3a9c06e5165fcae626 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Fri, 13 Feb 2026 10:54:37 -0600 Subject: [PATCH 5/5] add unit test to ensure no-update behavior --- .../src/test/test_matter_light_fan.lua | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua index 2da9b2afbc..b5ae89fd7a 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua @@ -150,7 +150,7 @@ end test.set_test_init_function(test_init) test.register_coroutine_test( - "Component-capabilitity update without Profile ID update should cause re-subscribe in infoChanged handler", function() + "Component-capability update without profile ID update should cause re-subscribe in infoChanged handler", function() local cluster_subscribe_list ={ clusters.FanControl.attributes.FanModeSequence, clusters.FanControl.attributes.FanMode, @@ -168,6 +168,25 @@ test.register_coroutine_test( { test_init = function() test.mock_device.add_test_device(mock_device_capabilities_disabled) end } ) +test.register_coroutine_test( + "No component-capability update an no profile ID update should not cause a re-subscribe in infoChanged handler", function() + local cluster_subscribe_list ={ + clusters.FanControl.attributes.FanModeSequence, + clusters.FanControl.attributes.FanMode, + clusters.FanControl.attributes.PercentCurrent, + } + local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_capabilities_disabled) + for i, clus in ipairs(cluster_subscribe_list) do + if i > 1 then subscribe_request:merge(clus:subscribe(mock_device_capabilities_disabled)) end + end + test.socket.device_lifecycle:__queue_receive(mock_device_capabilities_disabled:generate_info_changed( + {profile = {id = "00000000-1111-2222-3333-000000000004", components = { main = {capabilities={{id="firmwareUpdate", version=1}, {id="refresh", version=1}}}}}}) + ) + end, + { test_init = function() test.mock_device.add_test_device(mock_device_capabilities_disabled) end } +) + + test.register_coroutine_test( "Switch capability should send the appropriate commands", function() test.socket.capability:__queue_receive(