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.
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
1717local capabilities = require " st.capabilities"
18+ local clusters = require " st.matter.clusters"
1819local im = require " st.matter.interaction_model"
1920local log = require " log"
20- local clusters = require " st.matter.clusters"
2121local MatterDriver = require " st.matter.driver"
2222
2323local CURRENT_LIFT = " __current_lift"
2424local 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+
2537local 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
3243local 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 )
189200end
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
192240local 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
246261end
@@ -249,12 +264,30 @@ end
249264local 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
259292end
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
0 commit comments