Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ lua_libs-api_*
tools/test_output/*
tools/coverage_output/*
.DS_Store
.venv/
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local clusters = require "st.zigbee.zcl.clusters"
local cluster_base = require "st.zigbee.cluster_base"
local data_types = require "st.zigbee.data_types"
local aqara_utils = require "aqara/aqara_utils"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"

local Groups = clusters.Groups
local Basic = clusters.Basic
Expand Down Expand Up @@ -47,6 +48,8 @@ local function device_added(driver, device)
device:emit_event(hookLockState.hookLockState.unlocked())
device:emit_event(chargingState.chargingState.stopped())
device:emit_event(capabilities.battery.battery(100))
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.position(window_preset_defaults.PRESET_LEVEL, { visibility = {displayed = false}}))
end

local function do_refresh(self, device)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ local clusters = require "st.zigbee.zcl.clusters"
local cluster_base = require "st.zigbee.cluster_base"
local data_types = require "st.zigbee.data_types"
local aqara_utils = require "aqara/aqara_utils"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"

local Basic = clusters.Basic
local WindowCovering = clusters.WindowCovering
Expand Down Expand Up @@ -172,6 +173,8 @@ local function device_added(driver, device)
device:emit_event(capabilities.windowShadeLevel.shadeLevel(0))
device:emit_event(capabilities.windowShade.windowShade.closed())
device:emit_event(deviceInitialization.initializedState.notInitialized())
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.position(window_preset_defaults.PRESET_LEVEL, { visibility = {displayed = false}}))

device:send(cluster_base.write_manufacturer_specific_attribute(device, aqara_utils.PRIVATE_CLUSTER_ID,
aqara_utils.PRIVATE_ATTRIBUTE_ID, aqara_utils.MFG_CODE, data_types.Uint8, 1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local cluster_base = require "st.zigbee.cluster_base"
local FrameCtrl = require "st.zigbee.zcl.frame_ctrl"
local data_types = require "st.zigbee.data_types"
local aqara_utils = require "aqara/aqara_utils"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"

local Basic = clusters.Basic
local WindowCovering = clusters.WindowCovering
Expand Down Expand Up @@ -97,6 +98,8 @@ local function device_added(driver, device)
device:emit_event(capabilities.windowShade.windowShade.closed())
device:emit_event(initializedStateWithGuide.initializedStateWithGuide.notInitialized())
device:emit_event(shadeRotateState.rotateState.idle({ visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.position(window_preset_defaults.PRESET_LEVEL, { visibility = {displayed = false}}))

device:send(cluster_base.write_manufacturer_specific_attribute(device, aqara_utils.PRIVATE_CLUSTER_ID,
aqara_utils.PRIVATE_ATTRIBUTE_ID, aqara_utils.MFG_CODE, data_types.Uint8, 1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
-- limitations under the License.

local capabilities = require "st.capabilities"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"
local window_shade_utils = require "window_shade_utils"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local utils = require "st.utils"

Expand Down Expand Up @@ -52,7 +52,7 @@ local function window_shade_level_cmd_handler(driver, device, command)
end

local function window_shade_preset_cmd(driver, device, command)
local level = device.preferences and device.preferences.presetPosition or window_preset_defaults.PRESET_LEVEL
local level = window_shade_utils.get_preset_level(device, command.component)
window_shade_set_level(device, command, level)
end

Expand Down
7 changes: 5 additions & 2 deletions drivers/SmartThings/zigbee-window-treatment/src/axis/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@

local capabilities = require "st.capabilities"
local device_management = require "st.zigbee.device_management"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"
local window_shade_utils = require "window_shade_utils"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local utils = require "st.utils"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"

local Basic = zcl_clusters.Basic
local Level = zcl_clusters.Level
Expand Down Expand Up @@ -50,7 +51,7 @@ local function window_shade_level_cmd_handler(driver, device, command)
end

local function window_shade_preset_cmd(driver, device, command)
local level = device.preferences and device.preferences.presetPosition or window_preset_defaults.PRESET_LEVEL
local level = window_shade_utils.get_preset_level(device, command.component)
window_shade_set_level(device, command, level)
end

Expand Down Expand Up @@ -116,6 +117,8 @@ local device_added = function(self, device)
device:emit_event(capabilities.windowShade.supportedWindowShadeCommands({"open", "close", "pause"}, { visibility = { displayed = false } }))
device:set_field(SOFTWARE_VERSION, 0)
device:send(Basic.attributes.SWBuildID:read(device))
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.position(window_preset_defaults.PRESET_LEVEL, { visibility = {displayed = false}}))
end

local axis_handler = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

local capabilities = require "st.capabilities"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"
local window_shade_utils = require "window_shade_utils"
local window_shade_defaults = require "st.zigbee.defaults.windowShade_defaults"
local device_management = require "st.zigbee.device_management"
local Level = zcl_clusters.Level
Expand Down Expand Up @@ -50,7 +50,7 @@ local function level_attr_handler(driver, device, value, zb_rx)
end

local function window_shade_preset_cmd(driver, device, command)
local level = device.preferences.presetPosition or device:get_field(window_preset_defaults.PRESET_LEVEL_KEY) or window_preset_defaults.PRESET_LEVEL
local level = window_shade_utils.get_preset_level(device, command.component)
set_shade_level(device, level, command.component)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local Messages = require "st.zigbee.messages"
local data_types = require "st.zigbee.data_types"
local ZigbeeConstants = require "st.zigbee.constants"
local generic_body = require "st.zigbee.generic_body"
local window_shade_utils = require "window_shade_utils"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"

local TUYA_CLUSTER = 0xEF00
Expand Down Expand Up @@ -148,7 +149,7 @@ local function SetShadeLevelHandler(driver, device, capability_command)
end

local function PresetPositionHandler(driver, device, capability_command)
local level = device.preferences.presetPosition or device:get_field(window_preset_defaults.PRESET_LEVEL_KEY) or window_preset_defaults.PRESET_LEVEL
local level = window_shade_utils.get_preset_level(device, capability_command.component)
SetShadeLevelHandler(driver, device, {args = { shadeLevel = level }})
end

Expand Down Expand Up @@ -194,6 +195,8 @@ local function device_added(driver, device)
SetShadeLevelHandler(driver, device, {args = { shadeLevel = 50 }})
end)
end
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.position(window_preset_defaults.PRESET_LEVEL, { visibility = {displayed = false}}))
end

local function device_info_changed(driver, device, event, args)
Expand Down
21 changes: 21 additions & 0 deletions drivers/SmartThings/zigbee-window-treatment/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,29 @@
local capabilities = require "st.capabilities"
local ZigbeeDriver = require "st.zigbee"
local defaults = require "st.zigbee.defaults"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"

local function init_handler(self, device)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like I mentioned in the CHAD-15784, we need this.
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }})) in init handler.

Copy link
Contributor

@inasail inasail Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if device:supports_capability_by_id(capabilities.windowShadePreset.ID) and
device:get_latest_state("main", capabilities.windowShadePreset.ID, capabilities.windowShadePreset.position.NAME) == nil then

-- These should only ever be nil once (and at the same time) for already-installed devices
-- It can be removed after migration is complete
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))

local preset_position = device:get_field(window_preset_defaults.PRESET_LEVEL_KEY) or
(device.preferences ~= nil and device.preferences.presetPosition) or
window_preset_defaults.PRESET_LEVEL

device:emit_event(capabilities.windowShadePreset.position(preset_position, { visibility = {displayed = false}}))
device:set_field(window_preset_defaults.PRESET_LEVEL_KEY, preset_position, {persist = true})
end
end

local function added_handler(self, device)
device:emit_event(capabilities.windowShade.supportedWindowShadeCommands({"open", "close", "pause"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.position(window_preset_defaults.PRESET_LEVEL, { visibility = {displayed = false}}))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think 'windowShadePreset.position' should only be emitted when there is no latest value, because the added() handler is called in the case of a hub switch-over.
The same goes for Tuya curtain in Unofficial driver.

end

local zigbee_window_treatment_driver_template = {
Expand All @@ -40,6 +60,7 @@ local zigbee_window_treatment_driver_template = {
require("hanssem"),
require("screen-innovations")},
lifecycle_handlers = {
init = init_handler,
added = added_handler
},
health_check = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

local capabilities = require "st.capabilities"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local window_shade_utils = require "window_shade_utils"

local WindowCovering = zcl_clusters.WindowCovering

Expand Down Expand Up @@ -75,9 +76,8 @@ local function window_shade_level_cmd(driver, device, command)
end

local function window_shade_preset_cmd(driver, device, command)
if device.preferences ~= nil and device.preferences.presetPosition ~= nil then
set_shade_level(device, device.preferences.presetPosition, command)
end
local level = window_shade_utils.get_preset_level(device, command.component)
set_shade_level(device, level, command)
end

local ikea_window_treatment = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
-- require st provided libraries
local capabilities = require "st.capabilities"
local clusters = require "st.zigbee.zcl.clusters"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"
local window_shade_utils = require "window_shade_utils"
local device_management = require "st.zigbee.device_management"
local utils = require "st.utils"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"

local Basic = clusters.Basic
local WindowCovering = clusters.WindowCovering
Expand Down Expand Up @@ -52,16 +53,18 @@ end

-- this is window_shade_preset_cmd
local function window_shade_preset_cmd(driver, device, command)
local go_to_level = device.preferences.presetPosition or device:get_field(window_preset_defaults.PRESET_LEVEL_KEY) or window_preset_defaults.PRESET_LEVEL
local level = window_shade_utils.get_preset_level(device, command.component)
-- send levels without inverting as: 0% closed (i.e., open) to 100% closed
device:send_to_component(command.component, WindowCovering.server.commands.GoToLiftPercentage(device, go_to_level))
device:send_to_component(command.component, WindowCovering.server.commands.GoToLiftPercentage(device, level))
end

-- this is device_added
local function device_added(self, device)
device:emit_event(capabilities.windowShade.supportedWindowShadeCommands({ "open", "close", "pause" }, {visibility = {displayed = false}}))
-- initialize motor state
device:set_field(MOTOR_STATE, MOTOR_STATE_IDLE)
device:emit_event(capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, { visibility = { displayed = false }}))
device:emit_event(capabilities.windowShadePreset.position(window_preset_defaults.PRESET_LEVEL, { visibility = {displayed = false}}))
device.thread:call_with_delay(3, function(d)
do_refresh(self, device)
end)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

local capabilities = require "st.capabilities"
local utils = require "st.utils"
local window_preset_defaults = require "st.zigbee.defaults.windowShadePreset_defaults"
local window_shade_utils = require "window_shade_utils"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local WindowCovering = zcl_clusters.WindowCovering

Expand Down Expand Up @@ -109,7 +109,7 @@ local function window_shade_level_cmd(driver, device, command)
end

local function window_shade_preset_cmd(driver, device, command)
local level = device.preferences.presetPosition or device:get_field(window_preset_defaults.PRESET_LEVEL_KEY) or window_preset_defaults.PRESET_LEVEL
local level = window_shade_utils.get_preset_level(device, command.component)
command.args.shadeLevel = level
window_shade_level_cmd(driver, device, command)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ local mock_device = test.mock_device.build_test_zigbee_device(

zigbee_test_utils.prepare_zigbee_env_info()
local function test_init()
test.mock_device.add_test_device(mock_device)end
test.mock_device.add_test_device(mock_device)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, {visibility = {displayed=false}}))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.windowShadePreset.position(50, {visibility = {displayed=false}}))
)
end

test.set_test_init_function(test_init)

Expand Down Expand Up @@ -162,7 +169,13 @@ test.register_coroutine_test(
test.register_coroutine_test(
"windowShadePreset capability should be handled",
function()
test.socket.device_lifecycle():__queue_receive(mock_device:generate_info_changed({preferences = {presetPosition = 30}}))
test.socket.capability:__queue_receive(
{
mock_device.id,
{ capability = "windowShadePreset", component = "main", command = "setPresetPosition", args = {30}}
}
)
test.socket.capability:__expect_send(mock_device:generate_test_message("main", capabilities.windowShadePreset.position(30)))
test.wait_for_events()
test.socket.capability:__queue_receive(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,14 @@ end

zigbee_test_utils.prepare_zigbee_env_info()
local function test_init()
test.mock_device.add_test_device(mock_device)end
test.mock_device.add_test_device(mock_device)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.windowShadePreset.supportedCommands({"presetPosition", "setPresetPosition"}, {visibility = {displayed=false}}))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", capabilities.windowShadePreset.position(50, {visibility = {displayed=false}}))
)
end

test.set_test_init_function(test_init)

Expand Down Expand Up @@ -136,9 +143,10 @@ test.register_coroutine_test(
{ capability = "windowShadePreset", component = "main", command = "presetPosition", args = {} }
}
)
-- newly added devices will ignore the preference
test.socket.zigbee:__expect_send({
mock_device.id,
WindowCovering.server.commands.GoToLiftPercentage(mock_device, 70)
WindowCovering.server.commands.GoToLiftPercentage(mock_device, 50)
})
end
)
Expand Down
Loading
Loading