Skip to content

Commit ba8b84d

Browse files
committed
add support for tuya devices
1 parent d2ea624 commit ba8b84d

20 files changed

+2040
-0
lines changed

drivers/Unofficial/tuya/config.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: 'Tuya devices'
2+
packageKey: 'tuya_devices'
3+
permissions:
4+
zigbee: {}
5+
description: "Driver for uncertificed Tuya devices"
6+
isWWST: false
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
zigbeeManufacturer:
2+
- id: "_TZ3000_ja5osu5g/TS004F"
3+
deviceLabel: Tuya Button
4+
manufacturer: _TZ3000_ja5osu5g
5+
model: TS004F
6+
deviceProfileName: one-button-battery
7+
- id: "_TZ3000_8rppvwda/TS0041"
8+
deviceLabel: Tuya Button
9+
manufacturer: _TZ3000_8rppvwda
10+
model: TS0041
11+
deviceProfileName: one-button-battery
12+
- id: "_TZE284_nladmfvf/TS0601"
13+
deviceLabel: Tuya Curtain
14+
manufacturer: _TZE284_nladmfvf
15+
model: TS0601
16+
deviceProfileName: window-treatment-reverse
17+
- id: "_TZE200_mgxy2d9f/TS0601"
18+
deviceLabel: Tuya Motion Sensor
19+
manufacturer: _TZE200_mgxy2d9f
20+
model: TS0601
21+
deviceProfileName: motion-sensor
22+
- id: "_TZE200_ioxkjvuz/TS0601"
23+
deviceLabel: Tuya Gas Sensor
24+
manufacturer: _TZE200_ioxkjvuz
25+
model: TS0601
26+
deviceProfileName: smoke-detector
27+
- id: "_TZE204_mby4kbtq/TS0601"
28+
deviceLabel: Tuya Gas Sensor
29+
manufacturer: _TZE204_mby4kbtq
30+
model: TS0601
31+
deviceProfileName: smoke-detector
32+
- id: "_TZE200_uebojraa/TS0601"
33+
deviceLabel: Tuya Smoke Detector
34+
manufacturer: _TZE200_uebojraa
35+
model: TS0601
36+
deviceProfileName: smoke-battery
37+
- id: "_TZE204_h2rctifa/TS0601"
38+
deviceLabel: Tuya Switch 1
39+
manufacturer: _TZE204_h2rctifa
40+
model: TS0601
41+
deviceProfileName: basic-switch
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: basic-switch
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: switch
6+
version: 1
7+
- id: firmwareUpdate
8+
version: 1
9+
- id: refresh
10+
version: 1
11+
categories:
12+
- name: Switch
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: motion-sensor
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: motionSensor
6+
version: 1
7+
- id: battery
8+
version: 1
9+
- id: firmwareUpdate
10+
version: 1
11+
- id: refresh
12+
version: 1
13+
categories:
14+
- name: MotionSensor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: one-button-battery
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: button
6+
version: 1
7+
- id: battery
8+
version: 1
9+
- id: firmwareUpdate
10+
version: 1
11+
- id: refresh
12+
version: 1
13+
categories:
14+
- name: Button
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: smoke-battery
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: smokeDetector
6+
version: 1
7+
- id: batteryLevel
8+
version: 1
9+
- id: firmwareUpdate
10+
version: 1
11+
- id: refresh
12+
version: 1
13+
categories:
14+
- name: SmokeDetector
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: smoke-detector
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: smokeDetector
6+
version: 1
7+
- id: firmwareUpdate
8+
version: 1
9+
- id: refresh
10+
version: 1
11+
categories:
12+
- name: SmokeDetector
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: window-treatment-reverse
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: windowShade
6+
version: 1
7+
- id: windowShadeLevel
8+
version: 1
9+
- id: windowShadePreset
10+
version: 1
11+
- id: firmwareUpdate
12+
version: 1
13+
- id: refresh
14+
version: 1
15+
categories:
16+
- name: Blind
17+
18+
preferences:
19+
- preferenceId: reverse
20+
explicit: true
21+
- preferenceId: presetPosition
22+
explicit: true

