Skip to content
Draft
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
5 changes: 5 additions & 0 deletions drivers/SmartThings/zigbee-switch/fingerprints.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
zigbeeManufacturer:
- id: "_TZ3000_hurauima/TS0726"
deviceLabel: Zemismart KES606 4 Gang
manufacturer: _TZ3000_hurauima
model: TS0726
deviceProfileName: ts0601-scene-switch-4-gang
#SMARTvill
- id: "SMARTvill/SLA06"
deviceLabel: "SMARTvill Switch 1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: ts0601-scene-switch-4-gang
components:
- id: main
capabilities:
- id: switch
version: 1
- id: button
version: 1
- id: refresh
version: 1
categories:
- name: RemoteController
preferences:
- title: Indicator Light

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Regarding your review request:

The embedded preference definitions, or the approved shared preferences that should replace them without changing their meaning.

We would prefer you use the shared preferences where possible

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I audited the current standard, stse and certifiedpreferences catalogs (90 definitions). The three-state power-on and indicator settings and the 0–43200-second toggle countdown do not have equivalent shared definitions. For example, restorePowerState is Boolean; powerOffMemory adds unsupported reverse behavior; turnOffDelay lacks zero cancellation, the full range and delayed-on behavior.

stse.changeToWirelessSwitch matches the basic mode function, but replacing our existing switchMode enum also changes the saved key/type and introduces a false default for existing Scene children. I did not find a documented alias or supported per-device preference-value migration in the current profile/SDK interfaces. Ignoring the new default would leave the displayed setting inconsistent; treating it as an ordinary change risks switching a Scene gang back to relay mode.

I have therefore retained the inline definitions and saved settings in this revision. Could you advise whether there is a preferred compatible shared enum or supported migration mechanism for this case? The catalog/schema audit is complete, but I am not claiming a shared-preference conversion.

name: lightMode
required: false
preferenceType: enumeration
definition:
options:
none: Off
relay: Follow Relay
pos: Position Indicator
default: relay
- title: Global Power-on State
name: relayStatus
required: false
preferenceType: enumeration
definition:
options:
off: Off
on: On
memory: Memory
default: memory
- title: Gang 1 Power-on State
name: relayStatus1
required: false
preferenceType: enumeration
definition:
options:
off: Off
on: On
memory: Memory
default: memory
- title: Gang 2 Power-on State
name: relayStatus2
required: false
preferenceType: enumeration
definition:
options:
off: Off
on: On
memory: Memory
default: memory
- title: Gang 3 Power-on State
name: relayStatus3
required: false
preferenceType: enumeration
definition:
options:
off: Off
on: On
memory: Memory
default: memory
- title: Gang 4 Power-on State
name: relayStatus4
required: false
preferenceType: enumeration
definition:
options:
off: Off
on: On
memory: Memory
default: memory
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: ts0601-scene-switch-child-scene
components:
- id: main
capabilities:
- id: button
version: 1
categories:
- name: RemoteController
preferences:
- title: Mode
name: switchMode
description: Switch mode shows a controllable relay; scene mode shows a wireless button.
required: true
preferenceType: enumeration
definition:
options:
switch: Switch
scene: Scene
default: scene
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: ts0601-scene-switch-child
components:
- id: main
capabilities:
- id: switch
version: 1
- id: refresh
version: 1
categories:
- name: Switch
preferences:
- title: Mode
name: switchMode
description: Switch mode shows a controllable relay; scene mode shows a wireless button.
required: true
preferenceType: enumeration
definition:
options:
switch: Switch
scene: Scene
default: switch
- title: Countdown Seconds
name: countdownSeconds
description: Set the relay countdown in seconds; 0 disables the countdown.
required: false
preferenceType: integer
definition:
minimum: 0
maximum: 43200
default: 0
111 changes: 111 additions & 0 deletions drivers/SmartThings/zigbee-switch/src/test/test_tuya_multi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local BasicCluster = clusters.Basic
local OnOffCluster = clusters.OnOff
local t_utils = require "integration_test.utils"
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
local capabilities = require "st.capabilities"

local profile = t_utils.get_profile_definition("basic-switch.yml")

Expand All @@ -27,16 +28,126 @@ local mock_device = test.mock_device.build_test_zigbee_device({
},
fingerprinted_endpoint_id = 0x01
})
local mock_child = test.mock_device.build_test_child_device({
profile = profile,
parent_device_id = mock_device.id,
parent_assigned_child_key = "02",
label = "Generic Tuya second endpoint",
})

zigbee_test_utils.prepare_zigbee_env_info()

local function test_init()
mock_device:set_field("_configuration_version", 1, {persist = true})
test.mock_device.add_test_device(mock_device)
mock_child:set_field("_configuration_version", 1, {persist = true})
test.mock_device.add_test_device(mock_child)
end

test.set_test_init_function(test_init)

