From feccb91b9b801e1959fdcd8c7dcaf7f120c14944 Mon Sep 17 00:00:00 2001 From: JanJakubiszyn <101173721+JanJakubiszyn@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:11:56 +0200 Subject: [PATCH] Revert "Adding support for Hager WAASYS devices" --- .../matter-switch/fingerprints.yml | 20 - .../profiles/motion-illuminance.yml | 14 - .../profiles/window-covering.yml | 21 - .../SmartThings/matter-switch/src/init.lua | 3 +- .../src/sub_drivers/hager/can_handle.lua | 13 - .../src/sub_drivers/hager/init.lua | 475 ----- .../switch_handlers/attribute_handlers.lua | 2 +- .../src/switch_utils/device_configuration.lua | 1 + .../matter-switch/src/switch_utils/fields.lua | 10 - .../src/test/test_hager_waasys.lua | 1776 ----------------- 10 files changed, 3 insertions(+), 2332 deletions(-) delete mode 100644 drivers/SmartThings/matter-switch/profiles/motion-illuminance.yml delete mode 100644 drivers/SmartThings/matter-switch/profiles/window-covering.yml delete mode 100644 drivers/SmartThings/matter-switch/src/sub_drivers/hager/can_handle.lua delete mode 100644 drivers/SmartThings/matter-switch/src/sub_drivers/hager/init.lua delete mode 100644 drivers/SmartThings/matter-switch/src/test/test_hager_waasys.lua diff --git a/drivers/SmartThings/matter-switch/fingerprints.yml b/drivers/SmartThings/matter-switch/fingerprints.yml index 222950789b..8637b37713 100644 --- a/drivers/SmartThings/matter-switch/fingerprints.yml +++ b/drivers/SmartThings/matter-switch/fingerprints.yml @@ -904,26 +904,6 @@ matterManufacturer: vendorId: 0x1285 productId: 0x0009 deviceProfileName: 4-button-batteryLevel - - id: "4741/5" - deviceLabel: Hager Switch 1G - vendorId: 0x1285 - productId: 0x0005 - deviceProfileName: matter-bridge - - id: "4741/6" - deviceLabel: Hager Switch 2G - vendorId: 0x1285 - productId: 0x0006 - deviceProfileName: matter-bridge - - id: "4741/7" - deviceLabel: Hager PIR 1.1M - vendorId: 0x1285 - productId: 0x0007 - deviceProfileName: matter-bridge - - id: "4741/10" - deviceLabel: Hager PIR 2.2M - vendorId: 0x1285 - productId: 0x000A - deviceProfileName: matter-bridge # HAOJAI - id: "5530/4113" deviceLabel: HAOJAI Smart Switch 3-key diff --git a/drivers/SmartThings/matter-switch/profiles/motion-illuminance.yml b/drivers/SmartThings/matter-switch/profiles/motion-illuminance.yml deleted file mode 100644 index dfe052043c..0000000000 --- a/drivers/SmartThings/matter-switch/profiles/motion-illuminance.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: motion-illuminance -components: -- id: main - capabilities: - - id: motionSensor - version: 1 - - id: illuminanceMeasurement - version: 1 - - id: firmwareUpdate - version: 1 - - id: refresh - version: 1 - categories: - - name: MotionSensor diff --git a/drivers/SmartThings/matter-switch/profiles/window-covering.yml b/drivers/SmartThings/matter-switch/profiles/window-covering.yml deleted file mode 100644 index b588b41ec9..0000000000 --- a/drivers/SmartThings/matter-switch/profiles/window-covering.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: window-covering -components: -- id: main - capabilities: - - id: windowShade - version: 1 - - id: windowShadePreset - version: 1 - - id: windowShadeLevel - version: 1 - - id: firmwareUpdate - version: 1 - - id: refresh - version: 1 - categories: - - name: Blind -preferences: - - preferenceId: presetPosition - explicit: true - - preferenceId: reverse - explicit: true diff --git a/drivers/SmartThings/matter-switch/src/init.lua b/drivers/SmartThings/matter-switch/src/init.lua index 3a8f32f345..e2a04e103a 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -1,4 +1,4 @@ --- Copyright © 2026 SmartThings, Inc. +-- Copyright © 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 local MatterDriver = require "st.matter.driver" @@ -374,7 +374,6 @@ local matter_driver_template = { switch_utils.lazy_load_if_possible("sub_drivers.aqara_cube"), switch_utils.lazy_load("sub_drivers.camera"), switch_utils.lazy_load_if_possible("sub_drivers.eve_energy"), - switch_utils.lazy_load_if_possible("sub_drivers.hager"), switch_utils.lazy_load_if_possible("sub_drivers.ikea_scroll"), switch_utils.lazy_load_if_possible("sub_drivers.third_reality_mk1") }, diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/hager/can_handle.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/hager/can_handle.lua deleted file mode 100644 index 3ac75a5805..0000000000 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/hager/can_handle.lua +++ /dev/null @@ -1,13 +0,0 @@ --- Copyright © 2026 SmartThings, Inc. --- Licensed under the Apache License, Version 2.0 - -return function(opts, driver, device, ...) - local device_lib = require "st.device" - local get_product_override_field = require "switch_utils.utils".get_product_override_field - - local checked_device = device.network_type == device_lib.NETWORK_TYPE_MATTER and device or device:get_parent_device() - if get_product_override_field(checked_device, "needs_hager_subdriver") then - return true, require("sub_drivers.hager") - end - return false -end diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/hager/init.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/hager/init.lua deleted file mode 100644 index 448295247d..0000000000 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/hager/init.lua +++ /dev/null @@ -1,475 +0,0 @@ --- Copyright © 2026 SmartThings, Inc. --- Licensed under the Apache License, Version 2.0 - -local capabilities = require "st.capabilities" -local clusters = require "st.matter.clusters" -local cluster_base = require "st.matter.cluster_base" -local device_lib = require "st.device" -local buttonCfg = require "switch_utils.device_configuration".ButtonCfg -local create_child = require "switch_utils.device_configuration".ChildCfg.create_or_update_child_devices -local switch_utils = require "switch_utils.utils" -local fields = require "switch_utils.fields" - -local ACTIVE_EPS = "__active_EPS" -local MATTER_DEVICE_ID = "MATTER_DEVICE_ID" -local PARENT_ID = "PARENT_ID" -local CURRENT_LIFT = "__current_lift" -local BUTTON_EPS = "__button_eps" -local MAIN_WC_EP = "__main_wc_ep" - -local PRODUCT_ID = { - SWITCH_1G = 0x0005, - SWITCH_2G = 0x0006, - PIR_1_1M = 0x0007, - PIR_2_2M = 0x000A, -} - -local function subscribe (device, endpoint_id, cluster_id, attr_id, event_id) - device:send(cluster_base.subscribe(device, endpoint_id, cluster_id, attr_id, event_id)) -end - -local function get_parent (driver, device) - return driver:get_device_info(device:get_field(PARENT_ID) or nil) -end - -local function get_matter_device(device) - local matter_device_id = device:get_field(MATTER_DEVICE_ID) - if matter_device_id then - local driver = device.driver - if driver then - local matter_device = driver:get_device_info(matter_device_id) - if matter_device then - return matter_device - end - end - end - return nil -end - -local function extract_reported_endpoints(ib) - local eps = {} - if ib.data and ib.data.elements then - for _, el in ipairs(ib.data.elements) do - local ep_id = el.value - if type(ep_id) == "number" and ep_id ~= 1 and ep_id ~= 2 then - table.insert(eps, ep_id) - end - end - end - return eps -end - -local function detect_endpoint_changes (device, ib_elements) - local stored_eps = device:get_field(ACTIVE_EPS) or {} - ib_elements = ib_elements or {} - - local old_set, new_set = {}, {} - for _, ep_id in ipairs(stored_eps) do - old_set[ep_id] = true - end - for _, ep_id in ipairs(ib_elements) do - new_set[ep_id] = true - end - - local removed_eps, added_eps = {}, {} - - for ep_id in pairs(old_set) do - if not new_set[ep_id] then - table.insert(removed_eps, ep_id) - end - end - for ep_id in pairs(new_set) do - if not old_set[ep_id] then - table.insert(added_eps, ep_id) - end - end - return removed_eps, added_eps -end - -local function assign_profile_for_endpoint(device_type_id) - local profile = fields.device_type_profile_map[device_type_id] or "switch-binary" - return function(device, ep_id, is_child_device) - return profile, nil - end -end - -local function link_matter_device_and_parent(matter_device) - local parent = matter_device:get_parent_device() - matter_device:set_field(PARENT_ID, parent.id, { persist = true }) - matter_device:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) - parent:set_field(PARENT_ID, parent.id, { persist = true }) - parent:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) -end - -local function handle_set_preset(driver, device, cmd) - local endpoint_id = device:component_to_endpoint(cmd.component) - local lift_value = device.preferences.presetPosition - local hundredths_lift_percent = (100 - tonumber(lift_value)) * 100 - device:send(clusters.WindowCovering.server.commands.GoToLiftPercentage(device, endpoint_id, hundredths_lift_percent)) -end - -local function handle_close(driver, device, cmd) - local endpoint_id = device:component_to_endpoint(cmd.component) - local req = clusters.WindowCovering.server.commands.DownOrClose(device, endpoint_id) - if device.preferences.reverse then - req = clusters.WindowCovering.server.commands.UpOrOpen(device, endpoint_id) - end - device:send(req) -end - -local function handle_open(driver, device, cmd) - local endpoint_id = device:component_to_endpoint(cmd.component) - local req = clusters.WindowCovering.server.commands.UpOrOpen(device, endpoint_id) - if device.preferences.reverse then - req = clusters.WindowCovering.server.commands.DownOrClose(device, endpoint_id) - end - device:send(req) -end - -local function handle_pause(driver, device, cmd) - local endpoint_id = device:component_to_endpoint(cmd.component) - local req = clusters.WindowCovering.server.commands.StopMotion(device, endpoint_id) - device:send(req) -end - -local function handle_shade_level(driver, device, cmd) - local endpoint_id = device:component_to_endpoint(cmd.component) - local lift_percentage_value = 100 - cmd.args.shadeLevel - local hundredths_lift_percentage = lift_percentage_value * 100 - local req = clusters.WindowCovering.server.commands.GoToLiftPercentage( - device, endpoint_id, hundredths_lift_percentage - ) - device:send(req) -end - -local current_pos_handler = function(attribute) - return function(driver, device, ib, response) - if ib.data.value == nil then - return - end - local windowShade = capabilities.windowShade.windowShade - local position = 100 - math.floor(ib.data.value / 100) - local reverse = device.preferences.reverse - device:emit_event_for_endpoint(ib.endpoint_id, attribute(position)) - if attribute == capabilities.windowShadeLevel.shadeLevel then - device:set_field(CURRENT_LIFT, position) - end - local lift_position = device:get_field(CURRENT_LIFT) - if lift_position == 100 then - device:emit_event_for_endpoint(ib.endpoint_id, reverse and windowShade.closed() or windowShade.open()) - elseif lift_position > 0 then - device:emit_event_for_endpoint(ib.endpoint_id, windowShade.partially_open()) - elseif lift_position == 0 then - device:emit_event_for_endpoint(ib.endpoint_id, reverse and windowShade.open() or windowShade.closed()) - end - end -end - -local function current_status_handler(driver, device, ib, response) - local windowShade = capabilities.windowShade.windowShade - local reverse = device.preferences.reverse - local state = ib.data.value & clusters.WindowCovering.types.OperationalStatus.GLOBAL - if state == 1 then - device:emit_event_for_endpoint(ib.endpoint_id, reverse and windowShade.closing() or windowShade.opening()) - elseif state == 2 then - device:emit_event_for_endpoint(ib.endpoint_id, reverse and windowShade.opening() or windowShade.closing()) - elseif state ~= 0 then - device:emit_event_for_endpoint(ib.endpoint_id, windowShade.unknown()) - end -end - -local function info_changed(driver, device, event, args) - if not device or not device.id or not device.profile then - return - end - - if device.profile.id ~= args.old_st_store.profile.id or device.network_type == device_lib.NETWORK_TYPE_CHILD then - local parent = device:get_parent_device() - local matter_device = get_matter_device(parent) - local map = {} - device.thread:call_with_delay(2, function() - if device:supports_capability(capabilities.button) then - local button_eps = parent:get_field(BUTTON_EPS) - local clean_eps = {} - table.sort(button_eps) - - for _, v in ipairs(button_eps or {}) do - table.insert(clean_eps, v) - end - - buttonCfg.update_button_component_map(matter_device, clean_eps[1], clean_eps) - for _, ep_id in ipairs(clean_eps) do - subscribe(device, ep_id, clusters.Switch.ID, nil, clusters.Switch.events.MultiPressComplete.ID) - subscribe(device, ep_id, clusters.Switch.ID, nil, clusters.Switch.events.LongPress.ID) - device:emit_event_for_endpoint(ep_id, capabilities.button.supportedButtonValues({ "pushed", "double", "held" })) - end - return - elseif device:supports_capability(capabilities.switch) then - map = { main = 3 } - subscribe(device, nil, clusters.OnOff.ID, clusters.OnOff.attributes.OnOff.ID, nil) - if device:supports_capability(capabilities.switchLevel) then - map = { main = 4 } - subscribe(device, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.CurrentLevel.ID, nil) - subscribe(device, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MaxLevel.ID) - subscribe(device, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MinLevel.ID) - end - elseif device:supports_capability(capabilities.motionSensor) then - subscribe(device, nil, clusters.OccupancySensing.ID, clusters.OccupancySensing.attributes.Occupancy.ID, nil) - subscribe(device, nil, clusters.IlluminanceMeasurement.ID, clusters.IlluminanceMeasurement.attributes.MeasuredValue.ID, nil) - elseif device:supports_capability(capabilities.windowShadeLevel) then - map = { main = 5 } - subscribe(device, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.OperationalStatus.ID) - subscribe(device, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths.ID, nil) - end - if device.id == matter_device.id then - parent:set_field(fields.COMPONENT_TO_ENDPOINT_MAP, map, { persist = true }) - matter_device:set_field(fields.COMPONENT_TO_ENDPOINT_MAP, map, { persist = true }) - end - end) - end -end - -local function device_init (driver, device) - if device.network_type ~= device_lib.NETWORK_TYPE_MATTER then - device.thread:call_with_delay(4, function() - info_changed(driver, device, nil, { - old_st_store = { profile = { } } - }) - end) - return - end - - if device:get_parent_device() ~= nil then - link_matter_device_and_parent(device) - local parent = device:get_parent_device() - device:extend_device("send", function(self, message) - return parent:send(message) - end) - else - subscribe(device, 2, clusters.Descriptor.ID, clusters.Descriptor.attributes.PartsList.ID) - device.thread:call_with_delay(3, function() - local matter_device = get_matter_device(device) - device:extend_device("emit_event_for_endpoint", function(self, ep_info, event) - local endpoint_id = type(ep_info) == "number" and ep_info or ep_info.endpoint_id - local child = self:get_child_by_parent_assigned_key(string.format("%d", endpoint_id)) - if child then - return child:emit_event(event) - end - if matter_device then - return matter_device:emit_event_for_endpoint(ep_info, event) - end - end) - end) - end - - device:set_component_to_endpoint_fn(switch_utils.component_to_endpoint) - device:set_endpoint_to_component_fn(switch_utils.endpoint_to_component) -end - -local function handle_descriptor_report(driver, device, ib, response) - local product_id = device.manufacturer_info and device.manufacturer_info.product_id - local parent = get_parent(driver, device) - - if not parent or ib.endpoint_id ~= 2 then - return - end - - local matter_device = get_matter_device(device) - - local new_eps = extract_reported_endpoints(ib) or {} - table.sort(new_eps) - local removed_eps, added_eps = detect_endpoint_changes(device, new_eps) - - device:set_field(ACTIVE_EPS, new_eps, { persist = true }) - - for _, ep_id in ipairs(added_eps or {}) do - subscribe(parent, ep_id, clusters.Descriptor.ID, clusters.Descriptor.attributes.DeviceTypeList.ID, nil) - - if product_id == PRODUCT_ID.SWITCH_1G and ep_id == 3 then - matter_device:try_update_metadata({ profile = "light-binary" }) - elseif product_id == PRODUCT_ID.SWITCH_2G then - if ep_id == 3 then - local profile = switch_utils.tbl_contains(new_eps, 4) and "light-binary" or "2-button" - matter_device:try_update_metadata({ profile = profile }) - elseif ep_id == 4 then - local profile = switch_utils.tbl_contains(new_eps, 3) and "light-binary" or "2-button" - matter_device:try_update_metadata({ profile = profile }) - end - end - end - - for _, ep_id in ipairs(removed_eps) do - local button_eps = parent:get_field(BUTTON_EPS) or {} - local clean_eps = {} - - for _, value in ipairs(button_eps) do - if value ~= ep_id then - table.insert(clean_eps, value) - end - end - table.sort(clean_eps) - parent:set_field(BUTTON_EPS, clean_eps, { persist = true }) - matter_device:set_field(BUTTON_EPS, clean_eps, { persist = true }) - - local child = parent:get_child_by_parent_assigned_key(tostring(ep_id)) - if child then - driver:try_delete_device(child.id) - end - - if ep_id == 3 then - if device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_1G then - matter_device:try_update_metadata({ profile = "2-button" }) - elseif device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_2G then - local has_ep4 = switch_utils.tbl_contains(new_eps, 4) - matter_device:try_update_metadata({ profile = has_ep4 and "2-button" or "4-button" }) - end - elseif ep_id == 4 then - if device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_2G then - local button_comb = switch_utils.tbl_contains(new_eps, 3) - if button_comb then - matter_device:try_update_metadata({ profile = "2-button" }) - create_child(driver, parent, { 3 }, 1, assign_profile_for_endpoint(fields.DEVICE_TYPE_ID.LIGHT.ON_OFF)) - else - matter_device:try_update_metadata({ profile = "4-button" }) - end - end - end - end -end - -local function device_type_handler (driver, device, ib) - local parent = get_parent(driver, device) - if not parent then - return - end - - local matter_device = get_matter_device(device) - local stored_btn_eps = parent:get_field(BUTTON_EPS) or {} - local new_btn_eps = {} - local ep_id = ib.endpoint_id - local value = ib.data.elements - - for _, v in ipairs(stored_btn_eps) do - table.insert(new_btn_eps, v) - end - for _, element in ipairs(value) do - local device_type_field = element.elements.device_type - local device_type_id = device_type_field and device_type_field.value - - if device_type_id == fields.DEVICE_TYPE_ID.GENERIC_SWITCH then - if not switch_utils.tbl_contains(new_btn_eps, ep_id) then - switch_utils.set_field_for_endpoint(parent, fields.SUPPORTS_MULTI_PRESS, ep_id) - switch_utils.set_field_for_endpoint(parent, fields.IGNORE_NEXT_MPC, ep_id) - table.insert(new_btn_eps, ep_id) - table.sort(new_btn_eps) - parent:set_field(BUTTON_EPS, new_btn_eps, { persist = true }) - end - end - - if device_type_id == fields.DEVICE_TYPE_ID.LIGHT.ON_OFF then - local active_eps = device:get_field(ACTIVE_EPS) - - if ep_id == 3 and device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_1G then - return - elseif ep_id == 4 and device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_2G then - if switch_utils.tbl_contains(active_eps, 3) then - local ep3 = parent:get_child_by_parent_assigned_key("3") - if ep3 then - driver:try_delete_device(ep3.id) - end - else - create_child(driver, parent, { 4 }, 1, assign_profile_for_endpoint(device_type_id)) - return - end - elseif ep_id == 3 and device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_2G then - if not switch_utils.tbl_contains(active_eps, 4) then - create_child(driver, parent, { 3 }, 1, assign_profile_for_endpoint(device_type_id)) - else - driver:try_delete_device(ep_id) - end - return - end - create_child(driver, parent, { ep_id }, 1, assign_profile_for_endpoint(device_type_id)) - elseif device_type_id == fields.DEVICE_TYPE_ID.LIGHT.DIMMABLE then - if ep_id == 4 and device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_1G then - return - end - create_child(driver, parent, { ep_id }, 1, assign_profile_for_endpoint(device_type_id)) - elseif device_type_id == fields.DEVICE_TYPE_ID.WINDOW_COVERING then - if matter_device:get_field(MAIN_WC_EP) == nil then - create_child(driver, parent, { ep_id }, 1, assign_profile_for_endpoint(device_type_id)) - end - end - end -end - -local function do_configure (driver, device) - if device.network_type == device_lib.NETWORK_TYPE_MATTER then - - local wc_eps = device:get_endpoints(clusters.WindowCovering.ID) - local oc_eps = device:get_endpoints(clusters.OccupancySensing.ID) - local bt_eps = device:get_endpoints(clusters.Switch.ID) - local lvl_eps = device:get_endpoints(clusters.LevelControl.ID) - local product_id = device.manufacturer_info.product_id - - table.sort(wc_eps) - table.sort(oc_eps) - table.sort(lvl_eps) - - if #oc_eps > 0 then - device:try_update_metadata({ profile = "motion-illuminance" }) - elseif #wc_eps > 0 and product_id == PRODUCT_ID.SWITCH_1G then - device:try_update_metadata({ profile = "window-covering" }) - device:set_field(MAIN_WC_EP, wc_eps[1], { persist = true }) - elseif #bt_eps == 4 then - device:try_update_metadata({ profile = "4-button" }) - elseif #bt_eps == 2 then - device:try_update_metadata({ profile = "2-button" }) - elseif #lvl_eps > 0 and product_id == PRODUCT_ID.SWITCH_1G then - device:try_update_metadata({ profile = "light-level" }) - end - device:set_field(BUTTON_EPS, bt_eps, { persist = true }) - end -end - -local function added (driver, device) end -local function driver_switched(driver, device) end - -local hager_switch = { - NAME = "Hager Subdriver", - lifecycle_handlers = { - added = added, - init = device_init, - infoChanged = info_changed, - doConfigure = do_configure, - driverSwitched = driver_switched - }, - matter_handlers = { - attr = { - [clusters.Descriptor.ID] = { - [clusters.Descriptor.attributes.PartsList.ID] = handle_descriptor_report, - [clusters.Descriptor.attributes.DeviceTypeList.ID] = device_type_handler, - }, - [clusters.WindowCovering.ID] = { - [clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths.ID] = current_pos_handler(capabilities.windowShadeLevel.shadeLevel), - [clusters.WindowCovering.attributes.OperationalStatus.ID] = current_status_handler, - }, - }, - }, - capability_handlers = { - [capabilities.windowShadePreset.ID] = { - [capabilities.windowShadePreset.commands.presetPosition.NAME] = handle_set_preset, - }, - [capabilities.windowShade.ID] = { - [capabilities.windowShade.commands.close.NAME] = handle_close, - [capabilities.windowShade.commands.open.NAME] = handle_open, - [capabilities.windowShade.commands.pause.NAME] = handle_pause, - }, - [capabilities.windowShadeLevel.ID] = { - [capabilities.windowShadeLevel.commands.setShadeLevel.NAME] = handle_shade_level, - }, - }, - can_handle = require("sub_drivers.hager.can_handle") -} - -return hager_switch diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua index b7feed6344..45694bc613 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua @@ -246,7 +246,7 @@ end -- [[ OCCUPANCY CLUSTER ATTRIBUTES ]] -- function AttributeHandlers.occupancy_handler(driver, device, ib, response) - device:emit_event_for_endpoint(ib.endpoint_id, ib.data.value == 0x01 and capabilities.motionSensor.motion.active() or capabilities.motionSensor.motion.inactive()) + device:emit_event(ib.data.value == 0x01 and capabilities.motionSensor.motion.active() or capabilities.motionSensor.motion.inactive()) end diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua b/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua index 9c5235d1e2..772669ebd7 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/device_configuration.lua @@ -156,6 +156,7 @@ function ButtonDeviceConfiguration.update_button_component_map(device, default_e device:set_field(fields.COMPONENT_TO_ENDPOINT_MAP, component_map, {persist = true}) end + function ButtonDeviceConfiguration.configure_buttons(device, momentary_switch_ep_ids) local msr_eps = device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH_RELEASE}) local msl_eps = device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH_LONG_PRESS}) diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua index b0aca9d102..02c864fa4f 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -34,9 +34,7 @@ SwitchFields.DEVICE_TYPE_ID = { ELECTRICAL_SENSOR = 0x0510, FAN = 0x002B, GENERIC_SWITCH = 0x000F, - LIGHT_SENSOR = 0x0106, IRRIGATION_SYSTEM = 0x0040, - OCCUPANCY_SENSOR = 0x0107, MOUNTED_ON_OFF_CONTROL = 0x010F, MOUNTED_DIMMABLE_LOAD_CONTROL = 0x0110, ON_OFF_PLUG_IN_UNIT = 0x010A, @@ -52,7 +50,6 @@ SwitchFields.DEVICE_TYPE_ID = { COLOR_DIMMER = 0x0105, }, WATER_VALVE = 0x0042, - WINDOW_COVERING = 0x0202 } SwitchFields.device_type_profile_map = { @@ -67,7 +64,6 @@ SwitchFields.device_type_profile_map = { [SwitchFields.DEVICE_TYPE_ID.DIMMABLE_PLUG_IN_UNIT] = "plug-level", [SwitchFields.DEVICE_TYPE_ID.MOUNTED_ON_OFF_CONTROL] = "switch-binary", [SwitchFields.DEVICE_TYPE_ID.MOUNTED_DIMMABLE_LOAD_CONTROL] = "switch-level", - [SwitchFields.DEVICE_TYPE_ID.WINDOW_COVERING] = "window-covering" } -- COMPONENT_TO_ENDPOINT_MAP is here to preserve the endpoint mapping for @@ -120,12 +116,6 @@ SwitchFields.vendor_overrides = { [0x1189] = { -- LEDVANCE_MANUFACTURER_ID [0x0891] = { target_profile = "switch-binary", initial_profile = "light-binary" }, }, - [0x1285] = { -- HAGER_MANUFACTURER_ID - [0x0005] = { needs_hager_subdriver = true }, -- Hager HBnet 1g switch - [0x0006] = { needs_hager_subdriver = true }, -- Hager HBnet 2g switch - [0x0007] = { needs_hager_subdriver = true }, -- Hager HBnet PIR 1.1M - [0x000A] = { needs_hager_subdriver = true }, -- Hager HBnet PIR 2.2M - }, [0x1321] = { -- SONOFF_MANUFACTURER_ID [0x000C] = { target_profile = "switch-binary", initial_profile = "plug-binary" }, [0x000D] = { target_profile = "switch-binary", initial_profile = "plug-binary" }, diff --git a/drivers/SmartThings/matter-switch/src/test/test_hager_waasys.lua b/drivers/SmartThings/matter-switch/src/test/test_hager_waasys.lua deleted file mode 100644 index 2a1652a2be..0000000000 --- a/drivers/SmartThings/matter-switch/src/test/test_hager_waasys.lua +++ /dev/null @@ -1,1776 +0,0 @@ --- Copyright © 2026 SmartThings, Inc. --- Licensed under the Apache License, Version 2.0 - -local test = require "integration_test" -local capabilities = require "st.capabilities" -local t_utils = require "integration_test.utils" -local data_types = require "st.matter.data_types" -local clusters = require "st.matter.clusters" -local cluster_base = require "st.matter.cluster_base" -local descriptor = require "st.matter.generated.zap_clusters.Descriptor" - -local MATTER_DEVICE_ID = "MATTER_DEVICE_ID" -local PARENT_ID = "PARENT_ID" -local BUTTON_EPS = "__button_eps" - -local function create_parent_device(product_id) - return test.mock_device.build_test_matter_device({ - label = "Hager G2 4x Button", - profile = t_utils.get_profile_definition("matter-bridge.yml"), - manufacturer_info = { - vendor_id = 0x1285, - product_id = product_id, - }, - endpoints = { - { - endpoint_id = 0, - clusters = { { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } }, - device_types = { { device_type_id = 0x0016, device_type_revision = 1 } } - }, - { - endpoint_id = 1, - clusters = { - { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } - }, - device_types = { { device_type_id = 0x000E, device_type_revision = 1 } } -- AggregateNode - }, - { - endpoint_id = 2, - clusters = { - { - cluster_id = clusters.Descriptor.ID, - cluster_type = "SERVER", - cluster_revision = 1, - } - }, - device_types = { { device_type_id = 0x0039, device_type_revision = 1 } } -- BridgedNode - } - } - }) -end - -local function create_matter_device(profile_name, parent) - return test.mock_device.build_test_matter_device({ - label = "Hager G2 4x Button", - profile = t_utils.get_profile_definition(profile_name .. ".yml"), - type = "MATTER", - manufacturer_info = { - vendor_id = 0x1285, - product_id = 0x0006, - }, - parent_device_id = parent.id, - endpoints = { - { - endpoint_id = 0, - clusters = { { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } }, - device_types = { { device_type_id = 0x0016, device_type_revision = 1 } } - }, - { - endpoint_id = 8, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - { - endpoint_id = 9, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - { - endpoint_id = 10, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - { - endpoint_id = 11, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - } - }) -end - --- Create Hager 2G Relay device with endpoints 3 & 4 (OnOff clusters) -local function create_hager_2g_relay(profile_name, parent) - return test.mock_device.build_test_matter_device({ - label = "Hager G2 2G Relay", - profile = t_utils.get_profile_definition(profile_name .. ".yml"), - type = "MATTER", - manufacturer_info = { - vendor_id = 0x1285, - product_id = 0x0006, - }, - parent_device_id = parent.id, - endpoints = { - { - endpoint_id = 0, - clusters = { { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } }, - device_types = { { device_type_id = 0x0016, device_type_revision = 1 } } - }, - { - endpoint_id = 3, - clusters = { - { - cluster_id = clusters.OnOff.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.OnOff.attributes.OnOff.ID] = false - } - } - }, - device_types = { { device_type_id = 0x0100, device_type_revision = 1 } } - }, - { - endpoint_id = 4, - clusters = { - { - cluster_id = clusters.OnOff.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.OnOff.attributes.OnOff.ID] = false - } - } - }, - device_types = { { device_type_id = 0x0100, device_type_revision = 1 } } - }, - } - }) -end - --- Create Hager Dimmer device with ONLY dimmable endpoint (3) - no button endpoints -local function create_hager_dimmer_device_1g(profile_name, parent) - return test.mock_device.build_test_matter_device({ - label = "Hager G2 Dimmer", - profile = t_utils.get_profile_definition(profile_name .. ".yml"), - type = "MATTER", - manufacturer_info = { - vendor_id = 0x1285, - product_id = 0x0005, - }, - parent_device_id = parent.id, - endpoints = { - { - endpoint_id = 0, - clusters = { { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } }, - device_types = { { device_type_id = 0x0016, device_type_revision = 1 } } - }, - { - endpoint_id = 4, - clusters = { - { - cluster_id = clusters.OnOff.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.OnOff.attributes.OnOff.ID] = false - } - }, - { - cluster_id = clusters.LevelControl.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.LevelControl.attributes.CurrentLevel.ID] = 254 - } - } - }, - device_types = { { device_type_id = 0x0101, device_type_revision = 1 } } - }, - } - }) -end - -local function create_hager_dimmer_device_2g(profile_name, parent) - return test.mock_device.build_test_matter_device({ - label = "Hager G2 Dimmer", - profile = t_utils.get_profile_definition(profile_name .. ".yml"), - manufacturer_info = { - vendor_id = 0x1285, - product_id = 0x0006, - }, - parent_device_id = parent.id, - endpoints = { - { - endpoint_id = 0, - clusters = { { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } }, - device_types = { { device_type_id = 0x0016, device_type_revision = 1 } } - }, - { - endpoint_id = 3, - clusters = { - { - cluster_id = clusters.OnOff.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.OnOff.attributes.OnOff.ID] = false - } - }, - { - cluster_id = clusters.LevelControl.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.LevelControl.attributes.CurrentLevel.ID] = 254 - } - } - }, - device_types = { { device_type_id = 0x0101, device_type_revision = 1 } } - }, - { - endpoint_id = 8, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - { - endpoint_id = 9, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - } - }) -end - -local function create_matter_device_with_window(profile_name, parent) - return test.mock_device.build_test_matter_device({ - label = "Hager G2 matter device with Window Covering", - profile = t_utils.get_profile_definition(profile_name .. ".yml"), - manufacturer_info = { - vendor_id = 0x1285, - product_id = 0x0006, - }, - parent_device_id = parent.id, - endpoints = { - { - endpoint_id = 0, - clusters = { { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } }, - device_types = { { device_type_id = 0x0016, device_type_revision = 1 } } - }, - { - endpoint_id = 8, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - { - endpoint_id = 9, - clusters = { - { - cluster_id = clusters.Switch.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH, - attributes = { - [clusters.Switch.attributes.MultiPressMax.ID] = 2 - } - } - }, - device_types = { { device_type_id = 0x003B, device_type_revision = 1 } } - }, - { - endpoint_id = 12, - clusters = { - { - cluster_id = clusters.WindowCovering.ID, - cluster_type = "SERVER", - cluster_revision = 1, - feature_map = clusters.WindowCovering.types.Feature.LIFT | - clusters.WindowCovering.types.Feature.POSITION_AWARE_LIFT | - clusters.WindowCovering.types.Feature.ABSOLUTE_POSITION, - attributes = { - [clusters.WindowCovering.attributes.OperationalStatus.ID] = 0x00, - [clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths.ID] = 0x0000, - } - } - }, - device_types = { { device_type_id = 0x0202, device_type_revision = 1 } } - }, - } - }) -end - -local function create_hager_pir_device(profile_name, parent) - return test.mock_device.build_test_matter_device({ - label = "Hager G2 PIR with Buttons and Motion/Illuminance/Dimmer", - profile = t_utils.get_profile_definition(profile_name .. ".yml"), - type = "MATTER", - manufacturer_info = { - vendor_id = 0x1285, - product_id = 0x0007, - }, - parent_device_id = parent.id, - endpoints = { - { - endpoint_id = 0, - clusters = { { cluster_id = clusters.Basic.ID, cluster_type = "SERVER" } }, - device_types = { { device_type_id = 0x0016, device_type_revision = 1 } } - }, - { - endpoint_id = 3, - clusters = { - { - cluster_id = clusters.OnOff.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.OnOff.attributes.OnOff.ID] = false - } - }, - { - cluster_id = clusters.LevelControl.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.LevelControl.attributes.CurrentLevel.ID] = 254 - } - } - }, - device_types = { { device_type_id = 0x0101, device_type_revision = 1 } } - }, - { - endpoint_id = 6, - clusters = { - { - cluster_id = clusters.OccupancySensing.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.OccupancySensing.attributes.Occupancy.ID] = 0 - } - } - }, - device_types = { { device_type_id = 0x0107, device_type_revision = 1 } } - }, - { - endpoint_id = 5, - clusters = { - { - cluster_id = clusters.IlluminanceMeasurement.ID, - cluster_type = "SERVER", - cluster_revision = 1, - attributes = { - [clusters.IlluminanceMeasurement.attributes.MeasuredValue.ID] = 0 - } - } - }, - device_types = { { device_type_id = 0x0106, device_type_revision = 1 } } - }, - } - }) -end - -local function add_parent_device(parent) - test.mock_device.add_test_device(parent) - test.socket.device_lifecycle:__queue_receive({ parent.id, "added" }) - test.socket.device_lifecycle:__queue_receive({ parent.id, "init" }) - test.mock_time.advance_time(5) - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, 2, descriptor.ID, descriptor.attributes.PartsList.ID, nil) - }) -end - -local function subscribe_switch_events(parent, button_eps) - for _, ep in ipairs(button_eps) do - test.socket.matter:__expect_send({ - parent.id, - clusters.Switch.events.MultiPressComplete:subscribe(parent, ep) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Switch.events.LongPress:subscribe(parent, ep) - }) - end -end - -local function add_matter_device(matter_device, parent, expected_profile_change) - test.mock_device.add_test_device(matter_device) - test.socket.device_lifecycle:__queue_receive({ matter_device.id, "added" }) - test.socket.device_lifecycle:__queue_receive({ matter_device.id, "doConfigure" }) - matter_device:expect_metadata_update({ profile = expected_profile_change }) - matter_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) - test.socket.device_lifecycle:__queue_receive({ matter_device.id, "init" }) - - matter_device:set_field(PARENT_ID, parent.id, { persist = true }) - matter_device:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) - parent:set_field(PARENT_ID, parent.id, { persist = true }) - parent:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) - parent:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) -end - -local function add_switch_matter_device(matter_device, parent) - test.mock_device.add_test_device(matter_device) - test.socket.device_lifecycle:__queue_receive({ matter_device.id, "added" }) - test.socket.device_lifecycle:__queue_receive({ matter_device.id, "doConfigure" }) - matter_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) - test.socket.device_lifecycle:__queue_receive({ matter_device.id, "init" }) - - matter_device:set_field(PARENT_ID, parent.id, { persist = true }) - matter_device:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) - parent:set_field(PARENT_ID, parent.id, { persist = true }) - parent:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) - parent:set_field(MATTER_DEVICE_ID, matter_device.id, { persist = true }) -end - -local function button_supported_values (matter_device) - test.socket.capability:__expect_send(matter_device:generate_test_message("main", capabilities.button.supportedButtonValues({ "pushed", "double", "held" }))) - test.socket.capability:__expect_send(matter_device:generate_test_message("button2", capabilities.button.supportedButtonValues({ "pushed", "double", "held" }))) - test.socket.capability:__expect_send(matter_device:generate_test_message("button3", capabilities.button.supportedButtonValues({ "pushed", "double", "held" }))) - test.socket.capability:__expect_send(matter_device:generate_test_message("button4", capabilities.button.supportedButtonValues({ "pushed", "double", "held" }))) -end - -local function initiate_info_changed(device, profile) - test.socket.device_lifecycle:__queue_receive(device:generate_info_changed({ profile = { id = profile } })) - test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") - test.mock_time.advance_time(3) -end - -local function configure_parent(device) - test.socket.device_lifecycle:__queue_receive({ device.id, "doConfigure" }) - device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) - test.socket.device_lifecycle:__queue_receive({ device.id, "init" }) - test.socket.matter:__expect_send({ - device.id, - cluster_base.subscribe(device, 2, descriptor.ID, descriptor.attributes.PartsList.ID, nil) - }) - test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") - test.mock_time.advance_time(3) -end - -local parent = create_parent_device(0x0006) -- 2G button product -local parent_1g = create_parent_device(0x0005) -- 1G button product -local parent_pir = create_parent_device(0x0007) -- PIR product - -local function test_init() - test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") -end - -test.set_test_init_function(test_init) - -test.register_coroutine_test("Test: 4-Button Device Detection - Profile Changes from matter-bridge to 4-button When Four Button Endpoints Present", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent) - local matter_device = create_matter_device("matter-bridge", parent) - add_matter_device(matter_device, parent, "4-button") - test.wait_for_events() - configure_parent(parent) - test.wait_for_events() - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - data_types.Uint16(10), - data_types.Uint16(11), - })) - }) - - local matter_device_parent_id = matter_device:get_field(PARENT_ID) - local matter_device_id = matter_device:get_field(MATTER_DEVICE_ID) - local parent_id = parent:get_field(PARENT_ID) - local parent_matter_device_id = parent:get_field(MATTER_DEVICE_ID) - - assert(matter_device_parent_id == parent.id, "link_host_and_subhub 1/4") - assert(matter_device_id == matter_device.id, "link_host_and_subhub 2/4") - assert(parent_id == parent.id, "link_host_and_subhub 3/4") - assert(parent_matter_device_id == matter_device.id, "link_host_and_subhub 4/4") - - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x08) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x09) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x0A) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x0B) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 8, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 9, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 10, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 11, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - -end) - -test.register_coroutine_test("Test: Button Event Handling - Pushed, Double Press, and Held Events on 4-Button Device", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent) - local matter_device = create_matter_device("4-button", parent) - add_matter_device(matter_device, parent, "4-button") - test.wait_for_events() - configure_parent(parent) - test.wait_for_events() - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - data_types.Uint16(10), - data_types.Uint16(11), - })) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x08) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x09) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x0A) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 0x0B) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 8, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 9, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 10, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 11, data_types.Array({ - { device_type = 0x000F, revision = 0x0003 } - })) - }) - test.wait_for_events() - initiate_info_changed(matter_device, "4-button") - - subscribe_switch_events(parent, { 8, 9, 10, 11 }) - button_supported_values(matter_device) - - - -- Test single press (pushed) on endpoint 8 (button1) - test.wait_for_events() - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 8, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 1 -- Single press - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("main", capabilities.button.button.pushed({ state_change = true }))) - - --Test double press on endpoint 8 (button1) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 8, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 2 - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("main", capabilities.button.button.double({ state_change = true }))) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.LongPress:build_test_event_report( - matter_device, 8, { - new_position = 1, - previous_position = 0, - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("main", capabilities.button.button.held({ state_change = true }))) - - -- Test press on endpoint 9 (button2) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 9, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 1 - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button2", capabilities.button.button.pushed({ state_change = true }))) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 9, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 2 - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button2", capabilities.button.button.double({ state_change = true }))) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.LongPress:build_test_event_report( - matter_device, 9, { - new_position = 1, - previous_position = 0, - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button2", capabilities.button.button.held({ state_change = true }))) - - -- Test press on endpoint 10 (button3) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 10, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 1 - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button3", capabilities.button.button.pushed({ state_change = true }))) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 10, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 2 - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button3", capabilities.button.button.double({ state_change = true }))) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.LongPress:build_test_event_report( - matter_device, 10, { - new_position = 1, - previous_position = 0, - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button3", capabilities.button.button.held({ state_change = true }))) - - -- Test press on endpoint 11 (button4) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 11, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 1 - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button4", capabilities.button.button.pushed({ state_change = true }))) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.MultiPressComplete:build_test_event_report( - matter_device, 11, { - new_position = 1, - previous_position = 1, - total_number_of_presses_counted = 2 - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button4", capabilities.button.button.double({ state_change = true }))) - test.socket.matter:__queue_receive({ - matter_device.id, - clusters.Switch.events.LongPress:build_test_event_report( - matter_device, 11, { - new_position = 1, - previous_position = 0, - } - ) - }) - test.socket.capability:__expect_send(matter_device:generate_test_message("button4", capabilities.button.button.held({ state_change = true }))) -end) - -test.register_coroutine_test("Test: Device Type Handler - Handles Button (Type 15) and OnOff (Type 256) Device Types with Child Creation", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent) - local matter_device = create_matter_device("4-button", parent) - add_matter_device(matter_device, parent, "4-button") - test.wait_for_events() - configure_parent(parent) - test.wait_for_events() - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(9), - data_types.Uint16(8), - data_types.Uint16(6), - })) - }) - - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 9) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 8) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 6) - }) - - test.wait_for_events() - - -- Receive DeviceTypeList report with device type 15 (button) for endpoint 8 - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 8, data_types.Array { - { - device_type = data_types.Uint32(15), - revision = data_types.Uint16(1) - } - }) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 9, data_types.Array { - { - device_type = data_types.Uint32(15), - revision = data_types.Uint16(1) - } - }) - }) - - -- Receive DeviceTypeList report with device type 256 (OnOff) for endpoint 6 - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 6, data_types.Array { - { - device_type = data_types.Uint32(256), - revision = data_types.Uint16(1) - } - }) - }) - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "light-binary", - parent_device_id = parent.id, - parent_assigned_child_key = "6" - }) - test.wait_for_events() - initiate_info_changed(matter_device, "2-button") - subscribe_switch_events(parent, { 8, 9 }) - test.socket.capability:__expect_send(matter_device:generate_test_message("main", capabilities.button.supportedButtonValues({ "pushed", "double", "held" }))) - test.socket.capability:__expect_send(matter_device:generate_test_message("button2", capabilities.button.supportedButtonValues({ "pushed", "double", "held" }))) - - test.wait_for_events() - - local child_switch = test.mock_device.build_test_child_device({ - profile = t_utils.get_profile_definition("light-binary.yml"), - device_network_id = string.format("%s:6", parent.id), - parent_device_id = parent.id, - parent_assigned_child_key = "6" - }) - test.wait_for_events() - initiate_info_changed(child_switch, "light-binary") - - test.socket.matter:__expect_send({ - parent.id, - clusters.OnOff.attributes.OnOff:subscribe(parent, nil) - }) -end) - -test.register_coroutine_test("Test: 2G Relay - Profile Changes Between light-binary and 2-button Based On Endpoint Availability", function() - test.socket.matter:__set_channel_ordering("relaxed") - test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") - test.timer.__create_and_queue_test_time_advance_timer(2, "oneshot") - add_parent_device(parent) - local relay = create_hager_2g_relay("light-binary", parent) - add_switch_matter_device(relay, parent) - test.wait_for_events() - configure_parent(parent) - test.wait_for_events() - - -- Scenario 1: EP3 + EP4 present → light-binary profile - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(3), - data_types.Uint16(4), - })) - }) - -- - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 3) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 4) - }) - - relay:expect_metadata_update({ profile = "light-binary" }) - relay:expect_metadata_update({ profile = "light-binary" }) - test.wait_for_events() - -- Both endpoints are device type 256 (OnOff) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 3, data_types.Array { - { - device_type = data_types.Uint32(256), - revision = data_types.Uint16(1) - } - }) - }) - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 4, data_types.Array { - { - device_type = data_types.Uint32(256), - revision = data_types.Uint16(1) - } - }) - }) - - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "light-binary", - parent_device_id = parent.id, - parent_assigned_child_key = "4" - }) - local child_switch = test.mock_device.build_test_child_device({ - profile = t_utils.get_profile_definition("light-binary.yml"), - device_network_id = string.format("%s:4", parent.id), - parent_device_id = parent.id, - parent_assigned_child_key = "4" - }) - - initiate_info_changed(relay, "light-binary") - test.socket.matter:__expect_send({ - parent.id, - clusters.OnOff.attributes.OnOff:subscribe(parent, nil) - }) - - initiate_info_changed(child_switch, "light-binary") - test.socket.matter:__expect_send({ - parent.id, - clusters.OnOff.attributes.OnOff:subscribe(parent, nil) - }) - - --Scenario 2: EP4 removed → profile changes to 2-button, child created for EP3 - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(3), - data_types.Uint16(8), - data_types.Uint16(9), - })) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 9) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 8) - }) - - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "light-binary", - parent_device_id = parent.id, - parent_assigned_child_key = "3" - }) - - relay:expect_metadata_update({ profile = "2-button" }) - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 8, data_types.Array { - { - device_type = data_types.Uint32(15), - revision = data_types.Uint16(1) - } - }) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 9, data_types.Array { - { - device_type = data_types.Uint32(15), - revision = data_types.Uint16(1) - } - }) - }) - - -- Scenario 3: EP4 reappears → profile changes back to light-binary - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(3), - data_types.Uint16(4), - })) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 4) - }) - relay:expect_metadata_update({ profile = "light-binary" }) - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 4, data_types.Array { - { - device_type = data_types.Uint32(256), - revision = data_types.Uint16(1) - } - }) - }) - - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "light-binary", - parent_device_id = parent.id, - parent_assigned_child_key = "4" - }) - - -- Scenario 4: EP3 removed → profile changes to 2-button, child created for EP4 - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(4), - data_types.Uint16(12), - data_types.Uint16(13), - })) - }) - - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 12) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 13) - }) - - relay:expect_metadata_update({ profile = "2-button" }) - - -- Scenario 5: EP3 and EP4 removed → 4-button profile - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - data_types.Uint16(10), - data_types.Uint16(11), - })) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 8) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 9) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 10) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 11) - }) - relay:expect_metadata_update({ profile = "4-button" }) - - -- Scenario 6: Only EP4 present → 2-button profile, child created - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(4), - data_types.Uint16(8), - data_types.Uint16(9), - })) - }) - - relay:expect_metadata_update({ profile = "2-button" }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 4) - }) - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 4, data_types.Array { - { - device_type = data_types.Uint32(256), - revision = data_types.Uint16(1) - } - }) - }) - - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "light-binary", - parent_device_id = parent.id, - parent_assigned_child_key = "4" - }) -end) - -test.register_coroutine_test("Test: Dimmer Device - Child Creation for Dimmable Endpoint with Button Support", function() - test.socket.matter:__set_channel_ordering("relaxed") - - add_parent_device(parent) - local dimmer = create_hager_dimmer_device_2g("2-button", parent) - add_matter_device(dimmer, parent, "2-button") - test.wait_for_events() - configure_parent(parent) - test.wait_for_events() - - --Scenario 1: 2 button endpoints (8, 9) detected → 2-button profile - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - data_types.Uint16(3) - })) - }) - - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 8) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 9) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 3) - }) - dimmer:expect_metadata_update({ profile = "2-button" }) - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 3, data_types.Array { - { - device_type = data_types.Uint32(257), - revision = data_types.Uint16(1) - } - }) - }) - - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "light-level", - parent_device_id = parent.id, - parent_assigned_child_key = "3" - }) - - -- Create mock child device for EP3 - local child_dimmer = test.mock_device.build_test_child_device({ - profile = t_utils.get_profile_definition("light-level.yml"), - device_network_id = string.format("%s:3", parent.id), - parent_device_id = parent.id, - parent_assigned_child_key = "3" - }) - initiate_info_changed(child_dimmer, "light-level") - - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.OnOff.ID, clusters.OnOff.attributes.OnOff.ID, nil) - }) - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.CurrentLevel.ID, nil) - }) - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MaxLevel.ID, nil) - }) - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MinLevel.ID, nil) - }) - - test.socket.capability:__queue_receive({ child_dimmer.id, { capability = "switch", component = "main", command = "on", args = {} } }) - child_dimmer.expect_native_cmd_handler_registration(child_dimmer, "switch", "on") - - test.socket.matter:__expect_send({ - parent.id, - clusters.OnOff.server.commands.On(child_dimmer, 3) - }) - test.socket.capability:__queue_receive({ child_dimmer.id, { capability = "switchLevel", component = "main", command = "setLevel", args = { 20 } } }) - - child_dimmer.expect_native_cmd_handler_registration(child_dimmer, "switchLevel", "setLevel") - - test.socket.matter:__expect_send({ - parent.id, - clusters.LevelControl.server.commands.MoveToLevelWithOnOff(child_dimmer, 3, 51, nil, 0, 0) - }) - -end) - -test.register_coroutine_test("Test: 1G Dimmer - Initialization and Profile Update to light-level", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent) - local dimmer = create_hager_dimmer_device_2g("2-button", parent) - add_matter_device(dimmer, parent, "2-button") - test.wait_for_events() - configure_parent(parent) -end) - -test.register_coroutine_test("Test: 1G Dimmer - Host Commands and Level Control Capabilities", function() - test.socket.matter:__set_channel_ordering("relaxed") - test.timer.__create_and_queue_test_time_advance_timer(3, "oneshot") - - add_parent_device(parent_1g) - local dimmer = create_hager_dimmer_device_1g("light-level", parent_1g) - add_matter_device(dimmer, parent_1g, "light-level") - test.wait_for_events() - configure_parent(parent_1g) - - -- Send dimmable endpoint 4 detection (device type 257) to trigger profile change and subscriptions - test.socket.matter:__queue_receive({ - parent_1g.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent_1g, 2, data_types.Array({ - data_types.Uint16(4), - })) - }) - - test.socket.matter:__expect_send({ - parent_1g.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent_1g, 4) - }) - - test.wait_for_events() - - test.socket.matter:__queue_receive({ - parent_1g.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent_1g, 4, data_types.Array { - { - device_type = data_types.Uint32(257), - revision = data_types.Uint16(1) - } - }) - }) - - ----subscriptions from device_type_handler - - initiate_info_changed(dimmer, "light-level") - test.socket.matter:__expect_send({ - parent_1g.id, - cluster_base.subscribe(parent_1g, nil, clusters.OnOff.ID, clusters.OnOff.attributes.OnOff.ID, nil) - }) - test.socket.matter:__expect_send({ - parent_1g.id, - cluster_base.subscribe(parent_1g, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.CurrentLevel.ID, nil) - }) - test.socket.matter:__expect_send({ - parent_1g.id, - cluster_base.subscribe(parent_1g, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MaxLevel.ID, nil) - }) - test.socket.matter:__expect_send({ - parent_1g.id, - cluster_base.subscribe(parent_1g, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MinLevel.ID, nil) - }) - - test.socket.capability:__queue_receive({ dimmer.id, { capability = "switch", component = "main", command = "on", args = {} } }) - dimmer.expect_native_cmd_handler_registration(dimmer, "switch", "on") - - test.socket.matter:__expect_send({ - parent_1g.id, - clusters.OnOff.server.commands.On(dimmer, 4) - }) - test.socket.capability:__queue_receive({ dimmer.id, { capability = "switchLevel", component = "main", command = "setLevel", args = { 20 } } }) - - dimmer.expect_native_cmd_handler_registration(dimmer, "switchLevel", "setLevel") - - test.socket.matter:__expect_send({ - parent_1g.id, - clusters.LevelControl.server.commands.MoveToLevelWithOnOff(dimmer, 4, 51, nil, 0, 0) - }) -end) - -test.register_coroutine_test("Test: PIR Device - Initialization with Motion and Illuminance Capabilities", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent_pir) - local pir_device = create_hager_pir_device("motion-illuminance", parent_pir) - add_matter_device(pir_device, parent_pir, "motion-illuminance") - test.wait_for_events() - configure_parent(parent_pir) - -end) - -test.register_coroutine_test("Test: PIR Device - Complete Functionality with Motion, Illuminance, and Dimmer Support", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent_pir) - local pir_device = create_hager_pir_device("motion-illuminance", parent_pir) - add_matter_device(pir_device, parent_pir, "motion-illuminance") - test.wait_for_events() - - configure_parent(parent_pir) - - test.wait_for_events() - - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent_pir, 2, data_types.Array({ - data_types.Uint16(3), - data_types.Uint16(4), - data_types.Uint16(5) - - })) - }) - test.socket.matter:__expect_send({ - parent_pir.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent_pir, 3) - }) - test.socket.matter:__expect_send({ - parent_pir.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent_pir, 4) - }) - test.socket.matter:__expect_send({ - parent_pir.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent_pir, 5) - }) - - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent_pir, 4, data_types.Array { - { - device_type = data_types.Uint32(263), - revision = data_types.Uint16(1) - } - }) - }) - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent_pir, 5, data_types.Array { - { - device_type = data_types.Uint32(262), - revision = data_types.Uint16(1) - } - }) - }) - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent_pir, 3, data_types.Array { - { - device_type = data_types.Uint32(257), - revision = data_types.Uint16(1) - } - }) - }) - - parent_pir:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "light-level", - parent_device_id = parent_pir.id, - parent_assigned_child_key = "3" - }) - test.wait_for_events() - initiate_info_changed(pir_device, "motion-illuminance") - test.socket.matter:__expect_send({ - parent_pir.id, - cluster_base.subscribe(parent_pir, nil, clusters.IlluminanceMeasurement.ID, clusters.IlluminanceMeasurement.attributes.MeasuredValue.ID, nil) - }) - test.socket.matter:__expect_send({ - parent_pir.id, - cluster_base.subscribe(parent_pir, nil, clusters.OccupancySensing.ID, clusters.OccupancySensing.attributes.Occupancy.ID, nil) - }) - - local child_dimmer = test.mock_device.build_test_child_device({ - label = "Hager G2 4x Button 1", - profile = t_utils.get_profile_definition("light-level.yml"), - device_network_id = string.format("%s:3", parent_pir.id), - parent_device_id = parent_pir.id, - parent_assigned_child_key = "3" - }) - - test.mock_device.add_test_device(child_dimmer) - initiate_info_changed(child_dimmer, "light-level") - test.socket.matter:__expect_send({ - parent_pir.id, - cluster_base.subscribe(parent_pir, nil, clusters.OnOff.ID, clusters.OnOff.attributes.OnOff.ID, nil) - }) - test.socket.matter:__expect_send({ - parent_pir.id, - cluster_base.subscribe(parent_pir, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.CurrentLevel.ID, nil) - }) - test.socket.matter:__expect_send({ - parent_pir.id, - cluster_base.subscribe(parent_pir, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MaxLevel.ID, nil) - }) - test.socket.matter:__expect_send({ - parent_pir.id, - cluster_base.subscribe(parent_pir, nil, clusters.LevelControl.ID, clusters.LevelControl.attributes.MinLevel.ID, nil) - }) - test.wait_for_events() - --Verify motion detected event - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.OccupancySensing.attributes.Occupancy:build_test_report_data(parent_pir, 6, 1) - }) - - test.socket.capability:__expect_send(pir_device:generate_test_message("main", capabilities.motionSensor.motion.active())) - - -- Verify illuminance measurement event - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.IlluminanceMeasurement.attributes.MeasuredValue:build_test_report_data(parent_pir, 5, 21370) - }) - test.socket.capability:__expect_send(pir_device:generate_test_message("main", capabilities.illuminanceMeasurement.illuminance(137))) - - -- Send on command to OnOff endpoint (dimmer) - child_dimmer.expect_native_cmd_handler_registration(child_dimmer, "switch", "on") - - test.socket.capability:__queue_receive({ child_dimmer.id, { capability = "switch", component = "main", command = "on", args = {} } }) - test.socket.matter:__expect_send({ - parent_pir.id, - clusters.OnOff.commands.On(parent_pir, 3) - }) - -- Verify on state via attribute report - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.OnOff.attributes.OnOff:build_test_report_data(parent_pir, 3, true) - }) - test.socket.capability:__expect_send(child_dimmer:generate_test_message("main", capabilities.switch.switch.on())) - parent_pir.expect_native_attr_handler_registration(parent_pir, "switch", "switch") - - -- Set dimmer level to 50% - test.socket.capability:__queue_receive({ child_dimmer.id, { capability = "switchLevel", component = "main", command = "setLevel", args = { 50 } } }) - test.socket.matter:__expect_send({ - parent_pir.id, - clusters.LevelControl.commands.MoveToLevelWithOnOff(parent_pir, 3, 127, nil, 0, 0) - }) - child_dimmer.expect_native_cmd_handler_registration(child_dimmer, "switchLevel", "setLevel") - - -- Verify level via attribute report - test.socket.matter:__queue_receive({ - parent_pir.id, - clusters.LevelControl.attributes.CurrentLevel:build_test_report_data(parent_pir, 3, 127) - }) - test.socket.capability:__expect_send(child_dimmer:generate_test_message("main", capabilities.switchLevel.level(50))) - parent_pir.expect_native_attr_handler_registration(parent_pir, "switchLevel", "level") -end) - -test.register_coroutine_test("Test: Host with Window Covering - 2-Button Profile with Window Covering Child Device", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent) - local window_covering_device = create_matter_device_with_window("2-button", parent) - add_matter_device(window_covering_device, parent, "2-button") - test.wait_for_events() - configure_parent(parent) - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - data_types.Uint16(12), - })) - }) - - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 8) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 9) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 12) - }) - - -- DeviceTypeList report for window covering endpoint (type 514 = Window Covering Device) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 12, data_types.Array { - { - device_type = data_types.Uint32(514), - revision = data_types.Uint16(1) - } - }) - }) - - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "window-covering", - parent_device_id = parent.id, - parent_assigned_child_key = "12" - }) - - local child_wc = test.mock_device.build_test_child_device({ - profile = t_utils.get_profile_definition("window-covering.yml"), - device_network_id = string.format("%s:12", parent.id), - parent_device_id = parent.id, - parent_assigned_child_key = "12" - }) - - test.mock_device.add_test_device(child_wc) - initiate_info_changed(child_wc, "window-covering") - - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.OperationalStatus.ID, nil) - }) - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths.ID, nil) - }) - - -- Open command - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShade", component = "main", command = "open", args = {} } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.UpOrOpen(parent, 12) }) - test.wait_for_events() - - -- Verify open state via attribute report - test.socket.matter:__queue_receive({ - parent.id, - clusters.WindowCovering.attributes.OperationalStatus:build_test_report_data(parent, 12, 0x01) - }) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShade.windowShade.opening())) - - - -- Close command - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShade", component = "main", command = "close", args = {} } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.DownOrClose(parent, 12) }) - - -- Verify close state via attribute report - test.socket.matter:__queue_receive({ - parent.id, - clusters.WindowCovering.attributes.OperationalStatus:build_test_report_data(parent, 12, 0x02) - }) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShade.windowShade.closing())) - - -- Pause command - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShade", component = "main", command = "pause", args = {} } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.StopMotion(parent, 12) }) - test.wait_for_events() - - -- Set shade level to 50% - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShadeLevel", component = "main", command = "setShadeLevel", args = { 50 } } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.GoToLiftPercentage(parent, 12, 5000, nil, 0, 0) }) - test.wait_for_events() - - -- Verify shade level via attribute report - test.socket.matter:__queue_receive({ - parent.id, - clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths:build_test_report_data(parent, 12, 5000) - }) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShadeLevel.shadeLevel(50))) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShade.windowShade.partially_open())) - - - -- Set shade level to 100% - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShadeLevel", component = "main", command = "setShadeLevel", args = { 100 } } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.GoToLiftPercentage(parent, 12, 0, nil, 0, 0) }) - test.wait_for_events() - - -- Verify shade level via attribute report - test.socket.matter:__queue_receive({ - parent.id, - clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths:build_test_report_data(parent, 12, 0) - }) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShadeLevel.shadeLevel(100))) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShade.windowShade.open())) - - -- Set shade level to 0% - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShadeLevel", component = "main", command = "setShadeLevel", args = { 0 } } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.GoToLiftPercentage(parent, 12, 10000, nil, 0, 0) }) - - -- Verify shade level via attribute report - test.socket.matter:__queue_receive({ - parent.id, - clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths:build_test_report_data(parent, 12, 10000) - }) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShadeLevel.shadeLevel(0))) - test.socket.capability:__expect_send(child_wc:generate_test_message("main", capabilities.windowShade.windowShade.closed())) - -end) - -test.register_coroutine_test("Test: Window Covering - Preference Changes for Reverse Polarity and Preset Position", function() - test.socket.matter:__set_channel_ordering("relaxed") - add_parent_device(parent) - local window_covering_device = create_matter_device_with_window("2-button", parent) - add_matter_device(window_covering_device, parent, "2-button") - test.wait_for_events() - configure_parent(parent) - - test.wait_for_events() - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - data_types.Uint16(12), - })) - }) - -- - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 8) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 9) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 12) - }) - test.wait_for_events() - - -- DeviceTypeList reports for button endpoints (type 15 = Generic Switch) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 8, data_types.Array { - { - device_type = data_types.Uint32(15), - revision = data_types.Uint16(1) - } - }) - }) - - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 9, data_types.Array { - { - device_type = data_types.Uint32(15), - revision = data_types.Uint16(1) - } - }) - }) - - -- DeviceTypeList report for window covering endpoint (type 514 = Window Covering Device) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 12, data_types.Array { - { - device_type = data_types.Uint32(514), - revision = data_types.Uint16(1) - } - }) - }) - - parent:expect_device_create({ - type = "EDGE_CHILD", - label = "Hager G2 4x Button 1", - profile = "window-covering", - parent_device_id = parent.id, - parent_assigned_child_key = "12" - }) - - local child_wc = test.mock_device.build_test_child_device({ - profile = t_utils.get_profile_definition("window-covering.yml"), - device_network_id = string.format("%s:12", parent.id), - parent_device_id = parent.id, - parent_assigned_child_key = "12" - }) - test.mock_device.add_test_device(child_wc) - - test.socket.device_lifecycle():__queue_receive(child_wc:generate_info_changed({ preferences = { reverse = "false" } })) - test.socket.device_lifecycle():__queue_receive(child_wc:generate_info_changed({ preferences = { reverse = "true" } })) - test.wait_for_events() - local reverse_preference_set = child_wc.preferences.reverse - assert(reverse_preference_set == "true", "reverse_preference_set is True") - -- - --Send open command - with reverse_polarity true, this should send DownOrClose - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShade", component = "main", command = "open", args = {} } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.DownOrClose(parent, 12) }) - - -- Send close command - with reverse_polarity true, this should send UpOrOpen - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShade", component = "main", command = "close", args = {} } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.UpOrOpen(parent, 12) }) - - -- Send Pause command - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShade", component = "main", command = "pause", args = {} } }) - test.socket.matter:__expect_send({ parent.id, clusters.WindowCovering.commands.StopMotion(parent, 12) }) - - -- Position preset testing - test.socket.device_lifecycle():__queue_receive(child_wc:generate_info_changed({ preferences = { presetPosition = "50" } })) - test.socket.device_lifecycle():__queue_receive(child_wc:generate_info_changed({ preferences = { presetPosition = "20" } })) - - test.wait_for_events() - - local PRESET_LEVEL_KEY = child_wc.preferences.presetPosition - assert(PRESET_LEVEL_KEY == "20", " presetPosition is set to 20") - - test.socket.capability:__queue_receive({ child_wc.id, { capability = "windowShadePreset", component = "main", command = "presetPosition", args = {} } }) - test.socket.matter:__expect_send( - { parent.id, clusters.WindowCovering.server.commands.GoToLiftPercentage(parent, 12, 8000) } - ) - - test.wait_for_events() - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - })) - }) - - test.wait_for_events() - - test.timer.__create_and_queue_test_time_advance_timer(5, "oneshot") - test.mock_time.advance_time(5) - local BUTTON_EPS_FIELD = parent:get_field(BUTTON_EPS) - assert(BUTTON_EPS_FIELD[1] == 8) - assert(BUTTON_EPS_FIELD[2] == 9) - - test.wait_for_events() - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.PartsList:build_test_report_data(parent, 2, data_types.Array({ - data_types.Uint16(8), - data_types.Uint16(9), - data_types.Uint16(12), - })) - }) - test.socket.matter:__expect_send({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:subscribe(parent, 12) - }) - test.socket.matter:__queue_receive({ - parent.id, - clusters.Descriptor.attributes.DeviceTypeList:build_test_report_data(parent, 12, data_types.Array { - { - device_type = data_types.Uint32(514), - revision = data_types.Uint16(1) - } - }) - }) - child_wc:expect_metadata_update({ profile = "window-covering" }) - initiate_info_changed(child_wc, "window-covering") - - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.OperationalStatus.ID, nil) - }) - test.socket.matter:__expect_send({ - parent.id, - cluster_base.subscribe(parent, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths.ID, nil) - }) -end) - -test.run_registered_tests()