Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/SmartThings/matter-switch/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ end

function SwitchLifecycleHandlers.do_configure(driver, device)
if device.network_type == device_lib.NETWORK_TYPE_MATTER and not switch_utils.detect_bridge(device) then
switch_cfg.set_device_control_options(device)
device_cfg.match_profile(driver, device)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ function SwitchDeviceConfiguration.create_child_devices(driver, device, server_o
device:set_find_child(switch_utils.find_child)
end

-- Per the spec, these attributes are "meant to be changed only during commissioning."
function SwitchDeviceConfiguration.set_device_control_options(device)
for _, ep_info in ipairs(device.endpoints) do
-- before the Matter 1.3 lua libs update (HUB FW 54), OptionsBitmap was defined as LevelControlOptions
if switch_utils.ep_supports_cluster(ep_info, clusters.LevelControl.ID) then
device:send(clusters.LevelControl.attributes.Options:write(device, ep_info.endpoint_id, clusters.LevelControl.types.LevelControlOptions.EXECUTE_IF_OFF))
end
-- before the Matter 1.4 lua libs update (HUB FW 56), there was no OptionsBitmap type defined
if switch_utils.ep_supports_cluster(ep_info, clusters.ColorControl.ID) then
local excute_if_off_bit = clusters.ColorControl.types.OptionsBitmap and clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF or 0x0001
device:send(clusters.ColorControl.attributes.Options:write(device, ep_info.endpoint_id, excute_if_off_bit))
end
end
end

function ButtonDeviceConfiguration.update_button_profile(device, default_endpoint_id, num_button_eps)
local profile_name = string.gsub(num_button_eps .. "-button", "1%-", "") -- remove the "1-" in a device with 1 button ep
if switch_utils.device_type_supports_button_switch_combination(device, default_endpoint_id) then
Expand Down
16 changes: 16 additions & 0 deletions drivers/SmartThings/matter-switch/src/switch_utils/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,22 @@ function utils.get_endpoint_info(device, endpoint_id)
return {}
end

function utils.ep_supports_cluster(ep_info, cluster_id, opts)
opts = opts or {}
local clus_has_features = function(cluster, checked_feature)
return (cluster.feature_map & checked_feature) == checked_feature
end
for _, cluster in ipairs(ep_info.clusters) do
if ((cluster.cluster_id == cluster_id)
and (opts.feature_bitmap == nil or clus_has_features(cluster, opts.feature_bitmap))
and ((opts.cluster_type == nil and cluster.cluster_type == "SERVER" or cluster.cluster_type == "BOTH")
or (opts.cluster_type == cluster.cluster_type))
or (cluster_id == nil)) then
return true
end
end
end

-- Fallback handler for responses that dont have their own handler
function utils.matter_handler(driver, device, response_block)
device.log.info(string.format("Fallback handler for %s", response_block))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ test.register_coroutine_test(
"Test profile change on init for Electrical Sensor device type",
function()
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
test.socket.matter:__expect_send({mock_device.id, clusters.LevelControl.attributes.Options:write(mock_device, 2, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
mock_device:expect_metadata_update({ profile = "plug-level-power-energy-powerConsumption" })
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ local function test_init()
test.socket.matter:__expect_send({mock_device.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
test.socket.matter:__expect_send({mock_device.id, clusters.LevelControl.attributes.Options:write(mock_device, mock_device_ep1, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.matter:__expect_send({mock_device.id, clusters.ColorControl.attributes.Options:write(mock_device, mock_device_ep1, clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
mock_device:expect_metadata_update({ profile = "light-color-level-fan" })
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ local function test_init()
parent_assigned_child_key = string.format("%d", mock_device_ep5)
})
expect_configure_buttons()
test.socket.matter:__expect_send({mock_device.id, clusters.LevelControl.attributes.Options:write(mock_device, mock_device_ep1, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.matter:__expect_send({mock_device.id, clusters.LevelControl.attributes.Options:write(mock_device, mock_device_ep5, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.matter:__expect_send({mock_device.id, clusters.ColorControl.attributes.Options:write(mock_device, mock_device_ep5, clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })

-- simulate the profile change update taking affect and the device info changing
Expand Down
21 changes: 19 additions & 2 deletions drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,24 @@ local function test_init_color_temp()
subscribe_request:merge(cluster:subscribe(mock_device_color_temp))
end
end
test.socket.matter:__expect_send({mock_device_color_temp.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_color_temp.id, "added" })
test.socket.matter:__expect_send({mock_device_color_temp.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_color_temp.id, "init" })
test.socket.matter:__expect_send({mock_device_color_temp.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_color_temp.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_color_temp.id,
clusters.LevelControl.attributes.Options:write(mock_device_color_temp, 1, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
test.socket.matter:__expect_send({
mock_device_color_temp.id,
clusters.ColorControl.attributes.Options:write(mock_device_color_temp, 1, clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_color_temp:expect_metadata_update({ provisioning_state = "PROVISIONED" })
test.socket.matter:__expect_send({mock_device_color_temp.id, subscribe_request})
end

local function test_init_extended_color()
Expand All @@ -221,13 +230,21 @@ local function test_init_extended_color()
end
test.socket.matter:__expect_send({mock_device_extended_color.id, subscribe_request})
test.socket.device_lifecycle:__queue_receive({ mock_device_extended_color.id, "added" })
test.socket.matter:__expect_send({mock_device_extended_color.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_extended_color.id, "init" })
test.socket.matter:__expect_send({mock_device_extended_color.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_extended_color.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_extended_color.id,
clusters.LevelControl.attributes.Options:write(mock_device_extended_color, 1, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
test.socket.matter:__expect_send({
mock_device_extended_color.id,
clusters.ColorControl.attributes.Options:write(mock_device_extended_color, 1, clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_extended_color:expect_metadata_update({ provisioning_state = "PROVISIONED" })
test.socket.matter:__expect_send({mock_device_extended_color.id, subscribe_request})
end

test.register_message_test(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ local mock_device_mounted_on_off_control = test.mock_device.build_test_matter_de
endpoint_id = 7,
clusters = {
{cluster_id = clusters.OnOff.ID, cluster_type = "SERVER", cluster_revision = 1, feature_map = 0},
{cluster_id = clusters.LevelControl.ID, cluster_type = "CLIENT", feature_map = 2},
{cluster_id = clusters.LevelControl.ID, cluster_type = "SERVER", feature_map = 2},

},
device_types = {
Expand Down Expand Up @@ -173,7 +173,7 @@ local mock_device_mounted_dimmable_load_control = test.mock_device.build_test_ma
endpoint_id = 7,
clusters = {
{cluster_id = clusters.OnOff.ID, cluster_type = "SERVER", cluster_revision = 1, feature_map = 0},
{cluster_id = clusters.LevelControl.ID, cluster_type = "CLIENT", feature_map = 2},
{cluster_id = clusters.LevelControl.ID, cluster_type = "SERVER", feature_map = 2},

},
device_types = {
Expand Down Expand Up @@ -420,6 +420,10 @@ local function test_init_parent_child_switch_types()
test.socket.matter:__expect_send({mock_device_parent_child_switch_types.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_switch_types.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_parent_child_switch_types.id,
clusters.LevelControl.attributes.Options:write(mock_device_parent_child_switch_types, 7, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_parent_child_switch_types:expect_metadata_update({ profile = "switch-level" })
mock_device_parent_child_switch_types:expect_metadata_update({ provisioning_state = "PROVISIONED" })

Expand Down Expand Up @@ -463,6 +467,10 @@ end
local function test_init_dimmer()
test.mock_device.add_test_device(mock_device_dimmer)
test.socket.device_lifecycle:__queue_receive({ mock_device_dimmer.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_dimmer.id,
clusters.LevelControl.attributes.Options:write(mock_device_dimmer, 1, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_dimmer:expect_metadata_update({ profile = "switch-level" })
mock_device_dimmer:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
Expand Down Expand Up @@ -494,6 +502,10 @@ local function test_init_mounted_on_off_control()
test.socket.matter:__expect_send({mock_device_mounted_on_off_control.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_mounted_on_off_control.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_mounted_on_off_control.id,
clusters.LevelControl.attributes.Options:write(mock_device_mounted_on_off_control, 7, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_mounted_on_off_control:expect_metadata_update({ profile = "switch-binary" })
mock_device_mounted_on_off_control:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
Expand All @@ -502,6 +514,9 @@ local function test_init_mounted_dimmable_load_control()
test.mock_device.add_test_device(mock_device_mounted_dimmable_load_control)
local cluster_subscribe_list = {
clusters.OnOff.attributes.OnOff,
clusters.LevelControl.attributes.CurrentLevel,
clusters.LevelControl.attributes.MinLevel,
clusters.LevelControl.attributes.MaxLevel,
}
local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_mounted_dimmable_load_control)
for i, cluster in ipairs(cluster_subscribe_list) do
Expand All @@ -516,6 +531,10 @@ local function test_init_mounted_dimmable_load_control()
test.socket.matter:__expect_send({mock_device_mounted_dimmable_load_control.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_mounted_dimmable_load_control.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_mounted_dimmable_load_control.id,
clusters.LevelControl.attributes.Options:write(mock_device_mounted_dimmable_load_control, 7, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_mounted_dimmable_load_control:expect_metadata_update({ profile = "switch-level" })
mock_device_mounted_dimmable_load_control:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
Expand Down Expand Up @@ -557,6 +576,14 @@ local function test_init_parent_child_different_types()
test.socket.matter:__expect_send({mock_device_parent_child_different_types.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_different_types.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_parent_child_different_types.id,
clusters.LevelControl.attributes.Options:write(mock_device_parent_child_different_types, 10, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
test.socket.matter:__expect_send({
mock_device_parent_child_different_types.id,
clusters.ColorControl.attributes.Options:write(mock_device_parent_child_different_types, 10, clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_parent_child_different_types:expect_metadata_update({ profile = "switch-binary" })
mock_device_parent_child_different_types:expect_metadata_update({ provisioning_state = "PROVISIONED" })

Expand All @@ -575,6 +602,10 @@ local function test_init_parent_child_unsupported_device_type()
test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_unsupported_device_type.id, "init" })
test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_unsupported_device_type.id, "doConfigure" })
mock_device_parent_child_unsupported_device_type:expect_metadata_update({ profile = "switch-binary" })
test.socket.matter:__expect_send({
mock_device_parent_child_unsupported_device_type.id,
clusters.LevelControl.attributes.Options:write(mock_device_parent_child_unsupported_device_type, 10, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_parent_child_unsupported_device_type:expect_metadata_update({ provisioning_state = "PROVISIONED" })

mock_device_parent_child_unsupported_device_type:expect_device_create({
Expand Down Expand Up @@ -609,6 +640,10 @@ local function test_init_light_level_motion()
test.socket.matter:__expect_send({mock_device_light_level_motion.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_light_level_motion.id, "doConfigure" })
test.socket.matter:__expect_send({
mock_device_light_level_motion.id,
clusters.LevelControl.attributes.Options:write(mock_device_light_level_motion, 1, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)
})
mock_device_light_level_motion:expect_metadata_update({ profile = "light-level-motion" })
mock_device_light_level_motion:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ local function test_init()
test.socket.matter:__expect_send({mock_device.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
test.socket.matter:__expect_send({mock_device.id, clusters.LevelControl.attributes.Options:write(mock_device, child1_ep, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.matter:__expect_send({mock_device.id, clusters.LevelControl.attributes.Options:write(mock_device, child2_ep, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.matter:__expect_send({mock_device.id, clusters.ColorControl.attributes.Options:write(mock_device, child2_ep, clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
mock_device:expect_metadata_update({ profile = "light-binary" })
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })

Expand Down Expand Up @@ -222,7 +225,9 @@ for i, endpoint in ipairs(mock_device_parent_child_endpoints_non_sequential.endp
end

local function test_init_parent_child_endpoints_non_sequential()
test.mock_device.add_test_device(mock_device_parent_child_endpoints_non_sequential)
local unsup_mock_device = mock_device_parent_child_endpoints_non_sequential

test.mock_device.add_test_device(unsup_mock_device)
local cluster_subscribe_list = {
clusters.OnOff.attributes.OnOff,
clusters.LevelControl.attributes.CurrentLevel,
Expand All @@ -236,49 +241,53 @@ local function test_init_parent_child_endpoints_non_sequential()
clusters.ColorControl.attributes.CurrentX,
clusters.ColorControl.attributes.CurrentY
}
local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_parent_child_endpoints_non_sequential)
local subscribe_request = cluster_subscribe_list[1]:subscribe(unsup_mock_device)
for i, cluster in ipairs(cluster_subscribe_list) do
if i > 1 then
subscribe_request:merge(cluster:subscribe(mock_device_parent_child_endpoints_non_sequential))
subscribe_request:merge(cluster:subscribe(unsup_mock_device))
end
end

test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_endpoints_non_sequential.id, "added" })
test.socket.matter:__expect_send({mock_device_parent_child_endpoints_non_sequential.id, subscribe_request})
test.socket.device_lifecycle:__queue_receive({ unsup_mock_device.id, "added" })
test.socket.matter:__expect_send({unsup_mock_device.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ unsup_mock_device.id, "init" })
test.socket.matter:__expect_send({unsup_mock_device.id, subscribe_request})

test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_endpoints_non_sequential.id, "init" })
test.socket.matter:__expect_send({mock_device_parent_child_endpoints_non_sequential.id, subscribe_request})
test.socket.device_lifecycle:__queue_receive({ unsup_mock_device.id, "doConfigure" })
test.socket.matter:__expect_send({unsup_mock_device.id, clusters.LevelControl.attributes.Options:write(unsup_mock_device, child1_ep_non_sequential, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.matter:__expect_send({unsup_mock_device.id, clusters.LevelControl.attributes.Options:write(unsup_mock_device, child2_ep_non_sequential, clusters.LevelControl.types.OptionsBitmap.EXECUTE_IF_OFF)})
test.socket.matter:__expect_send({unsup_mock_device.id, clusters.ColorControl.attributes.Options:write(unsup_mock_device, child2_ep_non_sequential, clusters.ColorControl.types.OptionsBitmap.EXECUTE_IF_OFF)})

test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_endpoints_non_sequential.id, "doConfigure" })
mock_device_parent_child_endpoints_non_sequential:expect_metadata_update({ profile = "light-binary" })
mock_device_parent_child_endpoints_non_sequential:expect_metadata_update({ provisioning_state = "PROVISIONED" })
unsup_mock_device:expect_metadata_update({ profile = "light-binary" })
unsup_mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })

for _, child in pairs(mock_children_non_sequential) do
test.mock_device.add_test_device(child)
end

mock_device_parent_child_endpoints_non_sequential:expect_device_create({
unsup_mock_device:expect_device_create({
type = "EDGE_CHILD",
label = "Matter Switch 2",
profile = "light-color-level",
parent_device_id = mock_device_parent_child_endpoints_non_sequential.id,
parent_device_id = unsup_mock_device.id,
parent_assigned_child_key = string.format("%d", child2_ep_non_sequential)
})

-- switch-binary will be selected as an overridden child device profile
mock_device_parent_child_endpoints_non_sequential:expect_device_create({
unsup_mock_device:expect_device_create({
type = "EDGE_CHILD",
label = "Matter Switch 3",
profile = "switch-binary",
parent_device_id = mock_device_parent_child_endpoints_non_sequential.id,
parent_device_id = unsup_mock_device.id,
parent_assigned_child_key = string.format("%d", child3_ep_non_sequential)
})

mock_device_parent_child_endpoints_non_sequential:expect_device_create({
unsup_mock_device:expect_device_create({
type = "EDGE_CHILD",
label = "Matter Switch 4",
profile = "light-level",
parent_device_id = mock_device_parent_child_endpoints_non_sequential.id,
parent_device_id = unsup_mock_device.id,
parent_assigned_child_key = string.format("%d", child1_ep_non_sequential)
})
end
Expand Down
Loading