Skip to content

Commit 92942cb

Browse files
Matter Switch move initialization to doConfigure
This change moves the initialization logic for buttons and switches to doConfigure. This keeps all of the profile selection logic all within doConfigure and allows the removal of logic gates from device_init that were there to ensure init code only ran one time. Also added is a new function that runs at init that can rename or delete persisted fields on the device.
1 parent 88e19e0 commit 92942cb

9 files changed

+187
-120
lines changed

drivers/SmartThings/matter-switch/src/init.lua

+75-64
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,12 @@ local SWITCH_LEVEL_LIGHTING_MIN = 1
4444
local CURRENT_HUESAT_ATTR_MIN = 0
4545
local CURRENT_HUESAT_ATTR_MAX = 254
4646

47-
local SWITCH_INITIALIZED = "__switch_intialized"
48-
-- COMPONENT_TO_ENDPOINT_MAP is here only to preserve the endpoint mapping for
47+
-- COMPONENT_TO_ENDPOINT_MAP is here to preserve the endpoint mapping for
4948
-- devices that were joined to this driver as MCD devices before the transition
50-
-- to join all matter-switch devices as parent-child. This value will only exist
51-
-- in the device table for devices that joined prior to this transition, and it
52-
-- will not be set for new devices.
49+
-- to join switch devices as parent-child. This value will exist in the device
50+
-- table for devices that joined prior to this transition, and is also used for
51+
-- button devices that require component mapping.
5352
local COMPONENT_TO_ENDPOINT_MAP = "__component_to_endpoint_map"
54-
-- COMPONENT_TO_ENDPOINT_MAP_BUTTON is for devices with button endpoints, to
55-
-- preserve the MCD functionality for button devices from the matter-button
56-
-- driver after it was merged into the matter-switch driver. Note that devices
57-
-- containing both button endpoints and switch endpoints will use this field
58-
-- rather than COMPONENT_TO_ENDPOINT_MAP.
59-
local COMPONENT_TO_ENDPOINT_MAP_BUTTON = "__component_to_endpoint_map_button"
6053
local ENERGY_MANAGEMENT_ENDPOINT = "__energy_management_endpoint"
6154
local IS_PARENT_CHILD_DEVICE = "__is_parent_child_device"
6255
local COLOR_TEMP_BOUND_RECEIVED_KELVIN = "__colorTemp_bound_received_kelvin"
@@ -67,6 +60,12 @@ local LEVEL_BOUND_RECEIVED = "__level_bound_received"
6760
local LEVEL_MIN = "__level_min"
6861
local LEVEL_MAX = "__level_max"
6962
local COLOR_MODE = "__color_mode"
63+
64+
local updated_fields = {
65+
{ field_name = "__component_to_endpoint_map_button", updated_field_name = COMPONENT_TO_ENDPOINT_MAP },
66+
{ field_name = "__switch_intialized", updated_field_name = nil }
67+
}
68+
7069
local HUE_SAT_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION
7170
local X_Y_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY
7271

@@ -292,8 +291,6 @@ local HELD_THRESHOLD = 1
292291
-- this is the number of buttons for which we have a static profile already made
293292
local STATIC_BUTTON_PROFILE_SUPPORTED = {1, 2, 3, 4, 5, 6, 7, 8}
294293

295-
local BUTTON_DEVICE_PROFILED = "__button_device_profiled"
296-
297294
-- Some switches will send a MultiPressComplete event as part of a long press sequence. Normally the driver will create a
298295
-- button capability event on receipt of MultiPressComplete, but in this case that would result in an extra event because
299296
-- the "held" capability event is generated when the LongPress event is received. The IGNORE_NEXT_MPC flag is used
@@ -456,15 +453,15 @@ local function find_default_endpoint(device)
456453
end
457454

458455
local function component_to_endpoint(device, component)
459-
local map = device:get_field(COMPONENT_TO_ENDPOINT_MAP_BUTTON) or device:get_field(COMPONENT_TO_ENDPOINT_MAP) or {}
456+
local map = device:get_field(COMPONENT_TO_ENDPOINT_MAP) or {}
460457
if map[component] then
461458
return map[component]
462459
end
463460
return find_default_endpoint(device)
464461
end
465462

