Skip to content

Commit d5fad7a

Browse files
authored
Merge branch 'main' into inovelli-vzw31-sn
2 parents 2bc99e6 + 09cb31c commit d5fad7a

File tree

6 files changed

+466
-0
lines changed

6 files changed

+466
-0
lines changed

drivers/SmartThings/zigbee-button/fingerprints.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ zigbeeManufacturer:
3434
manufacturer: LUMI
3535
model: lumi.remote.b28ac1
3636
deviceProfileName: aqara-double-buttons-mode
37+
- id: "lumi/lumi.remote.rkba01"
38+
deviceLabel: "Aqara Wireless Smart Knob H1"
39+
manufacturer: LUMI
40+
model: lumi.remote.rkba01
41+
deviceProfileName: aqara-knob-switch
3742
- id: "HEIMAN/SOS-EM"
3843
deviceLabel: HEIMAN Button
3944
manufacturer: HEIMAN
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: aqara-knob-switch
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: button
6+
version: 1
7+
- id: knob
8+
version: 1
9+
- id: batteryLevel
10+
version: 1
11+
- id: firmwareUpdate
12+
version: 1
13+
- id: refresh
14+
version: 1
15+
categories:
16+
- name: Button
17+
preferences:
18+
- preferenceId: stse.knobSensitivity
19+
explicit: true
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- Copyright 2026 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
3+
4+
local is_aqara_products = function(opts, driver, device, ...)
5+
local FINGERPRINTS = { mfr = "LUMI", model = "lumi.remote.rkba01" }
6+
7+
if device:get_manufacturer() == FINGERPRINTS.mfr and device:get_model() == FINGERPRINTS.model then
8+
return true, require("aqara-knob")
9+
end
10+
return false
11+
end
12+
13+
return is_aqara_products
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
-- Copyright 2026 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
3+
4+
local battery_defaults = require "st.zigbee.defaults.battery_defaults"
5+
local clusters = require "st.zigbee.zcl.clusters"
6+
local cluster_base = require "st.zigbee.cluster_base"
7+
local data_types = require "st.zigbee.data_types"
8+
local capabilities = require "st.capabilities"
9+
local utils = require "st.utils"
10+
local button_utils = require "button_utils"
11+
12+
local PowerConfiguration = clusters.PowerConfiguration
13+
local PRIVATE_CLUSTER_ID = 0xFCC0
14+
local PRIVATE_ATTRIBUTE_ID = 0x0009
15+
local MFG_CODE = 0x115F
16+
local MULTISTATE_INPUT_CLUSTER_ID = 0x0012
17+
local PRESENT_ATTRIBUTE_ID = 0x0055
18+
local ROTATION_MONITOR_ID = 0x0232
19+
20+
local AQARA_KNOB = {
21+
["lumi.remote.rkba01"] = { mfr = "LUMI", type = "CR2032", quantity = 2 }, -- Aqara Wireless Knob Switch H1
22+
}
23+
24+
local function device_init(driver, device)
25+
local configuration = {
26+
{
27+
cluster = PowerConfiguration.ID,
28+
attribute = PowerConfiguration.attributes.BatteryVoltage.ID,
29+
minimum_interval = 30,
30+
maximum_interval = 3600,
31+
data_type = PowerConfiguration.attributes.BatteryVoltage.base_type,
32+
reportable_change = 1
33+
}
34+
}
35+
36+
battery_defaults.build_linear_voltage_init(2.6, 3.0)(driver, device)
37+
for _, attribute in ipairs(configuration) do
38+
device:add_configured_attribute(attribute)
39+
end
40+
end
41+
42+
local function device_added(self, device)
43+
local model = device:get_model()
44+
local type = AQARA_KNOB[model].type or "CR2032"
45+
local quantity = AQARA_KNOB[model].quantity or 1
46+
47+
device:emit_event(capabilities.button.supportedButtonValues({ "pushed", "held", "double" }, { visibility = { displayed = false } }))
48+
device:emit_event(capabilities.button.numberOfButtons({ value = 1 }))
49+
button_utils.emit_event_if_latest_state_missing(device, "main", capabilities.button,
50+
capabilities.button.button.NAME, capabilities.button.button.pushed({state_change = false}))
51+
device:emit_event(capabilities.batteryLevel.battery.normal())
52+
device:emit_event(capabilities.batteryLevel.type(type))
53+
device:emit_event(capabilities.batteryLevel.quantity(quantity))
54+
device:emit_event(capabilities.knob.rotateAmount(0))
55+
device:emit_event(capabilities.knob.heldRotateAmount(0))
56+
end
57+
58+
local function do_configure(driver, device)
59+
device:configure()
60+
-- Set manufacturer-specific attribute to enable "Operation Mode" for rotation reports
61+
device:send(cluster_base.write_manufacturer_specific_attribute(device,
62+
PRIVATE_CLUSTER_ID, PRIVATE_ATTRIBUTE_ID, MFG_CODE, data_types.Uint8, 1))
63+
device:emit_event(capabilities.knob.supportedAttributes({"rotateAmount", "heldRotateAmount"}, {state_change = true}))
64+
end
65+
66+
local function button_monitor_handler(driver, device, value, zb_rx)
67+
local val = value.value
68+
69+
if val == 1 then -- push
70+
device:emit_event(capabilities.button.button.pushed({ state_change = true }))
71+
elseif val == 2 then -- double push
72+
device:emit_event(capabilities.button.button.double({ state_change = true }))
73+
elseif val == 0 then -- down_hold
74+
device:emit_event(capabilities.button.button.held({ state_change = true }))
75+
end
76+
end
77+
78+
local function rotation_monitor_per_handler(driver, device, value, zb_rx)
79+
local SENSITIVITY_KEY = "stse.knobSensitivity"
80+
local SENSITIVITY_FACTORS = {0.5, 1.0, 2.0}
81+
82+
local end_point = zb_rx.address_header.src_endpoint.value
83+
local raw_val = utils.round(value.value)
84+
85+
if raw_val > 0x7FFF then
86+
raw_val = raw_val - 0x10000
87+
end
88+
89+
local sensitivity = tonumber(device.preferences[SENSITIVITY_KEY])
90+
local factor = SENSITIVITY_FACTORS[sensitivity] or 1.0
91+
local intermediate_val = raw_val * factor
92+
local sign = (intermediate_val > 0 and 1) or (intermediate_val < 0 and -1) or 0
93+
local val = math.floor(math.abs(intermediate_val) + 0.5) * sign
94+
val = math.max(-100, math.min(100, val))
95+
96+
if val == 0 then
97+
return
98+
elseif end_point == 0x47 then -- normal
99+
device:emit_event(capabilities.knob.rotateAmount({value = val}, {state_change = true}))
100+
elseif end_point == 0x48 then -- press
101+
device:emit_event(capabilities.knob.heldRotateAmount({value = val}, {state_change = true}))
102+
end
103+
end
104+
105+
local function battery_level_handler(driver, device, value, zb_rx)
106+
local voltage = value.value
107+
local batteryLevel = "normal"
108+
109+
if voltage <= 25 then
110+
batteryLevel = "critical"
111+
elseif voltage < 28 then
112+
batteryLevel = "warning"
113+
end
114+
115+
device:emit_event(capabilities.batteryLevel.battery(batteryLevel))
116+
end
117+
118+
local aqara_knob_switch_handler = {
119+
NAME = "Aqara Wireless Knob Switch Handler",
120+
lifecycle_handlers = {
121+
init = device_init,
122+
added = device_added,
123+
doConfigure = do_configure
124+
},
125+
zigbee_handlers = {
126+
attr = {
127+
[MULTISTATE_INPUT_CLUSTER_ID] = {
128+
[PRESENT_ATTRIBUTE_ID] = button_monitor_handler
129+
},
130+
[PRIVATE_CLUSTER_ID] = {
131+
[ROTATION_MONITOR_ID] = rotation_monitor_per_handler
132+
},
133+
[PowerConfiguration.ID] = {
134+
[PowerConfiguration.attributes.BatteryVoltage.ID] = battery_level_handler
135+
},
136+
}
137+
},
138+
can_handle = require("aqara-knob.can_handle"),
139+
}
140+
141+
return aqara_knob_switch_handler

drivers/SmartThings/zigbee-button/src/sub_drivers.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
local lazy_load_if_possible = require "lazy_load_subdriver"
55
local sub_drivers = {
66
lazy_load_if_possible("aqara"),
7+
lazy_load_if_possible("aqara-knob"),
78
lazy_load_if_possible("pushButton"),
89
lazy_load_if_possible("frient"),
910
lazy_load_if_possible("zigbee-multi-button"),

0 commit comments

Comments
 (0)