drivers/Unofficial/tuya/src/init.lua

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
-- Copyright 2025 SmartThings
2+
--
3+
-- Licensed under the Apache License, Version 2.0 (the "License");
4+
-- you may not use this file except in compliance with the License.
5+
-- You may obtain a copy of the License at
6+
--
7+
-- http://www.apache.org/licenses/LICENSE-2.0
8+
--
9+
-- Unless required by applicable law or agreed to in writing, software
10+
-- distributed under the License is distributed on an "AS IS" BASIS,
11+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
-- See the License for the specific language governing permissions and
13+
-- limitations under the License.
14+
15+
local defaults = require "st.zigbee.defaults"
16+
local ZigbeeDriver = require "st.zigbee"
17+
local capabilities = require "st.capabilities"
18+
19+
local unofficial_tuya_driver_template = {
20+
supported_capabilities = {
21+
capabilities.refresh,
22+
capabilities.battery
23+
},
24+
sub_drivers = {
25+
require("tuya-button"),
26+
require("tuya-curtain"),
27+
require("tuya-motion-sensor"),
28+
require("tuya-smoke-detector"),
29+
require("tuya-switch")}
30+
}
31+
32+
defaults.register_for_default_handlers(unofficial_tuya_driver_template, unofficial_tuya_driver_template.supported_capabilities)
33+
local unofficial_tuya = ZigbeeDriver("unofficial_tuya", unofficial_tuya_driver_template)
34+
unofficial_tuya:run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
-- Copyright 2025 SmartThings
2+
--
3+
-- Licensed under the Apache License, Version 2.0 (the "License");
4+
-- you may not use this file except in compliance with the License.
5+
-- You may obtain a copy of the License at
6+
--
7+
-- http://www.apache.org/licenses/LICENSE-2.0
8+
--
9+
-- Unless required by applicable law or agreed to in writing, software
10+
-- distributed under the License is distributed on an "AS IS" BASIS,
11+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
-- See the License for the specific language governing permissions and
13+
-- limitations under the License.
14+
15+
local capabilities = require "st.capabilities"
16+
local clusters = require "st.zigbee.zcl.clusters"
17+
local t_utils = require "integration_test.utils"
18+
local test = require "integration_test"
19+
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
20+
local PRESENT_CMD_ID = 0x00fd
21+
local MFG_CODE = 0x110A
22+
local OnOff = clusters.OnOff
23+
local PowerConfiguration = clusters.PowerConfiguration
24+
local button = capabilities.button
25+
26+
local mock_device = test.mock_device.build_test_zigbee_device(
27+
{
28+
profile = t_utils.get_profile_definition("one-button-battery.yml"),
29+
zigbee_endpoints = {
30+
[1] = {
31+
id = 1,
32+
manufacturer = "_TZ3000_ja5osu5g",
33+
model = "TS004F",
34+
server_clusters = { 0x0006, 0x0001 }
35+
}
36+
}
37+
}
38+
)
39+
40+
zigbee_test_utils.prepare_zigbee_env_info()
41+
42+
local function test_init()
43+
test.mock_device.add_test_device(mock_device)
44+
zigbee_test_utils.init_noop_health_check_timer()
45+
end
46+
47+
test.set_test_init_function(test_init)
48+
49+
test.register_coroutine_test(
50+
"added lifecycle event",
51+
function()
52+
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "added" })
53+
test.socket.capability:__expect_send(
54+
mock_device:generate_test_message(
55+
"main",
56+
capabilities.button.supportedButtonValues({ "pushed", "held", "double" }, { visibility = { displayed = false } })
57+
)
58+
)
59+
test.socket.capability:__expect_send(
60+
mock_device:generate_test_message(
61+
"main",
62+
capabilities.button.numberOfButtons({ value = 1 }, { visibility = { displayed = false } })
63+
)
64+
)
65+
test.socket.capability:__expect_send(
66+
mock_device:generate_test_message("main", button.button.pushed({ state_change = false }))
67+
)
68+
end
69+
)
70+
71+
test.register_coroutine_test(
72+
"doConfigure lifecycle event",
73+
function()
74+
test.socket.zigbee:__set_channel_ordering("relaxed")
75+
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
76+
test.socket.zigbee:__expect_send({ mock_device.id, zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, OnOff.ID) })
77+
test.socket.zigbee:__expect_send({ mock_device.id, zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, PowerConfiguration.ID) })
78+
test.socket.zigbee:__expect_send({ mock_device.id, PowerConfiguration.attributes.BatteryPercentageRemaining:configure_reporting(mock_device, 30, 21600, 1) })
79+
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
80+
end
81+
)
82+
83+
test.register_coroutine_test(
84+
"OnOff cluster private command should result with sending pushed event",
85+
function()
86+
test.socket.zigbee:__queue_receive({ mock_device.id, zigbee_test_utils.build_custom_command_id(mock_device, OnOff.ID, PRESENT_CMD_ID, MFG_CODE, "\x00") })
87+
test.socket.capability:__expect_send(
88+
mock_device:generate_test_message("main", button.button.pushed({ state_change = true }))
89+
)
90+
end
91+
)
92+
93+
test.register_coroutine_test(
94+
"OnOff cluster any command (except On or Off) should result with sending pushed event",
95+
function()
96+
test.socket.zigbee:__queue_receive({
97+
mock_device.id,
98+
OnOff.server.commands.Toggle.build_test_rx(mock_device)
99+
})
100+
test.socket.capability:__expect_send(
101+
mock_device:generate_test_message("main", button.button.pushed({ state_change = true }))
102+
)
103+
end
104+
)
105+
106+
test.register_coroutine_test(
107+
"OnOff cluster On command should result with sending double event",
108+
function()
109+
test.socket.zigbee:__queue_receive({
110+
mock_device.id,
111+
OnOff.server.commands.On.build_test_rx(mock_device)
112+
})
113+
test.socket.capability:__expect_send(
114+
mock_device:generate_test_message("main", button.button.double({ state_change = true }))
115+
)
116+
end
117+
)
118+
119+
test.register_coroutine_test(
120+
"OnOff cluster private command 0x01 should result with sending double event",
121+
function()
122+
test.socket.zigbee:__queue_receive({ mock_device.id, zigbee_test_utils.build_custom_command_id(mock_device, OnOff.ID, PRESENT_CMD_ID, MFG_CODE, "\x01") })
123+
test.socket.capability:__expect_send(
124+
mock_device:generate_test_message("main", button.button.double({ state_change = true }))
125+
)
126+
end
127+
)
128+
129+
test.register_coroutine_test(
130+
"OnOff cluster Off command should result with sending held event",
131+
function()
132+
test.socket.zigbee:__queue_receive({
133+
mock_device.id,
134+
OnOff.server.commands.Off.build_test_rx(mock_device)
135+
})
136+
test.socket.capability:__expect_send(
137+
mock_device:generate_test_message("main", button.button.held({ state_change = true }))
138+
)
139+
end
140+
)
141+
142+
test.register_coroutine_test(
143+
"OnOff cluster private command 0x02 should result with sending held event",
144+
function()
145+
test.socket.zigbee:__queue_receive({ mock_device.id, zigbee_test_utils.build_custom_command_id(mock_device, OnOff.ID, PRESENT_CMD_ID, MFG_CODE, "\x02") })
146+
test.socket.capability:__expect_send(
147+
mock_device:generate_test_message("main", button.button.held({ state_change = true }))
148+
)
149+
end
150+
)
151+
152+
test.run_registered_tests()

0 commit comments

Comments
 (0)