diff --git a/drivers/SmartThings/matter-switch/src/generic_handlers/attribute_handlers.lua b/drivers/SmartThings/matter-switch/src/generic_handlers/attribute_handlers.lua index ad8a74a00f..73844f2fb7 100644 --- a/drivers/SmartThings/matter-switch/src/generic_handlers/attribute_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/generic_handlers/attribute_handlers.lua @@ -361,8 +361,7 @@ function AttributeHandlers.power_source_attribute_list_handler(driver, device, i profile_name = string.format("%d-", #button_eps) .. profile_name end - if device.manufacturer_info.vendor_id == fields.AQARA_MANUFACTURER_ID and - device.manufacturer_info.product_id == fields.AQARA_CLIMATE_SENSOR_W100_ID then + if switch_utils.get_product_override_field(device, "is_climate_sensor_w100") then profile_name = profile_name .. "-temperature-humidity" end device:try_update_metadata({ profile = profile_name }) diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua index 54eb21dc88..0d11bc8b5e 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua @@ -88,6 +88,18 @@ end test.register_coroutine_test( "Profile should not change for devices with aggregator device type (bridges)", function() + local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_bridge) + for i, cluster in ipairs(cluster_subscribe_list) do + if i > 1 then + subscribe_request:merge(cluster:subscribe(mock_bridge)) + end + end + test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "added" }) + test.socket.matter:__expect_send({mock_bridge.id, subscribe_request}) + test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "init" }) + test.socket.matter:__expect_send({mock_bridge.id, subscribe_request}) + test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "doConfigure" }) + mock_bridge:expect_metadata_update({ provisioning_state = "PROVISIONED" }) end, { test_init = test_init_mock_bridge } ) diff --git a/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua b/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua index 3870b82f9f..b2d066ce61 100644 --- a/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua +++ b/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua @@ -50,20 +50,9 @@ function SwitchDeviceConfiguration.assign_child_profile(device, child_ep) end end - -- Check if device has an overridden child profile that differs from the profile that would match - -- the child's device type for the following two cases: - -- 1. To add Electrical Sensor only to the first EDGE_CHILD (light-power-energy-powerConsumption) - -- for the Aqara Light Switch H2. The profile of the second EDGE_CHILD for this device is - -- determined in the "for" loop above (e.g., light-binary) - -- 2. The selected profile for the child device matches the initial profile defined in - -- child_device_profile_overrides - for id, vendor in pairs(fields.child_device_profile_overrides_per_vendor_id) do - for _, fingerprint in ipairs(vendor) do - if device.manufacturer_info.product_id == fingerprint.product_id and - ((device.manufacturer_info.vendor_id == fields.AQARA_MANUFACTURER_ID and child_ep == 1) or profile == fingerprint.initial_profile) then - return fingerprint.target_profile - end - end + -- vendor override checks + if child_ep == switch_utils.get_product_override_field(device, "ep_id") or profile == switch_utils.get_product_override_field(device, "initial_profile") then + profile = switch_utils.get_product_override_field(device, "target_profile") or profile end -- default to "switch-binary" if no profile is found diff --git a/drivers/SmartThings/matter-switch/src/utils/switch_fields.lua b/drivers/SmartThings/matter-switch/src/utils/switch_fields.lua index 2244eab661..07ad0fdbeb 100644 --- a/drivers/SmartThings/matter-switch/src/utils/switch_fields.lua +++ b/drivers/SmartThings/matter-switch/src/utils/switch_fields.lua @@ -106,19 +106,20 @@ SwitchFields.HUE_SAT_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENT_ SwitchFields.X_Y_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY -SwitchFields.child_device_profile_overrides_per_vendor_id = { +SwitchFields.vendor_overrides = { [0x1321] = { - { product_id = 0x000C, target_profile = "switch-binary", initial_profile = "plug-binary" }, - { product_id = 0x000D, target_profile = "switch-binary", initial_profile = "plug-binary" }, + [0x000C] = { target_profile = "switch-binary", initial_profile = "plug-binary" }, + [0x000D] = { target_profile = "switch-binary", initial_profile = "plug-binary" }, }, - [0x115F] = { - { product_id = 0x1003, target_profile = "light-power-energy-powerConsumption" }, -- 2 Buttons(Generic Switch), 1 Channel(On/Off Light) - { product_id = 0x1004, target_profile = "light-power-energy-powerConsumption" }, -- 2 Buttons(Generic Switch), 2 Channels(On/Off Light) - { product_id = 0x1005, target_profile = "light-power-energy-powerConsumption" }, -- 4 Buttons(Generic Switch), 3 Channels(On/Off Light) - { product_id = 0x1006, target_profile = "light-level-power-energy-powerConsumption" }, -- 3 Buttons(Generic Switch), 1 Channels(Dimmable Light) - { product_id = 0x1008, target_profile = "light-power-energy-powerConsumption" }, -- 2 Buttons(Generic Switch), 1 Channel(On/Off Light) - { product_id = 0x1009, target_profile = "light-power-energy-powerConsumption" }, -- 4 Buttons(Generic Switch), 2 Channels(On/Off Light) - { product_id = 0x100A, target_profile = "light-level-power-energy-powerConsumption" }, -- 1 Buttons(Generic Switch), 1 Channels(Dimmable Light) + [0x115F] = { -- AQARA_MANUFACTURER_ID + [0x1003] = { target_profile = "light-power-energy-powerConsumption", ep_id = 1 }, -- 2 Buttons(Generic Switch), 1 Channel(On/Off Light) + [0x1004] = { target_profile = "light-power-energy-powerConsumption", ep_id = 1 }, -- 2 Buttons(Generic Switch), 2 Channels(On/Off Light) + [0x1005] = { target_profile = "light-power-energy-powerConsumption", ep_id = 1 }, -- 4 Buttons(Generic Switch), 3 Channels(On/Off Light) + [0x1008] = { target_profile = "light-power-energy-powerConsumption", ep_id = 1 }, -- 2 Buttons(Generic Switch), 1 Channel(On/Off Light) + [0x1009] = { target_profile = "light-power-energy-powerConsumption", ep_id = 1 }, -- 4 Buttons(Generic Switch), 2 Channels(On/Off Light) + [0x1006] = { ignore_combo_switch_button = true, target_profile = "light-level-power-energy-powerConsumption", ep_id = 1 }, -- 3 Buttons(Generic Switch), 1 Channels(Dimmable Light) + [0x100A] = { ignore_combo_switch_button = true, target_profile = "light-level-power-energy-powerConsumption", ep_id = 1 }, -- 1 Buttons(Generic Switch), 1 Channels(Dimmable Light) + [0x2004] = { is_climate_sensor_w100 = true }, -- Climate Sensor W100, requires unique profile } } @@ -150,9 +151,6 @@ SwitchFields.TEMP_BOUND_RECEIVED = "__temp_bound_received" SwitchFields.TEMP_MIN = "__temp_min" SwitchFields.TEMP_MAX = "__temp_max" -SwitchFields.AQARA_MANUFACTURER_ID = 0x115F -SwitchFields.AQARA_CLIMATE_SENSOR_W100_ID = 0x2004 - SwitchFields.TRANSITION_TIME = 0 --1/10ths of a second -- When sent with a command, these options mask and override bitmaps cause the command -- to take effect when the switch/light is off. diff --git a/drivers/SmartThings/matter-switch/src/utils/switch_utils.lua b/drivers/SmartThings/matter-switch/src/utils/switch_utils.lua index 86368e9208..2558429654 100644 --- a/drivers/SmartThings/matter-switch/src/utils/switch_utils.lua +++ b/drivers/SmartThings/matter-switch/src/utils/switch_utils.lua @@ -63,6 +63,14 @@ function utils.mired_to_kelvin(value, minOrMax) end end +function utils.get_product_override_field(device, override_key) + if fields.vendor_overrides[device.manufacturer_info.vendor_id] + and fields.vendor_overrides[device.manufacturer_info.vendor_id][device.manufacturer_info.product_id] + then + return fields.vendor_overrides[device.manufacturer_info.vendor_id][device.manufacturer_info.product_id][override_key] + end +end + function utils.check_field_name_updates(device) for _, field in ipairs(fields.updated_fields) do if device:get_field(field.current_field_name) then @@ -78,29 +86,18 @@ end --- whether the device type for an endpoint is currently supported by a profile for --- combination button/switch devices. function utils.device_type_supports_button_switch_combination(device, endpoint_id) - for _, ep in ipairs(device.endpoints) do - if ep.endpoint_id == endpoint_id then - for _, dt in ipairs(ep.device_types) do - if dt.device_type_id == fields.DIMMABLE_LIGHT_DEVICE_TYPE_ID then - for _, fingerprint in ipairs(fields.child_device_profile_overrides_per_vendor_id[0x115F]) do - if device.manufacturer_info.product_id == fingerprint.product_id then - return false -- For Aqara Dimmer Switch with Button. - end - end - return true - end - end - end + if utils.get_product_override_field(device, "ignore_combo_switch_button") then + return false end - return false + local dimmable_eps = utils.get_endpoints_by_device_type(device, fields.DIMMABLE_LIGHT_DEVICE_TYPE_ID) + return utils.tbl_contains(dimmable_eps, endpoint_id) end --- find_default_endpoint is a helper function to handle situations where --- device does not have endpoint ids in sequential order from 1 function utils.find_default_endpoint(device) - if device.manufacturer_info.vendor_id == fields.AQARA_MANUFACTURER_ID and - device.manufacturer_info.product_id == fields.AQARA_CLIMATE_SENSOR_W100_ID then - -- In case of Aqara Climate Sensor W100, in order to sequentially set the button name to button 1, 2, 3 + -- Buttons should not be set on the main component for the Aqara Climate Sensor W100, + if utils.get_product_override_field(device, "is_climate_sensor_w100") then return device.MATTER_DEFAULT_ENDPOINT end @@ -171,6 +168,19 @@ function utils.matter_handler(driver, device, response_block) device.log.info(string.format("Fallback handler for %s", response_block)) end +-- get a list of endpoints for a specified device type. +function utils.get_endpoints_by_device_type(device, device_type_id) + local dt_eps = {} + for _, ep in ipairs(device.endpoints) do + for _, dt in ipairs(ep.device_types) do + if dt.device_type_id == device_type_id then + table.insert(dt_eps, ep.endpoint_id) + end + end + end + return dt_eps +end + --helper function to create list of multi press values function utils.create_multi_press_values_list(size, supportsHeld) local list = {"pushed", "double"} @@ -183,14 +193,7 @@ function utils.create_multi_press_values_list(size, supportsHeld) end function utils.detect_bridge(device) - for _, ep in ipairs(device.endpoints) do - for _, dt in ipairs(ep.device_types) do - if dt.device_type_id == fields.AGGREGATOR_DEVICE_TYPE_ID then - return true - end - end - end - return false + return #utils.get_endpoints_by_device_type(device, fields.AGGREGATOR_DEVICE_TYPE_ID) > 0 end function utils.detect_matter_thing(device)