Skip to content

Integrate the matter window covering subdriver into the main driver #2169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 82 additions & 55 deletions drivers/SmartThings/matter-window-covering/src/init.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- Copyright 2022 SmartThings
-- Copyright 2025 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
Expand All @@ -15,19 +15,28 @@
--Note: Currently only support for window shades with the PositionallyAware Feature
--Note: No support for setting device into calibration mode, it must be done manually
local capabilities = require "st.capabilities"
local clusters = require "st.matter.clusters"
local im = require "st.matter.interaction_model"
local log = require "log"
local clusters = require "st.matter.clusters"
local MatterDriver = require "st.matter.driver"

local CURRENT_LIFT = "__current_lift"
local CURRENT_TILT = "__current_tilt"
local REVERSE_POLARITY = "__reverse_polarity"
local STATE_MACHINE = "__state_machine"

local StateMachineEnum = {
STATE_IDLE = 0x00,
STATE_MOVING = 0x01,
STATE_OPERATIONAL_STATE_FIRED = 0x02,
STATE_CURRENT_POSITION_FIRED = 0x03
}

local battery_support = {
NO_BATTERY = "NO_BATTERY",
BATTERY_LEVEL = "BATTERY_LEVEL",
BATTERY_PERCENTAGE = "BATTERY_PERCENTAGE"
}
local REVERSE_POLARITY = "__reverse_polarity"

local function find_default_endpoint(device, cluster)
local res = device.MATTER_DEFAULT_ENDPOINT
Expand Down Expand Up @@ -188,59 +197,63 @@ local function handle_shade_tilt_level(driver, device, cmd)
device:send(req)
end

--- Update the window shade status according to the lift and tilt positions.
--- LIFT TILT Window Shade
--- 100 any Open
--- 1-99 any Partially Open
--- 0 1-100 Partially Open
--- 0 0 Closed
--- 0 nil Closed
--- nil 100 Open
--- nil 1-99 Partially Open
--- nil 0 Closed
--- Note that lift or tilt may be nil if either the window shade does not
--- support them or if they haven't been received from a device report yet.
local function update_shade_status(device, endpoint_id, lift_position, tilt_position)
local windowShade = capabilities.windowShade.windowShade
if lift_position == nil then
if tilt_position == 0 then
device:emit_event_for_endpoint(endpoint_id, windowShade.closed())
elseif tilt_position == 100 then
device:emit_event_for_endpoint(endpoint_id, windowShade.open())
else
device:emit_event_for_endpoint(endpoint_id, windowShade.partially_open())
end
elseif lift_position == 100 then
device:emit_event_for_endpoint(endpoint_id, windowShade.open())
elseif lift_position > 0 then
device:emit_event_for_endpoint(endpoint_id, windowShade.partially_open())
elseif lift_position == 0 then
if tilt_position == nil or tilt_position == 0 then
device:emit_event_for_endpoint(endpoint_id, windowShade.closed())
elseif tilt_position > 0 then
device:emit_event_for_endpoint(endpoint_id, windowShade.partially_open())
end
else
device:emit_event_for_endpoint(endpoint_id, windowShade.unknown())
end
end

-- current lift/tilt percentage, changed to 100ths percent
local current_pos_handler = function(attribute)
return function(driver, device, ib, response)
if ib.data.value == nil then
return
end
local windowShade = capabilities.windowShade.windowShade
if ib.data.value == nil then return end
local position = reverse_polarity_if_needed(device, math.floor((ib.data.value / 100)))
device:emit_event_for_endpoint(ib.endpoint_id, attribute(position))

if attribute == capabilities.windowShadeLevel.shadeLevel then
device:set_field(CURRENT_LIFT, position)
else
else -- attribute = capabilities.windowShadeTiltLevel.shadeTiltLevel
device:set_field(CURRENT_TILT, position)
end

local lift_position = device:get_field(CURRENT_LIFT)
local tilt_position = device:get_field(CURRENT_TILT)

-- Update the window shade status according to the lift and tilt positions.
-- LIFT TILT Window Shade
-- 100 any Open
-- 1-99 any Partially Open
-- 0 1-100 Partially Open
-- 0 0 Closed
-- 0 nil Closed
-- nil 100 Open
-- nil 1-99 Partially Open
-- nil 0 Closed
-- Note that lift or tilt may be nil if either the window shade does not
-- support them or if they haven't been received from a device report yet.

if lift_position == nil then
if tilt_position == 0 then
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closed())
elseif tilt_position == 100 then
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.open())
else
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.partially_open())
end

elseif lift_position == 100 then
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.open())

elseif lift_position > 0 then
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.partially_open())

elseif lift_position == 0 then
if tilt_position == nil or tilt_position == 0 then
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closed())
elseif tilt_position > 0 then
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.partially_open())
end
local state_machine = device:get_field(STATE_MACHINE)
-- When state_machine is STATE_IDLE or STATE_CURRENT_POSITION_FIRED, nothing to do
if state_machine == StateMachineEnum.STATE_MOVING then
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_CURRENT_POSITION_FIRED)
elseif state_machine == StateMachineEnum.STATE_OPERATIONAL_STATE_FIRED or state_machine == nil then
update_shade_status(device, ib.endpoint_id, device:get_field(CURRENT_LIFT), device:get_field(CURRENT_TILT))
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
end
end
end
Expand All @@ -249,12 +262,30 @@ end
local function current_status_handler(driver, device, ib, response)
local windowShade = capabilities.windowShade.windowShade
local state = ib.data.value & clusters.WindowCovering.types.OperationalStatus.GLOBAL
if state == 1 then -- opening
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
elseif state == 2 then -- closing
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
elseif state ~= 0 then -- unknown
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.unknown())
local state_machine = device:get_field(STATE_MACHINE)
-- When state_machine is STATE_OPERATIONAL_STATE_FIRED, nothing to do
if state_machine == StateMachineEnum.STATE_IDLE then
if state == 1 then -- opening
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_MOVING)
elseif state == 2 then -- closing
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_MOVING)
end
elseif state_machine == StateMachineEnum.STATE_MOVING then
if state == 0 then
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_OPERATIONAL_STATE_FIRED)
elseif state == 1 then -- opening
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.opening())
elseif state == 2 then -- closing
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.closing())
else
device:emit_event_for_endpoint(ib.endpoint_id, windowShade.unknown())
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
end
elseif state_machine == StateMachineEnum.STATE_CURRENT_POSITION_FIRED then
update_shade_status(device, ib.endpoint_id, device:get_field(CURRENT_LIFT), device:get_field(CURRENT_TILT))
device:set_field(STATE_MACHINE, StateMachineEnum.STATE_IDLE)
end
end

Expand Down Expand Up @@ -367,10 +398,6 @@ local matter_driver_template = {
capabilities.windowShadePreset,
capabilities.battery,
capabilities.batteryLevel,
},
sub_drivers = {
-- for devices sending a position update while device is in motion
require("matter-window-covering-position-updates-while-moving")
}
}

Expand Down

This file was deleted.

Loading
Loading