466463
local function endpoint_to_component(device, ep)
467-
local map = device:get_field(COMPONENT_TO_ENDPOINT_MAP_BUTTON) or device:get_field(COMPONENT_TO_ENDPOINT_MAP) or {}
464+
local map = device:get_field(COMPONENT_TO_ENDPOINT_MAP) or {}
468465
for component, endpoint in pairs(map) do
469466
if endpoint == ep then
470467
return component
@@ -473,6 +470,17 @@ local function endpoint_to_component(device, ep)
473470
return "main"
474471
end
475472

473+
local function check_field_name_updates(device)
474+
for _, field in ipairs(updated_fields) do
475+
if device:get_field(field.field_name) then
476+
if field.updated_field_name ~= nil then
477+
device:set_field(field.updated_field_name, device:get_field(field.field_name), {persist = true})
478+
end
479+
device:set_field(field.field_name, nil)
480+
end
481+
end
482+
end
483+
476484
local function assign_child_profile(device, child_ep)
477485
local profile
478486

@@ -512,41 +520,6 @@ local function assign_child_profile(device, child_ep)
512520
return profile or "switch-binary"
513521
end
514522

515-
local function do_configure(driver, device)
516-
if device:get_field(BUTTON_DEVICE_PROFILED) then
517-
return
518-
end
519-
local fan_eps = device:get_endpoints(clusters.FanControl.ID)
520-
local level_eps = device:get_endpoints(clusters.LevelControl.ID)
521-
local energy_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalEnergyMeasurement.ID)
522-
local power_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalPowerMeasurement.ID)
523-
local valve_eps = embedded_cluster_utils.get_endpoints(device, clusters.ValveConfigurationAndControl.ID)
524-
local profile_name = nil
525-
local level_support = ""
526-
if #level_eps > 0 then
527-
level_support = "-level"
528-
end
529-
if #energy_eps > 0 and #power_eps > 0 then
530-
profile_name = "plug" .. level_support .. "-power-energy-powerConsumption"
531-
elseif #energy_eps > 0 then
532-
profile_name = "plug" .. level_support .. "-energy-powerConsumption"
533-
elseif #power_eps > 0 then
534-
profile_name = "plug" .. level_support .. "-power"
535-
elseif #valve_eps > 0 then
536-
profile_name = "water-valve"
537-
if #embedded_cluster_utils.get_endpoints(device, clusters.ValveConfigurationAndControl.ID,
538-
{feature_bitmap = clusters.ValveConfigurationAndControl.types.Feature.LEVEL}) > 0 then
539-
profile_name = profile_name .. "-level"
540-
end
541-
elseif #fan_eps > 0 then
542-
profile_name = "light-color-level-fan"
543-
end
544-
545-
if profile_name then
546-
device:try_update_metadata({ profile = profile_name })
547-
end
548-
end
549-
550523
local function configure_buttons(device)
551524
if device.network_type == device_lib.NETWORK_TYPE_CHILD then
552525
return
@@ -603,7 +576,7 @@ local function build_button_component_map(device, main_endpoint, button_eps)
603576
component_map[button_component] = ep
604577
end
605578
end
606-
device:set_field(COMPONENT_TO_ENDPOINT_MAP_BUTTON, component_map, {persist = true})
579+
device:set_field(COMPONENT_TO_ENDPOINT_MAP, component_map, {persist = true})
607580
end
608581

609582
local function build_button_profile(device, main_endpoint, num_button_eps)
@@ -619,7 +592,6 @@ local function build_button_profile(device, main_endpoint, num_button_eps)
619592
else
620593
device:try_update_metadata({profile = profile_name})
621594
end
622-
device:set_field(BUTTON_DEVICE_PROFILED, true)
623595
end
624596

