Desktop controller for Tuya and Tuya Smart wall switches, built with Tauri + Rust + Vite + TypeScript.
This project focuses on a real gap in the Tuya ecosystem: fast desktop control for multi-gang light switches without depending on the mobile app UI. It uses the official Tuya Cloud API, models each switch channel explicitly, and is structured to grow into local automation and API integrations.
- Latest tagged release:
v0.1.0 - Windows installer:
Tuya Desk Controller_0.1.0_x64-setup.exe
Most Tuya desktop workflows are either:
- mobile-first and awkward on PC
- tied to generic device dashboards
- not explicit about multi-gang wall switches
- hard to extend into local automations
Tuya Desk Controller solves that by treating a device as a parent entity with one or more independent channels such as switch, switch_led, or switch_1 through switch_4.
- Official Tuya Cloud integration, no scraping or reverse engineering
- Explicit multi-gang support for 1, 2, 3, and 4 channel switches
- Per-channel ON/OFF control
- Local credential and preference storage
- Clean desktop UI optimized for fast control
- Alias support for devices and individual channels
- Action log for recent operations
- Modular backend prepared for
automation,scheduler, andlocal_api
- It is built around wall-switch channels as first-class entities, not just generic device cards.
- It keeps Tuya credentials and request signing in the Rust backend instead of leaking cloud logic into the frontend.
- It is intentionally structured so the same backend can later power a desktop app, local scheduler, and local HTTP API.
The current release supports:
- saving Tuya Cloud credentials and region settings
- testing connection from the Rust backend
- obtaining and refreshing Tuya access tokens
- listing linked devices
- loading device functions, status, and capabilities
- inferring channel count and controlability
- toggling channels independently
- refreshing device state
- handling common failures such as invalid credentials, expired tokens, offline devices, and partial Tuya responses
Tauri v2RustViteTypeScript- lightweight vanilla frontend architecture
src/
components/
pages/
services/
stores/
styles/
types/
utils/
src-tauri/
src/
commands/
config/
errors/
future/
models/
services/
capabilities/
icons/
docs/
architecture.md
- Architecture notes: docs/architecture.md
- Release artifacts: releases
The app is built around a normalized domain model instead of rendering raw Tuya payloads directly.
DeviceidnameonlinecategoryproductIdinferredTypegangCountchannelsrawmetadata
DeviceChannelcodedisplayNameindexcurrentStatecontrollable
TuyaFunctionTuyaStatusAppConfigDeviceAliasChannelAlias
Channel inference is centralized in a reusable function:
infer_device_channels(...)
Detection priority:
switch_1,switch_2,switch_3,switch_4switchswitch_led- fallback to boolean switch-like codes discovered in functions, status, or capabilities
Rules:
- if
switch_1andswitch_2exist, the device is treated as 2-gang - if
switch_1toswitch_3exist, it is treated as 3-gang - if
switch_1toswitch_4exist, it is treated as 4-gang - if only
switchorswitch_ledexists, it is treated as 1 channel - if a code appears in
statusbut not in functions or capabilities, it is shown as detected but not controllable
The core design decision is to keep Tuya-specific logic in Rust and keep the frontend bound only to normalized app models.
Current flow:
frontend -> Tauri commands -> Tuya service -> Tuya Cloud
This keeps:
- secrets out of the frontend
- signature and token handling in one place
- device normalization reusable across future transports
Detailed notes are in docs/architecture.md.
The app stores data in the application data directory:
config.json- Tuya credentials and base URL
metadata.json- device aliases, channel aliases, UI preferences
actions.jsonl- lightweight local action history
Node.js 22+npm 10+Rust stable- Windows with the MSVC toolchain
Validated locally with rustc 1.94.1.
npm install
npm run tauri devBuild a release:
npm run tauri buildUseful verification commands:
npm run test
npm run build
cd src-tauri && cargo testTypical values for Tuya Western America:
Base URL:https://openapi.tuyaus.comRegion label:Western America Data Center
You will also need:
Client IDClient Secret
If the settings screen is opened later and Client Secret is left blank, the app preserves the stored secret instead of overwriting it.
Near term:
- better refresh behavior and background polling
- more robust endpoint fallback handling
- more fixtures from real Tuya device payloads
- improved offline/degraded mode
Planned architecture extensions:
automationschedulerlocal_api
Longer term:
- local HTTP endpoints for external integrations
- scheduled actions
- simple scenes and rules
- Home Assistant and Node-RED friendly local bridges
The current backend is intentionally shaped so it can evolve without rewriting the MVP:
- today:
frontend -> Tauri commands -> TuyaService - later:
frontend -> commands -> ApplicationService -> TuyaService - later:
local_api -> ApplicationService -> TuyaService - later:
scheduler -> ApplicationService -> TuyaService
That avoids duplicating:
- token handling
- request signing
- Tuya error mapping
- channel inference
- device normalization
This is a real, functional MVP aimed at Tuya wall-switch control from Windows. It is not a UI mockup and not a generic template scaffold.
The strongest current use case is multi-gang switch control with a clean desktop workflow and a backend foundation ready for automation features.