Skip to content

Commit 9858c56

Browse files
Add batteryLevel to matter window covering (#1860)
Improve the profile selection logic in matter-window-covering by reading the AttributeList, checking if BatPercentRemaining or BatChargeLevel is available, and then profile the device as needed.
1 parent f6df83a commit 9858c56

File tree

3 files changed

+219
-118
lines changed

3 files changed

+219
-118
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: window-covering-batteryLevel
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: windowShade
6+
version: 1
7+
- id: windowShadePreset
8+
version: 1
9+
- id: windowShadeLevel
10+
version: 1
11+
- id: batteryLevel
12+
version: 1
13+
- id: firmwareUpdate
14+
version: 1
15+
- id: refresh
16+
version: 1
17+
categories:
18+
- name: Blind
19+
preferences:
20+
- preferenceId: presetPosition
21+
explicit: true

drivers/SmartThings/matter-window-covering/src/init.lua

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
--Note: Currently only support for window shades with the PositionallyAware Feature
1616
--Note: No support for setting device into calibration mode, it must be done manually
1717
local capabilities = require "st.capabilities"
18+
local im = require "st.matter.interaction_model"
1819
local log = require "log"
1920
local clusters = require "st.matter.clusters"
2021
local MatterDriver = require "st.matter.driver"
21-
local PROFILE_MATCHED = "__profile_matched"
22+
23+
local battery_support = {
24+
NO_BATTERY = "NO_BATTERY",
25+
BATTERY_LEVEL = "BATTERY_LEVEL",
26+
BATTERY_PERCENTAGE = "BATTERY_PERCENTAGE"
27+
}
2228

2329
local function find_default_endpoint(device, cluster)
2430
local res = device.MATTER_DEFAULT_ENDPOINT
@@ -39,34 +45,47 @@ local function component_to_endpoint(device, component_name)
3945
return find_default_endpoint(device, clusters.WindowCovering.ID)
4046
end
4147

42-
local function match_profile(device)
48+
local function match_profile(device, battery_supported)
4349
local profile_name = "window-covering"
44-
local battery_eps = device:get_endpoints(clusters.PowerSource.ID,
45-
{feature_bitmap = clusters.PowerSource.types.PowerSourceFeature.BATTERY})
46-
47-
if #battery_eps > 0 then
48-
profile_name = "window-covering-battery"
50+
if battery_supported == battery_support.BATTERY_PERCENTAGE then
51+
profile_name = profile_name .. "-battery"
52+
elseif battery_supported == battery_support.BATTERY_LEVEL then
53+
profile_name = profile_name .. "-batteryLevel"
4954
end
5055
device:try_update_metadata({profile = profile_name})
51-
device:set_field(PROFILE_MATCHED, 1)
5256
end
5357

5458
local function device_init(driver, device)
55-
if not device:get_field(PROFILE_MATCHED) then
56-
match_profile(device)
57-
end
5859
device:set_component_to_endpoint_fn(component_to_endpoint)
5960
device:subscribe()
6061
end
6162

63+
local function do_configure(driver, device)
64+
local battery_feature_eps = device:get_endpoints(clusters.PowerSource.ID, {feature_bitmap = clusters.PowerSource.types.PowerSourceFeature.BATTERY})
65+
if #battery_feature_eps > 0 then
66+
local attribute_list_read = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {})
67+
attribute_list_read:merge(clusters.PowerSource.attributes.AttributeList:read())
68+
device:send(attribute_list_read)
69+
else
70+
match_profile(device, battery_support.NO_BATTERY)
71+
end
72+
end
73+
6274
local function info_changed(driver, device, event, args)
6375
if device.profile.id ~= args.old_st_store.profile.id then
6476
-- Profile has changed, resubscribe
6577
device:subscribe()
6678
else
6779
-- Something else has changed info (SW update, reinterview, etc.), so
6880
-- try updating profile as needed
69-
match_profile(device)
81+
local battery_feature_eps = device:get_endpoints(clusters.PowerSource.ID, {feature_bitmap = clusters.PowerSource.types.PowerSourceFeature.BATTERY})
82+
if #battery_feature_eps > 0 then
83+
local attribute_list_read = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {})
84+
attribute_list_read:merge(clusters.PowerSource.attributes.AttributeList:read())
85+
device:send(attribute_list_read)
86+
else
87+
match_profile(device, battery_support.NO_BATTERY)
88+
end
7089
end
7190
end
7291

@@ -169,8 +188,37 @@ local function battery_percent_remaining_attr_handler(driver, device, ib, respon
169188
end
170189
end
171190

191+
local function battery_charge_level_attr_handler(driver, device, ib, response)
192+
if ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.OK then
193+
device:emit_event(capabilities.batteryLevel.battery.normal())
194+
elseif ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.WARNING then
195+
device:emit_event(capabilities.batteryLevel.battery.warning())
196+
elseif ib.data.value == clusters.PowerSource.types.BatChargeLevelEnum.CRITICAL then
197+
device:emit_event(capabilities.batteryLevel.battery.critical())
198+
end
199+
end
200+
201+
local function power_source_attribute_list_handler(driver, device, ib, response)
202+
for _, attr in ipairs(ib.data.elements) do
203+
-- Re-profile the device if BatPercentRemaining (Attribute ID 0x0C) is present.
204+
if attr.value == 0x0C then
205+
match_profile(device, battery_support.BATTERY_PERCENTAGE)
206+
return
207+
elseif attr.value == 0x0E then
208+
match_profile(device, battery_support.BATTERY_LEVEL)
209+
return
210+
end
211+
end
212+
end
213+
172214
local matter_driver_template = {
173-
lifecycle_handlers = {init = device_init, removed = device_removed, added = device_added, infoChanged = info_changed},
215+
lifecycle_handlers = {
216+
init = device_init,
217+
removed = device_removed,
218+
added = device_added,
219+
infoChanged = info_changed,
220+
doConfigure = do_configure,
221+
},
174222
matter_handlers = {
175223
attr = {
176224
--TODO LevelControl may not be needed for certified devices since
@@ -184,6 +232,8 @@ local matter_driver_template = {
184232
[clusters.WindowCovering.attributes.OperationalStatus.ID] = current_status_handler,
185233
},
186234
[clusters.PowerSource.ID] = {
235+
[clusters.PowerSource.attributes.AttributeList.ID] = power_source_attribute_list_handler,
236+
[clusters.PowerSource.attributes.BatChargeLevel.ID] = battery_charge_level_attr_handler,
187237
[clusters.PowerSource.attributes.BatPercentRemaining.ID] = battery_percent_remaining_attr_handler,
188238
}
189239
},
@@ -198,7 +248,10 @@ local matter_driver_template = {
198248
},
199249
[capabilities.battery.ID] = {
200250
clusters.PowerSource.attributes.BatPercentRemaining
201-
}
251+
},
252+
[capabilities.batteryLevel.ID] = {
253+
clusters.PowerSource.attributes.BatChargeLevel,
254+
},
202255
},
203256
capability_handlers = {
204257
[capabilities.refresh.ID] = {
@@ -221,6 +274,7 @@ local matter_driver_template = {
221274
capabilities.windowShade,
222275
capabilities.windowShadePreset,
223276
capabilities.battery,
277+
capabilities.batteryLevel,
224278
},
225279
sub_drivers = {
226280
-- for devices sending a position update while device is in motion

0 commit comments

Comments
 (0)