625597
local function build_child_switch_profiles(driver, device, main_endpoint)
@@ -683,13 +655,15 @@ local function handle_light_switch_with_onOff_server_clusters(device, main_endpo
683655
end
684656

685657
local function initialize_buttons_and_switches(driver, device, main_endpoint)
658+
local profile_found = false
686659
local button_eps = device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH})
687660
if tbl_contains(STATIC_BUTTON_PROFILE_SUPPORTED, #button_eps) then
688661
build_button_profile(device, main_endpoint, #button_eps)
689662
-- All button endpoints found will be added as additional components in the profile containing the main_endpoint.
690-
-- The resulting endpoint to component map is saved in the COMPONENT_TO_ENDPOINT_MAP_BUTTON field
663+
-- The resulting endpoint to component map is saved in the COMPONENT_TO_ENDPOINT_MAP field
691664
build_button_component_map(device, main_endpoint, button_eps)
692665
configure_buttons(device)
666+
profile_found = true
693667
end
694668

695669
-- Without support for bindings, only clusters that are implemented as server are counted. This count is handled
@@ -701,9 +675,9 @@ local function initialize_buttons_and_switches(driver, device, main_endpoint)
701675
-- Note: since their device type isn't supported, these devices join as a matter-thing.
702676
if num_switch_server_eps > 0 and detect_matter_thing(device) then
703677
handle_light_switch_with_onOff_server_clusters(device, main_endpoint)
678+
profile_found = true
704679
end
705-
706-
device:set_field(SWITCH_INITIALIZED, true, {persist = true})
680+
return profile_found
707681
end
708682

709683
local function detect_bridge(device)
@@ -721,20 +695,13 @@ local function device_init(driver, device)
721695
if device.network_type ~= device_lib.NETWORK_TYPE_MATTER then
722696
return
723697
end
724-
698+
check_field_name_updates(device)
725699
device:set_component_to_endpoint_fn(component_to_endpoint)
726700
device:set_endpoint_to_component_fn(endpoint_to_component)
727-
728-
local main_endpoint = find_default_endpoint(device)
729-
if not device:get_field(COMPONENT_TO_ENDPOINT_MAP) and -- this field is only set for old MCD devices. See comments in the field def.
730-
not device:get_field(SWITCH_INITIALIZED) and
731-
not detect_bridge(device) then
732-
-- initialize the main device card with buttons if applicable, and create child devices as needed for multi-switch devices.
733-
initialize_buttons_and_switches(driver, device, main_endpoint)
734-
end
735701
if device:get_field(IS_PARENT_CHILD_DEVICE) then
736702
device:set_find_child(find_child)
737703
end
704+
local main_endpoint = find_default_endpoint(device)
738705
-- ensure subscription to all endpoint attributes- including those mapped to child devices
739706
for _, ep in ipairs(device.endpoints) do
740707
if ep.endpoint_id ~= main_endpoint then
@@ -756,6 +723,50 @@ local function device_init(driver, device)
756723
device:subscribe()
757724
end
758725

726+
local function do_configure(driver, device)
727+
if detect_bridge(device) then
728+
return
729+
end
730+
local main_endpoint = find_default_endpoint(device)
731+
-- initialize the main device card with buttons if applicable, and create child devices as needed for multi-switch devices.
732+
local profile_found = initialize_buttons_and_switches(driver, device, main_endpoint)
733+
if device:get_field(IS_PARENT_CHILD_DEVICE) then
734+
device:set_find_child(find_child)
735+
end
736+
if profile_found then
737+
return
738+
end
739+
740+
local fan_eps = device:get_endpoints(clusters.FanControl.ID)
741+
local level_eps = device:get_endpoints(clusters.LevelControl.ID)
742+
local energy_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalEnergyMeasurement.ID)
743+
local power_eps = embedded_cluster_utils.get_endpoints(device, clusters.ElectricalPowerMeasurement.ID)
744+
local valve_eps = embedded_cluster_utils.get_endpoints(device, clusters.ValveConfigurationAndControl.ID)
745+
local profile_name = nil
746+
local level_support = ""
747+
if #level_eps > 0 then
748+
level_support = "-level"
749+
end
750+
if #energy_eps > 0 and #power_eps > 0 then
751+
profile_name = "plug" .. level_support .. "-power-energy-powerConsumption"
752+
elseif #energy_eps > 0 then
753+
profile_name = "plug" .. level_support .. "-energy-powerConsumption"
754+
elseif #power_eps > 0 then
755+
profile_name = "plug" .. level_support .. "-power"
756+
elseif #valve_eps > 0 then
757+
profile_name = "water-valve"
758+
if #embedded_cluster_utils.get_endpoints(device, clusters.ValveConfigurationAndControl.ID,
759+
{feature_bitmap = clusters.ValveConfigurationAndControl.types.Feature.LEVEL}) > 0 then
760+
profile_name = profile_name .. "-level"
761+
end
762+
elseif #fan_eps > 0 then
763+
profile_name = "light-color-level-fan"
764+
end
765+
if profile_name then
766+
device:try_update_metadata({ profile = profile_name })
767+
end
768+
end
769+
759770
local function device_removed(driver, device)
760771
log.info("device removed")
761772
delete_import_poll_schedule(device)

0 commit comments

Comments
 (0)