Skip to content

Commit 3ef9c95

Browse files
Integrate the subdriver into the main driver
The subdriver existed to handle devices that send position updates while the shade is in motion. This PR integrates the functionality from the subdriver into the main driver in order to avoid having to maintain a subdriver.
1 parent 634161b commit 3ef9c95

File tree

3 files changed

+106
-233
lines changed

3 files changed

+106
-233
lines changed

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

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- Copyright 2022 SmartThings
1+
-- Copyright 2025 SmartThings
22
--
33
-- Licensed under the Apache License, Version 2.0 (the "License");
44
-- you may not use this file except in compliance with the License.
@@ -15,19 +15,28 @@
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 clusters = require "st.matter.clusters"
1819
local im = require "st.matter.interaction_model"
1920
local log = require "log"
20-
local clusters = require "st.matter.clusters"
2121
local MatterDriver = require "st.matter.driver"
2222

2323
local CURRENT_LIFT = "__current_lift"
2424
local CURRENT_TILT = "__current_tilt"
25+
local REVERSE_POLARITY = "__reverse_polarity"
26+
local STATE_MACHINE = "__state_machine"
27+
28+
local StateMachineEnum = {
29+
STATE_IDLE = 0x00,
30+
STATE_MOVING = 0x01,
31+
STATE_OPERATIONAL_STATE_FIRED = 0x02,
32+
STATE_CURRENT_POSITION_FIRED = 0x03
33+
}
34+
2535
local battery_support = {
2636
NO_BATTERY = "NO_BATTERY",
2737
BATTERY_LEVEL = "BATTERY_LEVEL",
2838
BATTERY_PERCENTAGE = "BATTERY_PERCENTAGE"
2939
}
30-
local REVERSE_POLARITY = "__reverse_polarity"
3140

3241
local function find_default_endpoint(device, cluster)
3342
local res = device.MATTER_DEFAULT_ENDPOINT
@@ -188,59 +197,63 @@ local function handle_shade_tilt_level(driver, device, cmd)
188197
device:send(req)
189198
end
190199

200+
--- Update the window shade status according to the lift and tilt positions.
201+
--- LIFT TILT Window Shade
202+
--- 100 any Open
203+
--- 1-99 any Partially Open
204+
--- 0 1-100 Partially Open
205+
--- 0 0 Closed
206+
--- 0 nil Closed
207+
--- nil 100 Open
208+
--- nil 1-99 Partially Open
209+
--- nil 0 Closed
210+
--- Note that lift or tilt may be nil if either the window shade does not
211+
--- support them or if they haven't been received from a device report yet.
212+
local function update_shade_status(device, endpoint_id, lift_position, tilt_position)
213+
local windowShade = capabilities.windowShade.windowShade
214+
if lift_position == nil then
215+
if tilt_position == 0 then
216+
device:emit_event_for_endpoint(endpoint_id, windowShade.closed())
217+
elseif tilt_position == 100 then
218+
device:emit_event_for_endpoint(endpoint_id, windowShade.open())
219+
else
220+
device:emit_event_for_endpoint(endpoint_id, windowShade.partially_open())
221+
end
222+
elseif lift_position == 100 then
223+
device:emit_event_for_endpoint(endpoint_id, windowShade.open())
224+
elseif lift_position > 0 then
225+
device:emit_event_for_endpoint(endpoint_id, windowShade.partially_open())
226+
elseif lift_position == 0 then
227+
if tilt_position == nil or tilt_position == 0 then
228+
device:emit_event_for_endpoint(endpoint_id, windowShade.closed())
229+
elseif tilt_position > 0 then
230+
device:emit_event_for_endpoint(endpoint_id, windowShade.partially_open())
231+
end
232+
else
233+
device:emit_event_for_endpoint(endpoint_id, windowShade.unknown())
234+
end
235+
end
236+
191237
-- current lift/tilt percentage, changed to 100ths percent
192238
local current_pos_handler = function(attribute)
193239
return function(driver, device, ib, response)
194-
if ib.data.value == nil then
195-
return
196-
end
197-
local windowShade = capabilities.windowShade.windowShade
240+
if ib.data.value == nil then return end
198241
local position = reverse_polarity_if_needed(device, math.floor((ib.data.value / 100)))
199242
device:emit_event_for_endpoint(ib.endpoint_id, attribute(position))
200243

