Local-network control of Tuya-compatible devices, built in Rust with first-class Python bindings. Designed for fleets of hundreds to thousands of devices — a native Rust core handles the I/O while the Python facade releases the GIL on every blocking call, so threaded Python workers stay live.
Rust
cargo add rustuyaPython
pip install rustuyaRust (sync)
use rustuya::sync::Device;
let dev = Device::new("DEVICE_ID", "LOCAL_KEY");
dev.set_value(1, true)?; // turn on DP 1
println!("{:?}", dev.status()?); // read current DPS
for msg in dev.listener() { // real-time events
println!("{:?}", msg);
}Rust (async) — inside a tokio runtime
use rustuya::Device;
use futures_util::StreamExt;
#[tokio::main]
async fn main() -> Result<(), rustuya::TuyaError> {
let dev = Device::new("DEVICE_ID", "LOCAL_KEY");
dev.set_value(1, true).await?; // turn on DP 1
println!("{:?}", dev.status().await?); // read current DPS
let mut listener = dev.listener();
while let Some(msg) = listener.next().await { // real-time events
println!("{:?}", msg);
}
Ok(())
}Python
from rustuya import Device
dev = Device("DEVICE_ID", "LOCAL_KEY")
dev.set_value(1, True) # turn on DP 1
print(dev.status()) # read current DPS
for msg in dev.listener(): # real-time events
print(msg)- Local-only — talks directly to devices over LAN, no Tuya Cloud
- Rust core + Python bindings (PyO3) — same engine for both
- Built for fleet scale — per-device background tasks with automatic reconnection and exponential backoff
- Full protocol coverage — Tuya 3.1 / 3.2 / 3.3 / 3.4 / 3.5 + device22
Large-scale behavior is exercised against real mock devices — a 1000-device tuyamock fleet with actual TCP connections and handshakes, not just unit mocks:
- This repo stands up the 1000-device fleet and asserts every connection
establishes without deadlock under a bounded connect-concurrency cap, in CI
(
python/tests/test_fleet_scale.py). - The downstream rustuya-bridge goes further end-to-end: a 1000-device fleet onboards cleanly with zero retained orphans after a mass clear, and a separate test holds the fleet idle for 30s to prove the heartbeat keeps every connection alive (witnessed mock-side) before fanning a single name-addressed command out to the whole fleet.
It proves the fleet machinery (bounded connect with no deadlock, heartbeat survival, fan-out) holds with 1000 concurrent devices over real sockets, end to end.
See the Guide for the full API reference, design philosophy, and architecture notes.
The Tuya protocol layer in rustuya is derived from the specifications and error codes documented in tinytuya:
MIT