test.register_coroutine_test("Other Tuya switches retain their generic lifecycle handler", function()
local driver = test.driver_wrapper.driver_under_test
local dispatchers = driver.lifecycle_dispatcher:find_child_dispatchers(driver, mock_device, "doConfigure")
assert(#dispatchers == 1 and dispatchers[1].name == "tuya-multi")
assert(#dispatchers[1]:find_child_dispatchers(driver, mock_device, "doConfigure") == 0,
"Generic Tuya devices must not enter the KES606 sub-driver")
assert(not require("zemismart-kes606.can_handle")({}, driver, mock_device))
end)

test.register_message_test("Other Tuya switches retain standard switch commands and reports", {
{ channel = "capability", direction = "receive", message = {
mock_device.id, { capability = "switch", component = "main", command = "on", args = {} }
} },
{ channel = "zigbee", direction = "send", message = {
mock_device.id, OnOffCluster.server.commands.On(mock_device)
} },
{ channel = "devices", direction = "send", message = {
"register_native_capability_cmd_handler",
{ device_uuid = mock_device.id, capability_id = "switch", capability_cmd_id = "on" }
} },
{ channel = "zigbee", direction = "receive", message = {
mock_device.id, OnOffCluster.attributes.OnOff:build_test_attr_report(mock_device, true)
} },
{ channel = "capability", direction = "send", message =
mock_device:generate_test_message("main", capabilities.switch.switch.on()) },
{ channel = "devices", direction = "send", message = {
"register_native_capability_attr_handler",
{ device_uuid = mock_device.id, capability_id = "switch", capability_attr_id = "switch" }
} },
})

test.register_coroutine_test("Generic Tuya children configure through the root without the Tuya magic packet", function()
local driver = test.driver_wrapper.driver_under_test
assert(not require("tuya-multi.can_handle")({}, driver, mock_child))
assert(#driver.lifecycle_dispatcher:find_child_dispatchers(driver, mock_child, "doConfigure") == 0)
test.socket.zigbee:__expect_send({ mock_device.id,
OnOffCluster.attributes.OnOff:read(mock_device):to_endpoint(0x02) })
mock_child:expect_metadata_update({ provisioning_state = "PROVISIONED" })
test.socket.device_lifecycle:__queue_receive({ mock_child.id, "doConfigure" })
end)

test.register_coroutine_test("Generic Tuya children retain the root driver-switch decision", function()
local driver = test.driver_wrapper.driver_under_test
assert(#driver.lifecycle_dispatcher:find_child_dispatchers(driver, mock_child, "driverSwitched") == 0)
mock_child:expect_metadata_update({ provisioning_state = "NONFUNCTIONAL" })
test.socket.device_lifecycle:__queue_receive({ mock_child.id, "driverSwitched" })
end)

test.register_coroutine_test("Generic Tuya child commands and reports retain their own endpoint", function()
mock_child:expect_native_cmd_handler_registration("switch", "on")
test.socket.zigbee:__expect_send({ mock_device.id,
OnOffCluster.server.commands.On(mock_device):to_endpoint(0x02) })
test.socket.capability:__queue_receive({ mock_child.id,
{ capability = "switch", component = "main", command = "on", args = {} } })
test.wait_for_events()
mock_device:expect_native_attr_handler_registration("switch", "switch")
test.socket.capability:__expect_send(mock_child:generate_test_message("main", capabilities.switch.switch.on()))
test.socket.zigbee:__queue_receive({ mock_device.id,
OnOffCluster.attributes.OnOff:build_test_attr_report(mock_device, true):from_endpoint(0x02) })
end)

for _, endpoint_ids in ipairs({ { 1, 4 }, { 1 } }) do
local endpoints = {}
for _, ep in ipairs(endpoint_ids) do
endpoints[ep] = { id = ep, manufacturer = "_TZ123fas", server_clusters = { 0x0006 } }
end
local sample = test.mock_device.build_test_zigbee_device({
profile = profile, fingerprinted_endpoint_id = 1, zigbee_endpoints = endpoints,
})
local is_multi = #endpoint_ids > 1
local description = is_multi
and "Sparse Tuya endpoints 1 and 4 both configure through the generic Tuya handler"
or "Single-endpoint Tuya device retains root configuration without a magic packet"
test.register_coroutine_test(description, function()
local driver = test.driver_wrapper.driver_under_test
local actual = driver:get_device_info(sample.id)
assert((require("tuya-multi.can_handle")({}, driver, actual)) == is_multi)
assert(actual.zigbee_endpoints[2] == nil, "The test must preserve the gap in the SDK endpoint map")
test.socket.zigbee:__set_channel_ordering("relaxed")
for _, ep in ipairs(endpoint_ids) do
test.socket.zigbee:__expect_send({ sample.id, OnOffCluster.attributes.OnOff:read(sample):to_endpoint(ep) })
test.socket.zigbee:__expect_send({ sample.id,
OnOffCluster.attributes.OnOff:configure_reporting(sample, 0, 300):to_endpoint(ep) })
test.socket.zigbee:__expect_send({ sample.id,
zigbee_test_utils.build_bind_request(sample, zigbee_test_utils.mock_hub_eui,
OnOffCluster.ID, ep):to_endpoint(ep) })
end
if is_multi then
test.socket.zigbee:__expect_send({ sample.id,
zigbee_test_utils.build_attribute_read(sample, BasicCluster.ID,
{ 0x0004, 0x0000, 0x0001, 0x0005, 0x0007, 0xfffe }) })
end
sample:expect_metadata_update({ provisioning_state = "PROVISIONED" })
test.socket.device_lifecycle:__queue_receive({ sample.id, "doConfigure" })
end, {
test_init = function()
sample:set_field("_configuration_version", 1, {persist = true})
test.mock_device.add_test_device(sample)
end,
})
end

test.register_coroutine_test(
"lifecycle configure event should configure device",
function ()
Expand Down
Loading
Loading