201244
if attribute == capabilities.windowShadeLevel.shadeLevel then
202245
device:set_field(CURRENT_LIFT, position)
203-
else
246+
else -- attribute = capabilities.windowShadeTiltLevel.shadeTiltLevel
204247
device:set_field(CURRENT_TILT, position)
205248
end
206249

207-
local lift_position = device:get_field(CURRENT_LIFT)
208-
local tilt_position = device:get_field(CURRENT_TILT)
209-
210-
-- Update the window shade status according to the lift and tilt positions.
211-
-- LIFT TILT Window Shade
212-
-- 100 any Open
213-
-- 1-99 any Partially Open
214-
-- 0 1-100 Partially Open
215-
-- 0 0 Closed
216-
-- 0 nil Closed
217-
-- nil 100 Open
218-
-- nil 1-99 Partially Open
219-
-- nil 0 Closed
220-
-- Note that lift or tilt may be nil if either the window shade does not
221-
-- support them or if they haven't been received from a device report yet.
222-
223-
if lift_position == nil then
224-
if tilt_position == 0 then
225-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closed())
226-
elseif tilt_position == 100 then
227-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.open())
228-
else
229-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.partially_open())
230-
end
231-
232-
elseif lift_position == 100 then
233-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.open())
234-
235-
elseif lift_position > 0 then
236-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.partially_open())
237-
238-
elseif lift_position == 0 then
239-
if tilt_position == nil or tilt_position == 0 then
240-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closed())
241-
elseif tilt_position > 0 then
242-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.partially_open())
243-
end
250+
local state_machine = device:get_field(STATE_MACHINE)
251+
-- When state_machine is STATE_IDLE or STATE_CURRENT_POSITION_FIRED, nothing to do
252+
if state_machine == StateMachineEnum.STATE_MOVING then
253+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_CURRENT_POSITION_FIRED)
254+
elseif state_machine == StateMachineEnum.STATE_OPERATIONAL_STATE_FIRED or state_machine == nil then
255+
update_shade_status(device, ib.endpoint_id, device:get_field(CURRENT_LIFT), device:get_field(CURRENT_TILT))
256+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
244257
end
245258
end
246259
end
@@ -249,12 +262,30 @@ end
249262
local function current_status_handler(driver, device, ib, response)
250263
local windowShade = capabilities.windowShade.windowShade
251264
local state = ib.data.value & clusters.WindowCovering.types.OperationalStatus.GLOBAL
252-
if state == 1 then -- opening
253-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
254-
elseif state == 2 then -- closing
255-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
256-
elseif state ~= 0 then -- unknown
257-
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.unknown())
265+
local state_machine = device:get_field(STATE_MACHINE)
266+
-- When state_machine is STATE_OPERATIONAL_STATE_FIRED, nothing to do
267+
if state_machine == StateMachineEnum.STATE_IDLE then
268+
if state == 1 then -- opening
269+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
270+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_MOVING)
271+
elseif state == 2 then -- closing
272+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
273+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_MOVING)
274+
end
275+
elseif state_machine == StateMachineEnum.STATE_MOVING then
276+
if state == 0 then
277+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_OPERATIONAL_STATE_FIRED)
278+
elseif state == 1 then -- opening
279+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
280+
elseif state == 2 then -- closing
281+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
282+
else
283+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.unknown())
284+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
285+
end
286+
elseif state_machine == StateMachineEnum.STATE_CURRENT_POSITION_FIRED then
287+
update_shade_status(device, ib.endpoint_id, device:get_field(CURRENT_LIFT), device:get_field(CURRENT_TILT))
288+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
258289
end
259290
end
260291

@@ -367,10 +398,6 @@ local matter_driver_template = {
367398
capabilities.windowShadePreset,
368399
capabilities.battery,
369400
capabilities.batteryLevel,
370-
},
371-
sub_drivers = {
372-
-- for devices sending a position update while device is in motion
373-
require("matter-window-covering-position-updates-while-moving")
374401
}
375402
}
376403

drivers/SmartThings/matter-window-covering/src/matter-window-covering-position-updates-while-moving/init.lua

Lines changed: 0 additions & 157 deletions
This file was deleted.

0 commit comments

Comments
 (0)