Skip to content

Commit dd1215c

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 dd1215c

File tree

3 files changed

+108
-233
lines changed

3 files changed

+108
-233
lines changed

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

Lines changed: 84 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,30 @@
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 DEFAULT_LEVEL = 0
29+
30+
local StateMachineEnum = {
31+
STATE_IDLE = 0x00,
32+
STATE_MOVING = 0x01,
33+
STATE_OPERATIONAL_STATE_FIRED = 0x02,
34+
STATE_CURRENT_POSITION_FIRED = 0x03
35+
}
36+
2537
local battery_support = {
2638
NO_BATTERY = "NO_BATTERY",
2739
BATTERY_LEVEL = "BATTERY_LEVEL",
2840
BATTERY_PERCENTAGE = "BATTERY_PERCENTAGE"
2941
}
30-
local REVERSE_POLARITY = "__reverse_polarity"
3142

3243
local function find_default_endpoint(device, cluster)
3344
local res = device.MATTER_DEFAULT_ENDPOINT
@@ -188,59 +199,63 @@ local function handle_shade_tilt_level(driver, device, cmd)
188199
device:send(req)
189200
end
190201

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

201246
if attribute == capabilities.windowShadeLevel.shadeLevel then
202247
device:set_field(CURRENT_LIFT, position)
203-
else
248+
else -- attribute = capabilities.windowShadeTiltLevel.shadeTiltLevel
204249
device:set_field(CURRENT_TILT, position)
205250
end
206251

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
252+
local state_machine = device:get_field(STATE_MACHINE)
253+
-- When state_machine is STATE_IDLE or STATE_CURRENT_POSITION_FIRED, nothing to do
254+
if state_machine == StateMachineEnum.STATE_MOVING then
255+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_CURRENT_POSITION_FIRED)
256+
elseif state_machine == StateMachineEnum.STATE_OPERATIONAL_STATE_FIRED or state_machine == nil then
257+
update_shade_status(device, ib.endpoint_id, device:get_field(CURRENT_LIFT), device:get_field(CURRENT_TILT))
258+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
244259
end
245260
end
246261
end
@@ -249,12 +264,30 @@ end
249264
local function current_status_handler(driver, device, ib, response)
250265
local windowShade = capabilities.windowShade.windowShade
251266
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())
267+
local state_machine = device:get_field(STATE_MACHINE)
268+
-- When state_machine is STATE_OPERATIONAL_STATE_FIRED, nothing to do
269+
if state_machine == StateMachineEnum.STATE_IDLE then
270+
if state == 1 then -- opening
271+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
272+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_MOVING)
273+
elseif state == 2 then -- closing
274+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
275+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_MOVING)
276+
end
277+
elseif state_machine == StateMachineEnum.STATE_MOVING then
278+
if state == 0 then
279+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_OPERATIONAL_STATE_FIRED)
280+
elseif state == 1 then -- opening
281+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
282+
elseif state == 2 then -- closing
283+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
284+
else
285+
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.unknown())
286+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
287+
end
288+
elseif state_machine == StateMachineEnum.STATE_CURRENT_POSITION_FIRED then
289+
update_shade_status(device, ib.endpoint_id, device:get_field(CURRENT_LIFT), device:get_field(CURRENT_TILT))
290+
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
258291
end
259292
end
260293

@@ -367,10 +400,6 @@ local matter_driver_template = {
367400
capabilities.windowShadePreset,
368401
capabilities.battery,
369402
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")
374403
}
375404
}
376405

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)