diff --git a/drivers/SmartThings/matter-switch/profiles/switch-fan.yml b/drivers/SmartThings/matter-switch/profiles/switch-fan.yml new file mode 100644 index 0000000000..533641aead --- /dev/null +++ b/drivers/SmartThings/matter-switch/profiles/switch-fan.yml @@ -0,0 +1,16 @@ +name: switch-fan +components: +- id: main + capabilities: + - id: switch + version: 1 + - id: fanMode + version: 1 + - id: fanSpeedPercent + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: Switch 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 078b99c7fe..16c27350ea 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 @@ -65,6 +65,44 @@ local mock_device = test.mock_device.build_test_matter_device({ } }) +local mock_device_switch = test.mock_device.build_test_matter_device({ + label = "Matter Switch", + profile = t_utils.get_profile_definition("switch-fan.yml"), + manufacturer_info = { + vendor_id = 0x0000, + product_id = 0x0000, + }, + 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_ep1, + clusters = { + {cluster_id = clusters.OnOff.ID, cluster_type = "SERVER"}, + }, + device_types = { + {device_type_id = 0x010A, device_type_revision = 2} -- On Off Plugin Unit + } + }, + { + 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, @@ -83,6 +121,13 @@ local CLUSTER_SUBSCRIBE_LIST ={ clusters.FanControl.attributes.PercentCurrent, } +local SWITCH_CLUSTER_SUBSCRIBE_LIST ={ + clusters.OnOff.attributes.OnOff, + clusters.FanControl.attributes.FanModeSequence, + clusters.FanControl.attributes.FanMode, + clusters.FanControl.attributes.PercentCurrent, +} + local function test_init() test.disable_startup_messages() test.mock_device.add_test_device(mock_device) @@ -104,6 +149,31 @@ end test.set_test_init_function(test_init) +local function test_init_switch() + test.disable_startup_messages() + test.mock_device.add_test_device(mock_device_switch) + local subscribe_request = SWITCH_CLUSTER_SUBSCRIBE_LIST[1]:subscribe(mock_device_switch) + for i, clus in ipairs(SWITCH_CLUSTER_SUBSCRIBE_LIST) do + if i > 1 then subscribe_request:merge(clus:subscribe(mock_device_switch)) end + end + + test.socket.device_lifecycle:__queue_receive({ mock_device_switch.id, "added" }) + test.socket.matter:__expect_send({mock_device_switch.id, subscribe_request}) + + test.socket.device_lifecycle:__queue_receive({ mock_device_switch.id, "init" }) + test.socket.matter:__expect_send({mock_device_switch.id, subscribe_request}) + + test.socket.device_lifecycle:__queue_receive({ mock_device_switch.id, "doConfigure" }) + mock_device_switch:expect_metadata_update({ profile = "switch-fan" }) + mock_device_switch:expect_metadata_update({ provisioning_state = "PROVISIONED" }) +end + +test.register_message_test( + "On Off Plug In Unit + Fan should profile as switch-fan", + {}, + { test_init = test_init_switch } +) + test.register_coroutine_test( "Switch capability should send the appropriate commands", function() test.socket.capability:__queue_receive( diff --git a/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua b/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua index 23b5ca6873..98524a321f 100644 --- a/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua +++ b/drivers/SmartThings/matter-switch/src/utils/device_configuration.lua @@ -177,7 +177,13 @@ function DeviceConfiguration.match_profile(driver, device) if switch_utils.tbl_contains(server_onoff_ep_ids, default_endpoint_id) then updated_profile = SwitchDeviceConfiguration.assign_profile_for_onoff_ep(device, default_endpoint_id) local generic_profile = function(s) return string.find(updated_profile or "", s, 1, true) end - if generic_profile("plug-binary") or generic_profile("plug-level") then + if (generic_profile("plug-binary") or generic_profile("light-binary")) and #device:get_endpoints(clusters.FanControl.ID) > 0 then + updated_profile = "switch-fan" + elseif generic_profile("light-color-level") and #device:get_endpoints(clusters.FanControl.ID) > 0 then + updated_profile = "light-color-level-fan" + elseif generic_profile("light-level") and #device:get_endpoints(clusters.OccupancySensing.ID) > 0 then + updated_profile = "light-level-motion" + elseif generic_profile("plug-binary") or generic_profile("plug-level") then if switch_utils.check_switch_category_vendor_overrides(device) then updated_profile = string.gsub(updated_profile, "plug", "switch") else @@ -186,10 +192,6 @@ function DeviceConfiguration.match_profile(driver, device) if #embedded_cluster_utils.get_endpoints(device, clusters.ElectricalEnergyMeasurement.ID) > 0 then electrical_tags = electrical_tags .. "-energy-powerConsumption" end if electrical_tags ~= "" then updated_profile = string.gsub(updated_profile, "-binary", "") .. electrical_tags end end - elseif generic_profile("light-color-level") and #device:get_endpoints(clusters.FanControl.ID) > 0 then - updated_profile = "light-color-level-fan" - elseif generic_profile("light-level") and #device:get_endpoints(clusters.OccupancySensing.ID) > 0 then - updated_profile = "light-level-motion" elseif generic_profile("light-level-colorTemperature") or generic_profile("light-color-level") then -- ignore attempts to dynamically profile light-level-colorTemperature and light-color-level devices for now, since -- these may lose fingerprinted Kelvin ranges when dynamically profiled.