@@ -31,83 +31,69 @@ local DeviceConfiguration = {}
3131local SwitchDeviceConfiguration = {}
3232local ButtonDeviceConfiguration = {}
3333
34- function SwitchDeviceConfiguration .assign_child_profile (device , child_ep )
35- local profile
36-
37- for _ , ep in ipairs (device .endpoints ) do
38- if ep .endpoint_id == child_ep then
39- -- Some devices report multiple device types which are a subset of
40- -- a superset device type (For example, Dimmable Light is a superset of
41- -- On/Off light). This mostly applies to the four light types, so we will want
42- -- to match the profile for the superset device type. This can be done by
43- -- matching to the device type with the highest ID
44- local id = 0
45- for _ , dt in ipairs (ep .device_types ) do
46- id = math.max (id , dt .device_type_id )
34+ function SwitchDeviceConfiguration .assign_profile_for_onoff_ep (device , onoff_ep_id , is_child_device )
35+ local ep = switch_utils .get_endpoint_info (device , onoff_ep_id )
36+ local primary_dt_id = switch_utils .find_max_subset_device_type (ep , fields .DEVICE_TYPE_ID .LIGHT )
37+ or (switch_utils .detect_matter_thing (device ) and switch_utils .find_max_subset_device_type (ep , fields .DEVICE_TYPE_ID .SWITCH ))
38+ or ep .device_types [1 ] and ep .device_types [1 ].device_type_id
39+ local profile = fields .device_type_profile_map [primary_dt_id ]
40+
41+ if is_child_device then
42+ -- Check if device has an overridden child profile that differs from the profile that would match
43+ -- the child's device type for the following two cases:
44+ -- 1. To add Electrical Sensor only to the first EDGE_CHILD (light-power-energy-powerConsumption)
45+ -- for the Aqara Light Switch H2. The profile of the second EDGE_CHILD for this device is
46+ -- determined in the "for" loop above (e.g., light-binary)
47+ -- 2. The selected profile for the child device matches the initial profile defined in
48+ -- child_device_profile_overrides
49+ for _ , vendor in pairs (fields .child_device_profile_overrides_per_vendor_id ) do
50+ for _ , fingerprint in ipairs (vendor ) do
51+ if device .manufacturer_info .product_id == fingerprint .product_id and
52+ ((device .manufacturer_info .vendor_id == fields .AQARA_MANUFACTURER_ID and onoff_ep_id == 1 ) or profile == fingerprint .initial_profile ) then
53+ return fingerprint .target_profile
54+ end
4755 end
48- profile = fields .device_type_profile_map [id ]
49- break
5056 end
51- end
5257
53- -- Check if device has an overridden child profile that differs from the profile that would match
54- -- the child's device type for the following two cases:
55- -- 1. To add Electrical Sensor only to the first EDGE_CHILD (light-power-energy-powerConsumption)
56- -- for the Aqara Light Switch H2. The profile of the second EDGE_CHILD for this device is
57- -- determined in the "for" loop above (e.g., light-binary)
58- -- 2. The selected profile for the child device matches the initial profile defined in
59- -- child_device_profile_overrides
60- for id , vendor in pairs (fields .child_device_profile_overrides_per_vendor_id ) do
61- for _ , fingerprint in ipairs (vendor ) do
62- if device .manufacturer_info .product_id == fingerprint .product_id and
63- ((device .manufacturer_info .vendor_id == fields .AQARA_MANUFACTURER_ID and child_ep == 1 ) or profile == fingerprint .initial_profile ) then
64- return fingerprint .target_profile
65- end
66- end
58+ -- default to "switch-binary" if no profile is found
59+ return profile or " switch-binary"
6760 end
6861
69- -- default to "switch-binary" if no profile is found
70- return profile or " switch-binary"
62+ return profile
7163end
7264
73- function SwitchDeviceConfiguration .create_child_switch_devices (driver , device , main_endpoint )
74- local num_switch_server_eps = 0
75- local parent_child_device = false
76- local switch_eps = device :get_endpoints (clusters .OnOff .ID )
77- table.sort (switch_eps )
78- for idx , ep in ipairs (switch_eps ) do
79- if device :supports_server_cluster (clusters .OnOff .ID , ep ) then
80- num_switch_server_eps = num_switch_server_eps + 1
81- if ep ~= main_endpoint then -- don't create a child device that maps to the main endpoint
82- local name = string.format (" %s %d" , device .label , num_switch_server_eps )
83- local child_profile = SwitchDeviceConfiguration .assign_child_profile (device , ep )
84- driver :try_create_device (
85- {
86- type = " EDGE_CHILD" ,
87- label = name ,
88- profile = child_profile ,
89- parent_device_id = device .id ,
90- parent_assigned_child_key = string.format (" %d" , ep ),
91- vendor_provided_label = name
92- }
93- )
94- parent_child_device = true
95- if idx == 1 and string.find (child_profile , " energy" ) then
96- -- when energy management is defined in the root endpoint(0), replace it with the first switch endpoint and process it.
97- device :set_field (fields .ENERGY_MANAGEMENT_ENDPOINT , ep , {persist = true })
98- end
65+ function SwitchDeviceConfiguration .create_child_devices (driver , device , server_onoff_ep_ids , main_endpoint_id )
66+ if # server_onoff_ep_ids == 1 and server_onoff_ep_ids [1 ] == main_endpoint_id then -- no children will be created
67+ return
68+ end
69+
70+ local device_num = 0
71+ table.sort (server_onoff_ep_ids )
72+ for idx , ep_id in ipairs (server_onoff_ep_ids ) do
73+ device_num = device_num + 1
74+ if ep_id ~= main_endpoint_id then -- don't create a child device that maps to the main endpoint
75+ local name = string.format (" %s %d" , device .label , device_num )
76+ local child_profile = SwitchDeviceConfiguration .assign_profile_for_onoff_ep (device , ep_id , true )
77+ driver :try_create_device (
78+ {
79+ type = " EDGE_CHILD" ,
80+ label = name ,
81+ profile = child_profile ,
82+ parent_device_id = device .id ,
83+ parent_assigned_child_key = string.format (" %d" , ep_id ),
84+ vendor_provided_label = name
85+ }
86+ )
87+ if idx == 1 and string.find (child_profile , " energy" ) then
88+ -- when energy management is defined in the root endpoint(0), replace it with the first switch endpoint and process it.
89+ device :set_field (fields .ENERGY_MANAGEMENT_ENDPOINT , ep_id , {persist = true })
9990 end
10091 end
10192 end
10293
103- -- If the device is a parent child device, set the find_child function on init. This is persisted because initialize_buttons_and_switches
104- -- is only run once, but find_child function should be set on each driver init.
105- if parent_child_device then
106- device :set_field (fields .IS_PARENT_CHILD_DEVICE , true , {persist = true })
107- end
108-
109- -- this is needed in initialize_buttons_and_switches
110- return num_switch_server_eps
94+ -- Persist so that the find_child function is always set on each driver init.
95+ device :set_field (fields .IS_PARENT_CHILD_DEVICE , true , {persist = true })
96+ device :set_find_child (switch_utils .find_child )
11197end
11298
11399function SwitchDeviceConfiguration .update_devices_with_onOff_server_clusters (device , main_endpoint )
@@ -117,7 +103,7 @@ function SwitchDeviceConfiguration.update_devices_with_onOff_server_clusters(dev
117103 if main_endpoint == ep .endpoint_id then
118104 for _ , dt in ipairs (ep .device_types ) do
119105 -- no device type that is not in the switch subset should be considered.
120- if (fields .ON_OFF_SWITCH_ID <= dt .device_type_id and dt .device_type_id <= fields .ON_OFF_COLOR_DIMMER_SWITCH_ID ) then
106+ if (fields .DEVICE_TYPE_ID . SWITCH . ON_OFF_LIGHT <= dt .device_type_id and dt .device_type_id <= fields .DEVICE_TYPE_ID . SWITCH . COLOR_DIMMER ) then
121107 cluster_id = math.max (cluster_id , dt .device_type_id )
122108 end
123109 end
@@ -199,75 +185,56 @@ end
199185
200186-- [[ PROFILE MATCHING AND CONFIGURATIONS ]] --
201187
202- function DeviceConfiguration .initialize_buttons_and_switches (driver , device , main_endpoint )
203- local profile_found = false
204- local button_eps = device :get_endpoints (clusters .Switch .ID , {feature_bitmap = clusters .Switch .types .SwitchFeature .MOMENTARY_SWITCH })
205- if switch_utils .tbl_contains (fields .STATIC_BUTTON_PROFILE_SUPPORTED , # button_eps ) then
206- ButtonDeviceConfiguration .update_button_profile (device , main_endpoint , # button_eps )
207- -- All button endpoints found will be added as additional components in the profile containing the main_endpoint.
208- -- The resulting endpoint to component map is saved in the COMPONENT_TO_ENDPOINT_MAP field
209- ButtonDeviceConfiguration .update_button_component_map (device , main_endpoint , button_eps )
210- ButtonDeviceConfiguration .configure_buttons (device )
211- profile_found = true
212- end
213-
214- -- Without support for bindings, only clusters that are implemented as server are counted. This count is handled
215- -- while building switch child profiles
216- local num_switch_server_eps = SwitchDeviceConfiguration .create_child_switch_devices (driver , device , main_endpoint )
217-
218- -- We do not support the Light Switch device types because they require OnOff to be implemented as 'client', which requires us to support bindings.
219- -- However, this workaround profiles devices that claim to be Light Switches, but that break spec and implement OnOff as 'server'.
220- -- Note: since their device type isn't supported, these devices join as a matter-thing.
221- if num_switch_server_eps > 0 and switch_utils .detect_matter_thing (device ) then
222- SwitchDeviceConfiguration .update_devices_with_onOff_server_clusters (device , main_endpoint )
223- profile_found = true
224- end
225- return profile_found
226- end
227-
228188function DeviceConfiguration .match_profile (driver , device )
229- local main_endpoint = switch_utils .find_default_endpoint (device )
230- -- initialize the main device card with buttons if applicable, and create child devices as needed for multi-switch devices.
231- local profile_found = DeviceConfiguration .initialize_buttons_and_switches (driver , device , main_endpoint )
232- if device :get_field (fields .IS_PARENT_CHILD_DEVICE ) then
233- device :set_find_child (switch_utils .find_child )
234- end
235- if profile_found then
236- return
237- end
189+ local main_endpoint_id = switch_utils .find_default_endpoint (device )
190+ local updated_profile = nil
238191
239- local fan_eps = device :get_endpoints (clusters .FanControl .ID )
240- local level_eps = device :get_endpoints (clusters .LevelControl .ID )
241- local energy_eps = embedded_cluster_utils .get_endpoints (device , clusters .ElectricalEnergyMeasurement .ID )
242- local power_eps = embedded_cluster_utils .get_endpoints (device , clusters .ElectricalPowerMeasurement .ID )
243192 local valve_eps = embedded_cluster_utils .get_endpoints (device , clusters .ValveConfigurationAndControl .ID )
244- local profile_name = nil
245- local level_support = " "
246- if # level_eps > 0 then
247- level_support = " -level"
248- end
249- if # energy_eps > 0 and # power_eps > 0 then
250- profile_name = " plug" .. level_support .. " -power-energy-powerConsumption"
251- elseif # energy_eps > 0 then
252- profile_name = " plug" .. level_support .. " -energy-powerConsumption"
253- elseif # power_eps > 0 then
254- profile_name = " plug" .. level_support .. " -power"
255- elseif # valve_eps > 0 then
256- profile_name = " water-valve"
193+ if # valve_eps > 0 then
194+ updated_profile = " water-valve"
257195 if # embedded_cluster_utils .get_endpoints (device , clusters .ValveConfigurationAndControl .ID ,
258196 {feature_bitmap = clusters .ValveConfigurationAndControl .types .Feature .LEVEL }) > 0 then
259- profile_name = profile_name .. " -level"
197+ updated_profile = updated_profile .. " -level"
260198 end
261- elseif # fan_eps > 0 then
262- profile_name = " light-color-level-fan"
263199 end
264- if profile_name then
265- device :try_update_metadata ({ profile = profile_name })
200+
201+ local server_onoff_ep_ids = device :get_endpoints (clusters .OnOff .ID , { cluster_type = " SERVER" })
202+ if # server_onoff_ep_ids > 0 then
203+ SwitchDeviceConfiguration .create_child_devices (driver , device , server_onoff_ep_ids , main_endpoint_id )
204+ updated_profile = SwitchDeviceConfiguration .assign_profile_for_onoff_ep (device , main_endpoint_id )
205+ local find_substr = function (s , p ) return string.find (s or " " , p , 1 , true ) end
206+
207+ if find_substr (updated_profile , " plug-binary" ) or find_substr (updated_profile , " plug-level" ) then
208+ local electrical_tags = " "
209+ if # embedded_cluster_utils .get_endpoints (device , clusters .ElectricalPowerMeasurement .ID ) > 0 then electrical_tags = electrical_tags .. " -power" end
210+ if # embedded_cluster_utils .get_endpoints (device , clusters .ElectricalEnergyMeasurement .ID ) > 0 then electrical_tags = electrical_tags .. " -energy-powerConsumption" end
211+ if electrical_tags ~= " " then updated_profile = string.gsub (updated_profile , " -binary" , " " ) .. electrical_tags end
212+ elseif find_substr (updated_profile , " light-color-level" ) and # device :get_endpoints (clusters .FanControl .ID ) > 0 then
213+ updated_profile = " light-color-level-fan"
214+ elseif find_substr (updated_profile , " light-level" ) and # device :get_endpoints (clusters .OccupancySensing .ID ) > 0 then
215+ updated_profile = " light-level-motion"
216+ elseif find_substr (updated_profile , " light-level-colorTemperature" ) or find_substr (updated_profile , " light-color-level" ) then
217+ -- ignore attempts to dynamically profile light-level-colorTemperature and light-color-level devices for now, since
218+ -- these may lose fingerprinted Kelvin ranges when dynamically profiled.
219+ return
220+ end
266221 end
222+
223+ -- initialize the main device card with buttons if applicable
224+ local button_eps = device :get_endpoints (clusters .Switch .ID , {feature_bitmap = clusters .Switch .types .SwitchFeature .MOMENTARY_SWITCH })
225+ if switch_utils .tbl_contains (fields .STATIC_BUTTON_PROFILE_SUPPORTED , # button_eps ) then
226+ ButtonDeviceConfiguration .update_button_profile (device , main_endpoint_id , # button_eps )
227+ -- All button endpoints found will be added as additional components in the profile containing the main_endpoint.
228+ ButtonDeviceConfiguration .update_button_component_map (device , main_endpoint_id , button_eps )
229+ ButtonDeviceConfiguration .configure_buttons (device )
230+ return
231+ end
232+
233+ device :try_update_metadata ({ profile = updated_profile })
267234end
268235
269236return {
270237 DeviceCfg = DeviceConfiguration ,
271238 SwitchCfg = SwitchDeviceConfiguration ,
272239 ButtonCfg = ButtonDeviceConfiguration
273- }
240+ }
0 commit comments