From 3087d96ea7ee75d45009f08570736a0947dcbe59 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Wed, 25 Feb 2026 11:12:12 -0600 Subject: [PATCH] revert the recent profile components check additions --- .../matter-lock/src/new-matter-lock/init.lua | 33 ++------ .../air_quality_sensor_utils/utils.lua | 24 ++---- .../sub_drivers/air_quality_sensor/init.lua | 3 +- .../SmartThings/matter-switch/src/init.lua | 3 +- .../sub_drivers/camera/camera_utils/utils.lua | 18 +++++ .../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 | 27 ------- .../src/test/test_matter_light_fan.lua | 81 ++----------------- 10 files changed, 45 insertions(+), 149 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 a91790cedb..11aa2b0884 100644 --- a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua +++ b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua @@ -22,6 +22,8 @@ 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", @@ -201,6 +203,7 @@ 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) @@ -238,37 +241,11 @@ local function match_profile_switch(driver, device) device:try_update_metadata({profile = profile_name}) end -local 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 - 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 not profile_changed(device.profile, args.old_st_store.profile) then + if device.profile.id == args.old_st_store.profile.id and not device:get_field(MODULAR_PROFILE_UPDATED) 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 5c1726d6d8..95ca80964c 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,26 +77,17 @@ function AirQualitySensorUtils.set_supported_health_concern_values(device) end end -function AirQualitySensorUtils.profile_changed(latest_profile, previous_profile) - if latest_profile.id ~= previous_profile.id then +function AirQualitySensorUtils.profile_changed(synced_components, prev_components) + if #synced_components ~= #prev_components 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 + 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 - 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 + for _, capability in pairs(component.capabilities or {}) do + if prev_components[component.id][capability.id] == nil then return true end end @@ -104,5 +95,4 @@ function AirQualitySensorUtils.profile_changed(latest_profile, previous_profile) 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 41c6514b40..98b8430c98 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,7 +66,8 @@ function AirQualitySensorLifecycleHandlers.device_init(driver, device) end function AirQualitySensorLifecycleHandlers.info_changed(driver, device, event, args) - if aqs_utils.profile_changed(device.profile, args.old_st_store.profile) then + 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 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 12ee2b3662..cac42e4483 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -64,7 +64,8 @@ function SwitchLifecycleHandlers.driver_switched(driver, device) end function SwitchLifecycleHandlers.info_changed(driver, device, event, args) - if switch_utils.profile_changed(device.profile, args.old_st_store.profile) then + 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.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/camera_utils/utils.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua index 5f3205d73b..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 @@ -134,6 +134,24 @@ 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 = {} 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 c32e131572..f13589ff41 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, args.old_st_store.profile) then + if camera_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 98a300924f..8542972320 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua @@ -238,6 +238,7 @@ 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 ec620eaa65..6eb03b1472 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -147,6 +147,8 @@ 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 16d602cfc1..0592d9a342 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua @@ -325,33 +325,6 @@ function utils.create_multi_press_values_list(size, supportsHeld) return list end -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 - 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 b5ae89fd7a..e1af3ad52b 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,8 +16,7 @@ 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", - {enabled_optional_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}}}), + profile = t_utils.get_profile_definition("fan-modular.yml", {}), manufacturer_info = { vendor_id = 0x0000, product_id = 0x0000, @@ -59,40 +58,6 @@ 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, @@ -145,48 +110,16 @@ 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-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, - 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( - "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(