Skip to content

add support for tuya devices #2102

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
6 changes: 6 additions & 0 deletions drivers/Unofficial/tuya/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: 'Tuya devices'
packageKey: 'tuya_devices'
permissions:
zigbee: {}
description: "Driver for uncertificed Tuya devices"
isWWST: false
41 changes: 41 additions & 0 deletions drivers/Unofficial/tuya/fingerprints.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
zigbeeManufacturer:
- id: "_TZ3000_ja5osu5g/TS004F"
deviceLabel: Tuya Button
manufacturer: _TZ3000_ja5osu5g
model: TS004F
deviceProfileName: one-button-battery
- id: "_TZ3000_8rppvwda/TS0041"
deviceLabel: Tuya Button
manufacturer: _TZ3000_8rppvwda
model: TS0041
deviceProfileName: one-button-battery
- id: "_TZE284_nladmfvf/TS0601"
deviceLabel: Tuya Curtain
manufacturer: _TZE284_nladmfvf
model: TS0601
deviceProfileName: window-treatment-reverse
- id: "_TZE200_mgxy2d9f/TS0601"
deviceLabel: Tuya Motion Sensor
manufacturer: _TZE200_mgxy2d9f
model: TS0601
deviceProfileName: motion-sensor
- id: "_TZE200_ioxkjvuz/TS0601"
deviceLabel: Tuya Gas Sensor
manufacturer: _TZE200_ioxkjvuz
model: TS0601
deviceProfileName: smoke-detector
- id: "_TZE204_mby4kbtq/TS0601"
deviceLabel: Tuya Gas Sensor
manufacturer: _TZE204_mby4kbtq
model: TS0601
deviceProfileName: smoke-detector
- id: "_TZE200_uebojraa/TS0601"
deviceLabel: Tuya Smoke Detector
manufacturer: _TZE200_uebojraa
model: TS0601
deviceProfileName: smoke-battery
- id: "_TZE204_h2rctifa/TS0601"
deviceLabel: Tuya Switch 1
manufacturer: _TZE204_h2rctifa
model: TS0601
deviceProfileName: basic-switch
12 changes: 12 additions & 0 deletions drivers/Unofficial/tuya/profiles/basic-switch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: basic-switch
components:
- id: main
capabilities:
- id: switch
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: Switch
14 changes: 14 additions & 0 deletions drivers/Unofficial/tuya/profiles/motion-sensor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: motion-sensor
components:
- id: main
capabilities:
- id: motionSensor
version: 1
- id: battery
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: MotionSensor
14 changes: 14 additions & 0 deletions drivers/Unofficial/tuya/profiles/one-button-battery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: one-button-battery
components:
- id: main
capabilities:
- id: button
version: 1
- id: battery
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: Button
14 changes: 14 additions & 0 deletions drivers/Unofficial/tuya/profiles/smoke-battery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: smoke-battery
components:
- id: main
capabilities:
- id: smokeDetector
version: 1
- id: batteryLevel
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: SmokeDetector
12 changes: 12 additions & 0 deletions drivers/Unofficial/tuya/profiles/smoke-detector.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: smoke-detector
components:
- id: main
capabilities:
- id: smokeDetector
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: SmokeDetector
22 changes: 22 additions & 0 deletions drivers/Unofficial/tuya/profiles/window-treatment-reverse.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: window-treatment-reverse
components:
- id: main
capabilities:
- id: windowShade
version: 1
- id: windowShadeLevel
version: 1
- id: windowShadePreset
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: Blind

preferences:
- preferenceId: reverse
explicit: true
- preferenceId: presetPosition
explicit: true
34 changes: 34 additions & 0 deletions drivers/Unofficial/tuya/src/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- 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.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

local defaults = require "st.zigbee.defaults"
local ZigbeeDriver = require "st.zigbee"
local capabilities = require "st.capabilities"

