Skip to content

Commit

Permalink
fixup! move mapping and profile switch to init, fix unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ctowns committed Sep 25, 2023
1 parent a9e0c3a commit d70a309
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 49 deletions.
43 changes: 21 additions & 22 deletions drivers/SmartThings/matter-switch/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ local function find_default_endpoint(device, component)
return res
end

local function create_endpoint_to_component_map(device)
local function initialize_switch(device)
local switch_eps = device:get_endpoints(clusters.OnOff.ID)
table.sort(switch_eps)

Expand All @@ -71,6 +71,21 @@ local function create_endpoint_to_component_map(device)
end

device:set_field(ENDPOINT_TO_COMPONENT_MAP, endpoint_map, {persist = true})

-- New profiles need to be added for devices that have more switch endpoints
local MAX_MULTI_SWITCH_EPS = 7
-- Note: This profile switching is needed because of shortcoming in the generic fingerprints
-- where devices with multiple endpoints with the same device type cannot be detected
local num_switch_eps = #switch_eps
if num_switch_eps > 1 then
device:try_update_metadata({profile = string.format("switch-%d", math.min(num_switch_eps, MAX_MULTI_SWITCH_EPS))})
end
if num_switch_eps > MAX_MULTI_SWITCH_EPS then
error(string.format(
"Matter multi switch device will have limited function. Profile doesn't exist with %d components",
num_switch_eps
))
end
end

local function component_to_endpoint(device, component_name)
Expand All @@ -91,7 +106,10 @@ end

local function device_init(driver, device)
log.info_with({hub_logs=true}, "device init")
create_endpoint_to_component_map(device)
if not device:get_field(ENDPOINT_TO_COMPONENT_MAP) then
-- create endpoint to component map and switch profile as needed
initialize_switch(device)
end
device:set_component_to_endpoint_fn(component_to_endpoint)
device:set_endpoint_to_component_fn(endpoint_to_component)
device:subscribe()
Expand All @@ -101,24 +119,6 @@ local function device_removed(driver, device)
log.info("device removed")
end

local function do_configure(driver, device)
-- New profiles need to be added for devices that have more switch endpoints
local MAX_MULTI_SWITCH_EPS = 7
-- Note: This profile switching is needed because of shortcoming in the generic fingerprints
-- where devices with multiple endpoints with the same device type cannot be detected
local switch_eps = device:get_endpoints(clusters.OnOff.ID)
local num_switch_eps = #switch_eps
if num_switch_eps > 1 then
device:try_update_metadata({profile = string.format("switch-%d", math.min(num_switch_eps, MAX_MULTI_SWITCH_EPS))})
end
if num_switch_eps > MAX_MULTI_SWITCH_EPS then
error(string.format(
"Matter multi switch device will have limited function. Profile doesn't exist with %d components",
num_switch_eps
))
end
end

local function handle_switch_on(driver, device, cmd)
local endpoint_id = device:component_to_endpoint(cmd.component)
--TODO use OnWithRecallGlobalScene for devices with the LT feature
Expand Down Expand Up @@ -302,8 +302,7 @@ end
local matter_driver_template = {
lifecycle_handlers = {
init = device_init,
removed = device_removed,
doConfigure = do_configure,
removed = device_removed
},
matter_handlers = {
attr = {
Expand Down
75 changes: 48 additions & 27 deletions drivers/SmartThings/matter-switch/src/test/test_multi_switch.lua
Original file line number Diff line number Diff line change
Expand Up @@ -146,46 +146,40 @@ local mock_3switch_non_sequential = test.mock_device.build_test_matter_device({
}
})


local function test_init()
local function test_init_mock_3switch()
local cluster_subscribe_list = {
clusters.OnOff.attributes.OnOff,
}
test.socket.matter:__set_channel_ordering("relaxed")
local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_3switch)
test.socket.matter:__expect_send({mock_3switch.id, subscribe_request})
test.mock_device.add_test_device(mock_3switch)
mock_3switch:expect_metadata_update({ profile = "switch-3" })
end

local function test_init_mock_2switch()
local cluster_subscribe_list = {
clusters.OnOff.attributes.OnOff,
}
test.socket.matter:__set_channel_ordering("relaxed")
local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_2switch)
test.socket.matter:__expect_send({mock_2switch.id, subscribe_request})
test.mock_device.add_test_device(mock_2switch)
mock_2switch:expect_metadata_update({ profile = "switch-2" })
end

local function test_init_mock_3switch_non_sequential()
local cluster_subscribe_list = {
clusters.OnOff.attributes.OnOff,
}
test.socket.matter:__set_channel_ordering("relaxed")
local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_3switch_non_sequential)
test.socket.matter:__expect_send({mock_3switch_non_sequential.id, subscribe_request})
test.mock_device.add_test_device(mock_3switch_non_sequential)
mock_3switch_non_sequential:expect_metadata_update({ profile = "switch-3" })
end
test.set_test_init_function(test_init)


test.register_coroutine_test(
"Profile change for 3 switch device", function()
test.socket.device_lifecycle:__queue_receive({ mock_3switch.id, "doConfigure" })
mock_3switch:expect_metadata_update({ profile = "switch-3" })
mock_3switch:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end)

test.register_coroutine_test(
"Profile change for 2 switch device", function()
test.socket.device_lifecycle:__queue_receive({ mock_2switch.id, "doConfigure" })
mock_2switch:expect_metadata_update({ profile = "switch-2" })
mock_2switch:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end)

test.register_coroutine_test(
"Profile change for 3 switch device with non sequential endpoints", function()
test.socket.device_lifecycle:__queue_receive({ mock_3switch_non_sequential.id, "doConfigure" })
mock_3switch_non_sequential:expect_metadata_update({ profile = "switch-3" })
mock_3switch_non_sequential:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end)

-- The custom "test_init" function also checks that the appropriate profile is switched on init
test.register_message_test(
"On command to component switch should send the appropriate commands",
{
Expand All @@ -205,9 +199,35 @@ test.register_message_test(
clusters.OnOff.server.commands.On(mock_3switch, 2)
}
}
}
},
{ test_init = test_init_mock_3switch }
)

-- The custom "test_init" function also checks that the appropriate profile is switched on init
test.register_message_test(
"On command to component switch should send the appropriate commands",
{
{
channel = "capability",
direction = "receive",
message = {
mock_2switch.id,
{ capability = "switch", component = "main", command = "on", args = { } }
}
},
{
channel = "matter",
direction = "send",
message = {
mock_2switch.id,
clusters.OnOff.server.commands.On(mock_2switch, 1)
}
}
},
{ test_init = test_init_mock_2switch }
)

-- The custom "test_init" function also checks that the appropriate profile is switched on init
test.register_message_test(
"On command to component switch should send the appropriate commands for devices with non-sequential endpoints",
{
Expand All @@ -227,7 +247,8 @@ test.register_message_test(
clusters.OnOff.server.commands.On(mock_3switch_non_sequential, 15) -- switch 3 is on endpoint 15
}
}
}
},
{ test_init = test_init_mock_3switch_non_sequential }
)

test.run_registered_tests()

0 comments on commit d70a309

Please sign in to comment.