@@ -88,6 +88,7 @@ local MOUNTED_ON_OFF_CONTROL_ID = 0x010F
88
88
local MOUNTED_DIMMABLE_LOAD_CONTROL_ID = 0x0110
89
89
local GENERIC_SWITCH_ID = 0x000F
90
90
local ELECTRICAL_SENSOR_ID = 0x0510
91
+ local FAN_DEVICE_TYPE_ID = 0x002B
91
92
local device_type_profile_map = {
92
93
[ON_OFF_LIGHT_DEVICE_TYPE_ID ] = " light-binary" ,
93
94
[DIMMABLE_LIGHT_DEVICE_TYPE_ID ] = " light-level" ,
@@ -406,25 +407,64 @@ local function ignore_initial_color_read(device, attr_bit)
406
407
return false
407
408
end
408
409
410
+ local supported_mcd_configs_per_main_endpoint_device_type = {
411
+ [DIMMABLE_LIGHT_DEVICE_TYPE_ID ] = {
412
+ [GENERIC_SWITCH_ID ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 }
413
+ },
414
+ [FAN_DEVICE_TYPE_ID ] = {
415
+ [EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID ] = { 1 }
416
+ }
417
+ }
418
+
409
419
--- device_type_supports_multicomponent_configuration helper function used to check
410
- --- whether the device type for an endpoint is currently supported by a profile for
411
- --- multicomponent devices.
412
- local function device_type_supports_multicomponent_configuration (device , endpoint_id , device_type_id )
420
+ --- whether the device is currently supported by a profile for a multicomponent configuration.
421
+ local function device_type_supports_multicomponent_configuration (device , main_endpoint , secondary_endpoints )
422
+ local main_device_type = nil
423
+ local secondary_device_types_count = {}
424
+
413
425
for _ , ep in ipairs (device .endpoints ) do
414
- if ep .endpoint_id == endpoint_id then
426
+ if ep .endpoint_id == main_endpoint then
415
427
for _ , dt in ipairs (ep .device_types ) do
416
428
for _ , fingerprint in ipairs (child_device_profile_overrides_per_vendor_id [0x115F ]) do
417
429
if device .manufacturer_info .product_id == fingerprint .product_id then
418
430
return false -- For Aqara Dimmer Switch with Button.
419
431
end
420
432
end
421
- if dt .device_type_id == device_type_id then
422
- return true
433
+ if supported_mcd_configs_per_main_endpoint_device_type [dt .device_type_id ] then
434
+ main_device_type = dt .device_type_id
435
+ break
423
436
end
424
437
end
425
438
end
426
439
end
427
- return false
440
+
441
+ if not main_device_type then
442
+ return false
443
+ end
444
+
445
+ for _ , ep in ipairs (device .endpoints ) do
446
+ if secondary_endpoints [ep .endpoint_id ] and ep .endpoint_id ~= main_endpoint then
447
+ for _ , dt in ipairs (ep .device_types ) do
448
+ if supported_mcd_configs_per_main_endpoint_device_type [main_device_type ][dt .device_type_id ] then
449
+ secondary_device_types_count [dt .device_type_id ] = (secondary_device_types_count [dt .device_type_id ] or 0 ) + 1
450
+ else
451
+ return false
452
+ end
453
+ end
454
+ end
455
+ end
456
+
457
+ for secondary_device_type , count in pairs (secondary_device_types_count ) do
458
+ local allowed_counts = supported_mcd_configs_per_main_endpoint_device_type [main_device_type ][secondary_device_type ]
459
+
460
+ for _ , allowed_count in ipairs (allowed_counts ) do
461
+ if count == allowed_count then
462
+ return true
463
+ end
464
+ end
465
+
466
+ return false
467
+ end
428
468
end
429
469
430
470
local function get_first_non_zero_endpoint (endpoints )
@@ -472,7 +512,7 @@ local function find_default_endpoint(device)
472
512
-- default endpoint.
473
513
if # switch_eps > 0 and # button_eps > 0 then
474
514
local main_endpoint = get_first_non_zero_endpoint (switch_eps )
475
- if device_type_supports_multicomponent_configuration (device , main_endpoint , DIMMABLE_LIGHT_DEVICE_TYPE_ID ) then
515
+ if device_type_supports_multicomponent_configuration (device , main_endpoint , button_eps ) then
476
516
return main_endpoint
477
517
else
478
518
device .log .warn (" The main switch endpoint does not contain a supported device type for a component configuration with buttons" )
@@ -592,25 +632,25 @@ local function find_child(parent, ep_id)
592
632
return parent :get_child_by_parent_assigned_key (string.format (" %d" , ep_id ))
593
633
end
594
634
595
- local function build_component_map (device , main_endpoint , button_eps , light_eps )
596
- local eps , component_name , component_field
597
- if button_eps ~= nil and STATIC_BUTTON_PROFILE_SUPPORTED [# button_eps ] then
598
- eps = button_eps
599
- component_name = " button"
635
+ local function build_component_map (device , main_endpoint , secondary_endpoints , secondary_device_type )
636
+ local secondary_component_name , component_field
637
+ if secondary_device_type == GENERIC_SWITCH_ID then
638
+ secondary_component_name = " button"
600
639
component_field = COMPONENT_TO_ENDPOINT_MAP_BUTTON
601
- elseif light_eps ~= nil then
602
- eps = light_eps
603
- component_name = " light"
640
+ elseif secondary_device_type == EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID then
641
+ secondary_component_name = " light"
604
642
component_field = COMPONENT_TO_ENDPOINT_MAP_FAN
643
+ else
644
+ device .log .warn_with ({hub_logs = true }, " Device uses unsupported configuration for a multicomponent profile" )
645
+ return
605
646
end
606
- if eps == nil then return end
607
647
608
648
local component_map = {}
609
649
component_map [" main" ] = main_endpoint
610
- for component_num , ep in ipairs (eps ) do
650
+ for component_num , ep in ipairs (secondary_endpoints ) do
611
651
if ep ~= main_endpoint then
612
- local component = component_name
613
- if # eps > 1 then
652
+ local component = secondary_component_name
653
+ if # secondary_endpoints > 1 then
614
654
component = component .. component_num
615
655
end
616
656
component_map [component ] = ep
@@ -619,13 +659,13 @@ local function build_component_map(device, main_endpoint, button_eps, light_eps)
619
659
device :set_field (component_field , component_map , {persist = true })
620
660
end
621
661
622
- local function build_button_profile (device , endpoint , num_button_eps )
662
+ local function build_button_profile (device , main_endpoint , secondary_endpoints )
623
663
local profile_name
624
664
local battery_supported
625
- if device_type_supports_multicomponent_configuration (device , endpoint , DIMMABLE_LIGHT_DEVICE_TYPE_ID ) then
626
- profile_name = " light-level-" .. num_button_eps .. " -button"
665
+ if device_type_supports_multicomponent_configuration (device , main_endpoint , secondary_endpoints ) then
666
+ profile_name = " light-level-" .. # secondary_endpoints .. " -button"
627
667
else
628
- profile_name = num_button_eps .. " -button"
668
+ profile_name = # secondary_endpoints .. " -button"
629
669
battery_supported = # device :get_endpoints (clusters .PowerSource .ID , {feature_bitmap = clusters .PowerSource .types .PowerSourceFeature .BATTERY }) > 0
630
670
if device .manufacturer_info .vendor_id == HUE_MANUFACTURER_ID then battery_supported = false end -- no battery support in Hue case
631
671
if battery_supported then
@@ -643,8 +683,8 @@ local function build_button_profile(device, endpoint, num_button_eps)
643
683
device :set_field (BUTTON_DEVICE_PROFILED , true )
644
684
end
645
685
646
- local function build_fan_profile (device , endpoint , num_fan_eps )
647
- if device_type_supports_multicomponent_configuration (device , endpoint , EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID ) and num_fan_eps == 1 then
686
+ local function build_fan_profile (device , main_endpoint , secondary_endpoints )
687
+ if device_type_supports_multicomponent_configuration (device , main_endpoint , secondary_endpoints ) then
648
688
device :try_update_metadata ({profile = " fan-light-color-level" })
649
689
device :set_field (FAN_DEVICE_PROFILED , true )
650
690
else
@@ -719,15 +759,15 @@ local function initialize_buttons_and_switches(driver, device, main_endpoint)
719
759
table.sort (fan_eps )
720
760
721
761
-- Any button endpoints found will be added as additional components in the profile containing the
722
- -- main endpoint. A fan endpoint will be considered the main endpoint and other additional switch
723
- -- endpoints will be added as additional components . The resulting endpoint to component map is
762
+ -- main endpoint. A fan endpoint will be considered the main endpoint and the additional switch
763
+ -- endpoint will be added as an additional component . The resulting endpoint to component map is
724
764
-- saved in the corresponding component to endpoint map field.
725
765
if # button_eps > 0 then
726
- build_component_map (device , main_endpoint , button_eps , nil )
727
- build_button_profile (device , main_endpoint , # button_eps )
766
+ build_component_map (device , main_endpoint , button_eps , GENERIC_SWITCH_ID )
767
+ build_button_profile (device , main_endpoint , button_eps )
728
768
elseif # fan_eps > 0 then
729
- build_component_map (device , main_endpoint , nil , switch_eps )
730
- build_fan_profile (device , switch_eps [ 1 ], # fan_eps )
769
+ build_component_map (device , main_endpoint , switch_eps , EXTENDED_COLOR_LIGHT_DEVICE_TYPE_ID )
770
+ build_fan_profile (device , main_endpoint , switch_eps )
731
771
device :set_field (SWITCH_INITIALIZED , true , {persist = true })
732
772
return
733
773
end
0 commit comments