local unofficial_tuya_driver_template = {
supported_capabilities = {
capabilities.refresh,
capabilities.battery
},
sub_drivers = {
require("tuya-button"),
require("tuya-curtain"),
require("tuya-motion-sensor"),
require("tuya-smoke-detector"),
require("tuya-switch")}
}

defaults.register_for_default_handlers(unofficial_tuya_driver_template, unofficial_tuya_driver_template.supported_capabilities)
local unofficial_tuya = ZigbeeDriver("unofficial_tuya", unofficial_tuya_driver_template)
unofficial_tuya:run()
152 changes: 152 additions & 0 deletions drivers/Unofficial/tuya/src/test/test_tuya_button.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
-- 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.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

local capabilities = require "st.capabilities"
local clusters = require "st.zigbee.zcl.clusters"
local t_utils = require "integration_test.utils"
local test = require "integration_test"
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
local PRESENT_CMD_ID = 0x00fd
local MFG_CODE = 0x110A
local OnOff = clusters.OnOff
local PowerConfiguration = clusters.PowerConfiguration
local button = capabilities.button

local mock_device = test.mock_device.build_test_zigbee_device(
{
profile = t_utils.get_profile_definition("one-button-battery.yml"),
zigbee_endpoints = {
[1] = {
id = 1,
manufacturer = "_TZ3000_ja5osu5g",
model = "TS004F",
server_clusters = { 0x0006, 0x0001 }
}
}
}
)

zigbee_test_utils.prepare_zigbee_env_info()

local function test_init()
test.mock_device.add_test_device(mock_device)
zigbee_test_utils.init_noop_health_check_timer()
end

test.set_test_init_function(test_init)

test.register_coroutine_test(
"added lifecycle event",
function()
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "added" })
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main",
capabilities.button.supportedButtonValues({ "pushed", "held", "double" }, { visibility = { displayed = false } })
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main",
capabilities.button.numberOfButtons({ value = 1 }, { visibility = { displayed = false } })
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button.button.pushed({ state_change = false }))
)
end
)

test.register_coroutine_test(
"doConfigure lifecycle event",
function()
test.socket.zigbee:__set_channel_ordering("relaxed")
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
test.socket.zigbee:__expect_send({ mock_device.id, zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, OnOff.ID) })
test.socket.zigbee:__expect_send({ mock_device.id, zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, PowerConfiguration.ID) })
test.socket.zigbee:__expect_send({ mock_device.id, PowerConfiguration.attributes.BatteryPercentageRemaining:configure_reporting(mock_device, 30, 21600, 1) })
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
)

test.register_coroutine_test(
"OnOff cluster private command should result with sending pushed event",
function()
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") })
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button.button.pushed({ state_change = true }))
)
end
)

test.register_coroutine_test(
"OnOff cluster any command (except On or Off) should result with sending pushed event",
function()
test.socket.zigbee:__queue_receive({
mock_device.id,
OnOff.server.commands.Toggle.build_test_rx(mock_device)
})
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button.button.pushed({ state_change = true }))
)
end
)

test.register_coroutine_test(
"OnOff cluster On command should result with sending double event",
function()
test.socket.zigbee:__queue_receive({
mock_device.id,
OnOff.server.commands.On.build_test_rx(mock_device)
})
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button.button.double({ state_change = true }))
)
end
)

test.register_coroutine_test(
"OnOff cluster private command 0x01 should result with sending double event",
function()
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") })
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button.button.double({ state_change = true }))
)
end
)

test.register_coroutine_test(
"OnOff cluster Off command should result with sending held event",
function()
test.socket.zigbee:__queue_receive({
mock_device.id,
OnOff.server.commands.Off.build_test_rx(mock_device)
})
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button.button.held({ state_change = true }))
)
end
)

test.register_coroutine_test(
"OnOff cluster private command 0x02 should result with sending held event",
function()
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") })
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button.button.held({ state_change = true }))
)
end
)

test.run_registered_tests()
Loading
Loading