Skip to content

Commit

Permalink
Fix incorrect operational state issue by workaround (#1266)
Browse files Browse the repository at this point in the history
Fix incorrect operational state issue

Operational state is mismatching with actual status of window covering

Signed-off-by: Hunsup Jung <[email protected]>
  • Loading branch information
HunsupJung authored Mar 19, 2024
1 parent 561aa74 commit ce6b03c
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 14 deletions.
68 changes: 54 additions & 14 deletions drivers/SmartThings/matter-window-covering/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ local MatterDriver = require "st.matter.driver"

local DEFAULT_LEVEL = 0
local PROFILE_MATCHED = "__profile_matched"
local IS_MOVING = "__is_moving"
local EVENT_STATE = "__event_state"

local WindowCoveringEventEnum = {
NO_EVENT = 0x00,
CURRENT_POSITION_EVENT = 0x01,
OPERATIONAL_STATE_EVENT = 0x02
}

local function find_default_endpoint(device, cluster)
local res = device.MATTER_DEFAULT_ENDPOINT
Expand Down Expand Up @@ -54,6 +62,8 @@ local function match_profile(device)
end

local function device_init(driver, device)
device:set_field(EVENT_STATE, WindowCoveringEventEnum.NO_EVENT)
device:set_field(IS_MOVING, false)
if not device:get_field(PROFILE_MATCHED) then
match_profile(device)
end
Expand Down Expand Up @@ -127,12 +137,29 @@ end

-- current lift percentage, changed to 100ths percent
local function current_pos_handler(driver, device, ib, response)
local position = 0
if ib.data.value ~= nil then
local position = 100 - math.floor((ib.data.value / 100))
position = 100 - math.floor((ib.data.value / 100))
device:emit_event_for_endpoint(
ib.endpoint_id, capabilities.windowShadeLevel.shadeLevel(position)
)
end
if device:get_field(EVENT_STATE) == WindowCoveringEventEnum.OPERATIONAL_STATE_EVENT then
if not device:get_field(IS_MOVING) then
if position == 0 then
device:emit_event_for_endpoint(ib.endpoint_id, capabilities.windowShade.windowShade.closed())
elseif position == 100 then
device:emit_event_for_endpoint(ib.endpoint_id, capabilities.windowShade.windowShade.open())
elseif position > 0 and position < 100 then
device:emit_event_for_endpoint(ib.endpoint_id, capabilities.windowShade.windowShade.partially_open())
else
device:emit_event_for_endpoint(ib.endpoint_id, capabilities.windowShade.windowShade.unknown())
end
end
device:set_field(EVENT_STATE, WindowCoveringEventEnum.NO_EVENT)
else
device:set_field(EVENT_STATE, WindowCoveringEventEnum.CURRENT_POSITION_EVENT)
end
end

-- checks the current position of the shade
Expand All @@ -150,20 +177,34 @@ local function current_status_handler(driver, device, ib, response)
end
end
local state = ib.data.value & clusters.WindowCovering.types.OperationalStatus.GLOBAL --Could use LIFT instead
if state == 0 then -- not moving
if position == 100 then -- open
device:emit_event_for_endpoint(ib.endpoint_id, attr.open())
elseif position == 0 then -- closed
device:emit_event_for_endpoint(ib.endpoint_id, attr.closed())
if device:get_field(EVENT_STATE) == WindowCoveringEventEnum.CURRENT_POSITION_EVENT then
if state == 0 then -- not moving
if position == 100 then -- open
device:emit_event_for_endpoint(ib.endpoint_id, attr.open())
elseif position == 0 then -- closed
device:emit_event_for_endpoint(ib.endpoint_id, attr.closed())
else
device:emit_event_for_endpoint(ib.endpoint_id, attr.partially_open())
end
elseif state == 1 then -- opening
device:emit_event_for_endpoint(ib.endpoint_id, attr.opening())
elseif state == 2 then -- closing
device:emit_event_for_endpoint(ib.endpoint_id, attr.closing())
else
device:emit_event_for_endpoint(ib.endpoint_id, attr.partially_open())
device:emit_event_for_endpoint(ib.endpoint_id, attr.unknown())
end
elseif state == 1 then -- opening
device:emit_event_for_endpoint(ib.endpoint_id, attr.opening())
elseif state == 2 then -- closing
device:emit_event_for_endpoint(ib.endpoint_id, attr.closing())
device:set_field(EVENT_STATE, WindowCoveringEventEnum.NO_EVENT)
else
device:emit_event_for_endpoint(ib.endpoint_id, attr.unknown())
if state == 1 then -- opening
device:emit_event_for_endpoint(ib.endpoint_id, attr.opening())
device:set_field(IS_MOVING, true)
elseif state == 2 then -- closing
device:emit_event_for_endpoint(ib.endpoint_id, attr.closing())
device:set_field(IS_MOVING, true)
else
device:set_field(IS_MOVING, false)
end
device:set_field(EVENT_STATE, WindowCoveringEventEnum.OPERATIONAL_STATE_EVENT)
end
end

Expand All @@ -181,7 +222,6 @@ local function battery_percent_remaining_attr_handler(driver, device, ib, respon
end
end


local matter_driver_template = {
lifecycle_handlers = {init = device_init, removed = device_removed, added = device_added, infoChanged = info_changed},
matter_handlers = {
Expand Down Expand Up @@ -238,4 +278,4 @@ local matter_driver_template = {
}

local matter_driver = MatterDriver("matter-window-covering", matter_driver_template)
matter_driver:run()
matter_driver:run()
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,36 @@ test.register_coroutine_test(
end
)

test.register_coroutine_test(
"WindowCovering OperationalStatus state closed before position 0", function()
test.socket.capability:__set_channel_ordering("relaxed")
test.socket.matter:__queue_receive(
{
mock_device.id,
WindowCovering.attributes.OperationalStatus:build_test_report_data(mock_device, 10, 0),
}
)
test.socket.matter:__queue_receive(
{
mock_device.id,
WindowCovering.attributes.CurrentPositionLiftPercent100ths:build_test_report_data(
mock_device, 10, 10000
),
}
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main", capabilities.windowShade.windowShade.closed()
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main", capabilities.windowShadeLevel.shadeLevel(0)
)
)
end
)

test.register_coroutine_test(
"WindowCovering OperationalStatus state open", function()
test.socket.capability:__set_channel_ordering("relaxed")
Expand Down Expand Up @@ -220,6 +250,36 @@ test.register_coroutine_test(
end
)

test.register_coroutine_test(
"WindowCovering OperationalStatus state open before position event", function()
test.socket.capability:__set_channel_ordering("relaxed")
test.socket.matter:__queue_receive(
{
mock_device.id,
WindowCovering.attributes.OperationalStatus:build_test_report_data(mock_device, 10, 0),
}
)
test.socket.matter:__queue_receive(
{
mock_device.id,
WindowCovering.attributes.CurrentPositionLiftPercent100ths:build_test_report_data(
mock_device, 10, 0
),
}
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main", capabilities.windowShade.windowShade.open()
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main", capabilities.windowShadeLevel.shadeLevel(100)
)
)
end
)

test.register_coroutine_test(
"WindowCovering OperationalStatus partially open", function()
test.socket.capability:__set_channel_ordering("relaxed")
Expand Down Expand Up @@ -250,6 +310,36 @@ test.register_coroutine_test(
end
)

test.register_coroutine_test(
"WindowCovering OperationalStatus partially open before position event", function()
test.socket.capability:__set_channel_ordering("relaxed")
test.socket.matter:__queue_receive(
{
mock_device.id,
WindowCovering.attributes.OperationalStatus:build_test_report_data(mock_device, 10, 0),
}
)
test.socket.matter:__queue_receive(
{
mock_device.id,
WindowCovering.attributes.CurrentPositionLiftPercent100ths:build_test_report_data(
mock_device, 10, ((100 - 25) *100)
),
}
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main", capabilities.windowShadeLevel.shadeLevel(25)
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main", capabilities.windowShade.windowShade.partially_open()
)
)
end
)

test.register_coroutine_test("WindowCovering OperationalStatus opening", function()
test.socket.capability:__set_channel_ordering("relaxed")
test.socket.matter:__queue_receive(
Expand Down

0 comments on commit ce6b03c

Please sign in to comment.