From 83380a08852a7f1d3f8e5d3f2ef7f5907cda8f43 Mon Sep 17 00:00:00 2001 From: simon3panda <340443303@qq.com> Date: Fri, 14 Nov 2025 17:19:19 +0800 Subject: [PATCH 1/6] Linxura Aura Smart Button Zigbee mode support 12 buttons and battery report --- .../zigbee-button/fingerprints.yml | 2 +- .../twelve-buttons-without-main-button.yml | 82 +++++++++++++++++++ ..._linxura_aura_smart_button_12x_button.lua} | 8 +- .../src/zigbee-multi-button/linxura/init.lua | 62 +++++++++++++- .../zigbee-multi-button/supported_values.lua | 8 +- 5 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 drivers/SmartThings/zigbee-button/profiles/twelve-buttons-without-main-button.yml rename drivers/SmartThings/zigbee-button/src/test/{test_linxura_aura_smart_button.lua => test_linxura_aura_smart_button_12x_button.lua} (95%) diff --git a/drivers/SmartThings/zigbee-button/fingerprints.yml b/drivers/SmartThings/zigbee-button/fingerprints.yml index 61285448f7..7b2b348b3b 100644 --- a/drivers/SmartThings/zigbee-button/fingerprints.yml +++ b/drivers/SmartThings/zigbee-button/fingerprints.yml @@ -244,7 +244,7 @@ zigbeeManufacturer: deviceLabel: Aura Smart Button manufacturer: Linxura model: Aura Smart Button - deviceProfileName: four-buttons-without-main-button + deviceProfileName: twelve-buttons-without-main-button isJoinable: true # Wall Hero - id: "WALL HERO/ACL-401SCA4" diff --git a/drivers/SmartThings/zigbee-button/profiles/twelve-buttons-without-main-button.yml b/drivers/SmartThings/zigbee-button/profiles/twelve-buttons-without-main-button.yml new file mode 100644 index 0000000000..adbe68a733 --- /dev/null +++ b/drivers/SmartThings/zigbee-button/profiles/twelve-buttons-without-main-button.yml @@ -0,0 +1,82 @@ +name: twelve-buttons-without-main-button +components: + - id: main + capabilities: + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: RemoteController + - id: button1 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button2 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button3 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button4 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button5 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button6 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button7 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button8 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button9 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button10 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button11 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController + - id: button12 + capabilities: + - id: button + version: 1 + categories: + - name: RemoteController diff --git a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button.lua b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua similarity index 95% rename from drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button.lua rename to drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua index 4b9ca6dfd9..628f703901 100644 --- a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button.lua +++ b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua @@ -25,7 +25,7 @@ local button_attr = capabilities.button.button local mock_device = test.mock_device.build_test_zigbee_device( { - profile = t_utils.get_profile_definition("four-buttons-without-main-button.yml"), + profile = t_utils.get_profile_definition("twelve-buttons-without-main-button.yml"), zigbee_endpoints = { [1] = { id = 1, @@ -72,7 +72,7 @@ test.register_coroutine_test( test.register_coroutine_test( "Test cases for Buttons Pushed", function() - for var = 0, 3 do + for var = 0, 11 do test.socket.zigbee:__queue_receive({ mock_device.id, ZoneStatusAttribute:build_test_attr_report(mock_device, 1 + var * 6) @@ -88,7 +88,7 @@ test.register_coroutine_test( test.register_coroutine_test( "Test cases for Buttons Double", function() - for var = 0, 3 do + for var = 0, 11 do test.socket.zigbee:__queue_receive({ mock_device.id, ZoneStatusAttribute:build_test_attr_report(mock_device, 3 + var * 6) @@ -105,7 +105,7 @@ test.register_coroutine_test( test.register_coroutine_test( "Test cases for Buttons Held", function() - for var = 0, 3 do + for var = 0, 11 do test.socket.zigbee:__queue_receive({ mock_device.id, ZoneStatusAttribute:build_test_attr_report(mock_device, 5 + var * 6) diff --git a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua index 0e7cf44e93..f86969290b 100644 --- a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua +++ b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua @@ -13,6 +13,10 @@ -- limitations under the License. local capabilities = require "st.capabilities" local IASZone = (require "st.zigbee.zcl.clusters").IASZone +local zcl_clusters = require "st.zigbee.zcl.clusters" +local PowerConfiguration = zcl_clusters.PowerConfiguration +local supported_values = require "zigbee-multi-button.supported_values" + local log = require "log" @@ -29,6 +33,14 @@ local configuration = { maximum_interval = 3600, data_type = IASZone.attributes.ZoneStatus.base_type, reportable_change = 1 + }, + { + cluster = PowerConfiguration.ID, + attribute = PowerConfiguration.attributes.BatteryPercentageRemaining.ID, + minimum_interval = 0, + maximum_interval = 3600, + data_type = PowerConfiguration.attributes.BatteryPercentageRemaining.base_type, + reportable_change = 2 } } local is_linxura_button = function(opts, driver, device) @@ -56,6 +68,8 @@ local function present_value_attr_handler(driver, device, zone_status, zb_rx) event = capabilities.button.button.double(additional_fields) elseif mod == 5 then event = capabilities.button.button.held(additional_fields) + else + return false end if (event) then @@ -64,22 +78,68 @@ local function present_value_attr_handler(driver, device, zone_status, zb_rx) end end +local function battery_attr_handler(driver, device, value, zb_rx) + local raw = value.value + if raw == nil then return end + + local pct = nil + if raw == 0xFF then + log.info("BatteryPercentageRemaining is unknown (0xFF)") + else + pct = math.floor(math.max(0, math.min(200, raw)) / 2) + end + + if pct then + device:emit_event(capabilities.battery.battery(pct)) + else + end +end local function device_init(driver, device) for _, attribute in ipairs(configuration) do device:add_configured_attribute(attribute) end end +local function device_added(driver, device) + local config = supported_values.get_device_parameters(device) + for _, component in pairs(device.profile.components) do + if config ~= nil then + local number_of_buttons = component.id == "main" and config.NUMBER_OF_BUTTONS or 1 + device:emit_component_event(component, + capabilities.button.supportedButtonValues(config.SUPPORTED_BUTTON_VALUES, { visibility = { displayed = false } })) + device:emit_component_event(component, + capabilities.button.numberOfButtons({ value = number_of_buttons }, { visibility = { displayed = false } })) + else + device:emit_component_event(component, + capabilities.button.supportedButtonValues({ "pushed", "held" }, { visibility = { displayed = false } })) + device:emit_component_event(component, + capabilities.button.numberOfButtons({ value = 1 }, { visibility = { displayed = false } })) + end + end + device:emit_event(capabilities.button.button.pushed({state_change = false})) + + device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:read(device)) +end + +local function do_configure(driver, device) + device:configure() + device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:read(device)) +end local linxura_device_handler = { NAME = "Linxura Device Handler", lifecycle_handlers = { - init = device_init + init = device_init, + added = device_added, + doConfigure = do_configure, }, zigbee_handlers = { attr = { [IASZone.ID] = { [IASZone.attributes.ZoneStatus.ID] = present_value_attr_handler + }, + [PowerConfiguration.ID] = { + [PowerConfiguration.attributes.BatteryPercentageRemaining.ID] = battery_attr_handler } } }, diff --git a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/supported_values.lua b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/supported_values.lua index 172a7c1ca3..a7418621b7 100644 --- a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/supported_values.lua +++ b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/supported_values.lua @@ -119,11 +119,17 @@ local devices = { { mfr = "ShinaSystem", model = "SBM300ZC4" }, { mfr = "ShinaSystem", model = "SQM300ZC4" }, { mfr = "Linxura", model = "Smart Controller" }, - { mfr = "Linxura", model = "Aura Smart Button" } }, SUPPORTED_BUTTON_VALUES = { "pushed", "held", "double" }, NUMBER_OF_BUTTONS = 4 }, + BUTTON_PUSH_HELD_DOUBLE_12 = { + MATCHING_MATRIX = { + { mfr = "Linxura", model = "Aura Smart Button" } + }, + SUPPORTED_BUTTON_VALUES = { "pushed", "held", "double" }, + NUMBER_OF_BUTTONS = 12 + }, BUTTON_PUSH_DOWN_HOLD_UP_VIMAR_2 = { MATCHING_MATRIX = { { mfr = "Vimar", model = "RemoteControl_v1.0" } From a79da54303fdb72d4db2a2186a4fcd2e49eaf3a1 Mon Sep 17 00:00:00 2001 From: simon3panda <340443303@qq.com> Date: Fri, 14 Nov 2025 17:31:43 +0800 Subject: [PATCH 2/6] Update test_linxura_aura_smart_button_12x_button.lua --- .../src/test/test_linxura_aura_smart_button_12x_button.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua index 628f703901..df622891f9 100644 --- a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua +++ b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua @@ -19,6 +19,7 @@ local IASZone = clusters.IASZone local capabilities = require "st.capabilities" local zigbee_test_utils = require "integration_test.zigbee_test_utils" local t_utils = require "integration_test.utils" +local PowerConfiguration = clusters.PowerConfiguration local ZoneStatusAttribute = IASZone.attributes.ZoneStatus local button_attr = capabilities.button.button @@ -31,7 +32,7 @@ local mock_device = test.mock_device.build_test_zigbee_device( id = 1, manufacturer = "Linxura", model = "Aura Smart Button", - server_clusters = {0x0500, 0x0000} + server_clusters = {0x0001, 0x0500, 0x0000} } } } From 4c12cac20cce8a21d065e9375355091862419065 Mon Sep 17 00:00:00 2001 From: simon3panda <340443303@qq.com> Date: Fri, 14 Nov 2025 18:05:07 +0800 Subject: [PATCH 3/6] Update init.lua --- .../zigbee-button/src/zigbee-multi-button/linxura/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua index f86969290b..6da0423c27 100644 --- a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua +++ b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua @@ -118,7 +118,7 @@ local function device_added(driver, device) end device:emit_event(capabilities.button.button.pushed({state_change = false})) - device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:read(device)) + -- device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:read(device)) end local function do_configure(driver, device) From c21e24ca064b4dfd50f7137120e3e4391c0126d3 Mon Sep 17 00:00:00 2001 From: simon3panda <340443303@qq.com> Date: Fri, 14 Nov 2025 18:27:09 +0800 Subject: [PATCH 4/6] remove warning. --- .../src/test/test_linxura_aura_smart_button_12x_button.lua | 5 +++++ .../zigbee-button/src/zigbee-multi-button/linxura/init.lua | 7 +++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua index df622891f9..e0d297a937 100644 --- a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua +++ b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua @@ -47,7 +47,12 @@ test.set_test_init_function(test_init) test.register_coroutine_test( "added lifecycle event", function() + test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" }) test.socket.capability:__set_channel_ordering("relaxed") + test.socket.zigbee:__expect_send({ + mock_device.id, + PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device) + }) for button_name, _ in pairs(mock_device.profile.components) do if button_name ~= "main" then test.socket.capability:__expect_send( diff --git a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua index 6da0423c27..220de89d8a 100644 --- a/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua +++ b/drivers/SmartThings/zigbee-button/src/zigbee-multi-button/linxura/init.lua @@ -37,10 +37,10 @@ local configuration = { { cluster = PowerConfiguration.ID, attribute = PowerConfiguration.attributes.BatteryPercentageRemaining.ID, - minimum_interval = 0, - maximum_interval = 3600, + minimum_interval = 0, + maximum_interval = 3600, data_type = PowerConfiguration.attributes.BatteryPercentageRemaining.base_type, - reportable_change = 2 + reportable_change = 2 } } local is_linxura_button = function(opts, driver, device) @@ -91,7 +91,6 @@ local function battery_attr_handler(driver, device, value, zb_rx) if pct then device:emit_event(capabilities.battery.battery(pct)) - else end end local function device_init(driver, device) From 82108c5de701668d328a0b1928b776b28ae7fd40 Mon Sep 17 00:00:00 2001 From: simon3panda <340443303@qq.com> Date: Fri, 14 Nov 2025 18:33:46 +0800 Subject: [PATCH 5/6] Update test_linxura_aura_smart_button_12x_button.lua --- .../src/test/test_linxura_aura_smart_button_12x_button.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua index e0d297a937..df622891f9 100644 --- a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua +++ b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua @@ -47,12 +47,7 @@ test.set_test_init_function(test_init) test.register_coroutine_test( "added lifecycle event", function() - test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" }) test.socket.capability:__set_channel_ordering("relaxed") - test.socket.zigbee:__expect_send({ - mock_device.id, - PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device) - }) for button_name, _ in pairs(mock_device.profile.components) do if button_name ~= "main" then test.socket.capability:__expect_send( From d5649132e3ac8191cc6c74a68ba410bb643f62e4 Mon Sep 17 00:00:00 2001 From: simon3panda <340443303@qq.com> Date: Fri, 14 Nov 2025 18:37:47 +0800 Subject: [PATCH 6/6] Update test_linxura_aura_smart_button_12x_button.lua --- .../src/test/test_linxura_aura_smart_button_12x_button.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua index df622891f9..732888b727 100644 --- a/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua +++ b/drivers/SmartThings/zigbee-button/src/test/test_linxura_aura_smart_button_12x_button.lua @@ -19,7 +19,6 @@ local IASZone = clusters.IASZone local capabilities = require "st.capabilities" local zigbee_test_utils = require "integration_test.zigbee_test_utils" local t_utils = require "integration_test.utils" -local PowerConfiguration = clusters.PowerConfiguration local ZoneStatusAttribute = IASZone.attributes.ZoneStatus local button_attr = capabilities.button.button