private@homelab: ~/latest
local-first guides · privacy-aware · no noisy tracking
private@homelab:~$ cat guides/article.md
· ·
8–12 minutes
read

tinytuya: Tuya Local Control Without Home Assistant

tinytuya lets you script and verify local control of Tuya Wi-Fi devices directly, without Home Assistant, LocalTuya, or tuya-local in the loop.

If you’ve already worked through this site’s guide to pulling a Tuya local key, or you’re weighing LocalTuya against tuya-local for Home Assistant, there’s a decent chance what you actually want is smaller than either of those. Something that confirms a local_key works, pokes a plug from a cron job, or drives a Tuya device from a script that has nothing to do with Home Assistant at all.

tinytuya is built for exactly that. It’s an open-source Python module and command-line tool for controlling and monitoring Tuya Wi-Fi smart devices (plugs, switches, lights, covers) over the local network, with no Home Assistant integration anywhere in the chain.

This piece is built from tinytuya’s own GitHub documentation and examples, not a bench test. There’s no Tuya test-bench hardware behind this cluster yet, so the commands and script below reflect what the project documents, not behavior confirmed against a device here.

What tinytuya actually is (and isn’t)

tinytuya is a standalone Python module and CLI hosted at github.com/jasonacox/tinytuya. It talks to Tuya Wi-Fi devices over the local API directly. It is not a Home Assistant custom integration, and it doesn’t add entities to HA on its own.

One scope detail worth setting straight early, because it cuts against the framing of most write-ups: tinytuya is not local-only by construction. The project describes itself as a Python API and CLI for Tuya Wi-Fi devices “using a direct local area network (LAN) connection or the cloud (TuyaCloud API).” It ships an optional cloud module alongside the local one. Local LAN control is the default path and the reason most people install it, but the cloud path exists and you should know it’s there rather than assume every tinytuya call stays on your network.

The HA distinction matters because most existing coverage of local Tuya control, including this site’s own, sits inside the LocalTuya-vs-tuya-local decision. Both of those are Home Assistant integrations built to expose Tuya devices as HA entities. tinytuya sits a level below that. It’s the tool you’d reach for to talk to a device directly, whether or not HA is in the picture at all.

Note · before you start

The wizard step below still needs a Tuya IoT Platform Cloud Development project linked to your Smart Life or Tuya Smart app account — the same prerequisite covered in the local-key extraction guide. tinytuya automates the steps that come after that point. It doesn’t remove the account requirement itself.

When you’d reach for it instead of LocalTuya or tuya-local

A few situations favor a standalone tool over a full HA integration.

  • Verifying a local_key actually works before you commit to configuring LocalTuya or tuya-local
  • Scripting a device from a cron job, a shell script, or Node-RED’s exec node, none of which need HA in the loop
  • Controlling Tuya devices from a setup that doesn’t run Home Assistant at all, like a standalone dashboard or a lightweight automation box

None of that competes with LocalTuya or tuya-local for building actual HA automations. It’s a diagnostic and scripting layer that happens to use the same local-key mechanism underneath.

Getting local keys with the built-in wizard

Installation is a single command, and the package on PyPI ships both the Python API and the CLI.

terminal
pip install tinytuya

python -m tinytuya wizard

The wizard walks through the same Tuya IoT Platform Cloud Development flow already covered in the local-key extraction guide: the Client ID, Client Secret, and linked app account steps. What it adds is automation. Instead of hand-copying device IDs and local keys out of the Tuya IoT Platform console, the wizard queries the linked project and writes a devices.json file containing each device’s ID, IP address, and local_key.

If you’ve already done the manual local-key extraction from this site’s guide, running the wizard against the same Cloud Development project should produce the same keys, just without the copy-paste.

Scanning your LAN for Tuya devices without touching the cloud

A separate command handles discovery.

terminal
python -m tinytuya scan

The mechanism here is worth being precise about, because it’s the part that determines whether the command touches the internet at all. Tuya Wi-Fi devices announce themselves on the local network with UDP broadcast packets, and the scanner listens for those on UDP ports 6666, 6667 and 7000, collecting the IP addresses and device IDs that come back. Actual device control then runs over TCP 6668. Nothing in that path goes to Tuya’s cloud.

Those four port numbers are the practical takeaway if you segment your IoT gear: a scan run from outside the device VLAN won’t hear the broadcasts, and control calls need TCP 6668 open in the direction of the device. On its own a scan isn’t enough to control anything, since you still need a local_key from the wizard step, but it’s a fast way to confirm which devices actually respond on the LAN before you go further.

That local-only behavior is worth putting next to two other things this site has already covered: what data the Smart Life app normally sends when it does reach Tuya’s servers (the Smart Life data investigation), and what still works when you cut a Tuya device off from the internet entirely (the internet-blocking guide). A scan that stays on-LAN is a small, concrete example of the same local-first behavior those pieces describe from the network side.

Basic local control: a minimal script walkthrough

Once you have a device’s ID, IP, and local_key, control happens through direct local-socket calls. No cloud round trip during normal operation.

control.py
import tinytuya

d = tinytuya.OutletDevice(
    dev_id="eb1234567890abcdef1234",
    address="192.168.10.45",
    local_key="1234567890abcdef",
    version=3.4,
)

d.turn_on()
print(d.status())

The IDs, IP, and key above are placeholders, not real device data. Swap in whatever the wizard or scan wrote to your devices.json. The .turn_on(), .turn_off(), and .status() methods on the device object are the basic control surface documented in tinytuya’s examples. Other device types (covers, dimmers) expose their own equivalent methods.

The protocol-version gotcha

That version=3.4 argument in the script above is not optional in practice, even though it looks like it should have a sane default. Each Tuya device negotiates a specific local API protocol version somewhere in the 3.1 to 3.5 range, depending on the device and its firmware. Per the project’s own examples documentation, getting it wrong is a common failure mode, and it’s mostly silent: the device just doesn’t respond, rather than tinytuya returning a clear error telling you the version is off.

Warning

If a script or CLI command against a device times out with no response, check the protocol version before assuming the local_key or IP is wrong. A version mismatch produces the same symptom as a bad key: nothing happens, and there’s no useful error either way.

Of all the rough edges documented in tinytuya’s examples, this is the one worth flagging first to anyone scripting Tuya devices for the first time. A silent timeout that could mean three different things — wrong key, wrong IP, wrong protocol version — is the kind of failure mode that turns a five-minute task into a debugging session for no good reason.

Where this fits into a local-first Tuya setup

Tool Runs where Needs Home Assistant Best for
tinytuya Anywhere Python runs: script, cron, Node-RED exec node, standalone app No Verifying a local_key, scripting, quick diagnostics
LocalTuya Inside Home Assistant, as a custom integration Yes Full HA entities and automations for Tuya Wi-Fi devices
tuya-local Inside Home Assistant, as a custom integration Yes Same goal as LocalTuya, different community project and config approach

These aren’t competing choices so much as different layers. If the goal is HA automations, LocalTuya or tuya-local is still the right target. tinytuya is what you’d use to confirm the local_key and protocol version work at all before wiring either of those integrations up, or to keep a device scriptable somewhere that isn’t running HA in the first place.

FAQ

Do I need a Tuya cloud account to use tinytuya?
Yes, for the wizard step. It needs a Tuya IoT Platform Cloud Development project linked to your app account to pull device IDs and local keys, the same requirement as the manual local-key extraction process covered elsewhere on this site. Once you have the keys, day-to-day local control through the Python API or CLI doesn’t touch Tuya’s servers.

Is tinytuya safe to use with Tuya devices?
Be clear about what it is before you decide. tinytuya is an independent open-source project, not a Tuya product, and the local protocol it speaks is reverse-engineered rather than officially published for consumers. The project says so itself: its README credits protocol reverse-engineering work to community developers (clach04, jepsonrob, and uzlonewolf for the 3.4/3.5 session-key handling among them), and tinytuya descends from the earlier TuyAPI and pytuya projects. That’s normal for this corner of the ecosystem — LocalTuya and tuya-local rest on the same reverse-engineered foundation — but “reverse-engineered” is the accurate description, not “documented API.”

I found no published independent security audit of the tinytuya codebase, so treat any assurance beyond “the source is open and you can read it” as unearned. What you can verify yourself is behavioral: the local control calls and the LAN scan stay on your network, and the cloud contact is concentrated in the wizard step and the optional cloud module.

What’s the difference between tinytuya and LocalTuya?
LocalTuya and tuya-local are Home Assistant custom integrations. They use the same local-key, local-API mechanism tinytuya does, but they’re built to run inside HA and expose entities. tinytuya is the standalone version of that mechanism, usable with or without Home Assistant anywhere in the picture.

Can tinytuya control Tuya Zigbee devices, or only Wi-Fi ones?
Wi-Fi devices are what the project documents. Tuya Zigbee devices go through a Zigbee gateway rather than talking Wi-Fi directly, and that’s a different local-control path — covered in why Tuya Zigbee devices don’t need a local key.

Does tinytuya work without Home Assistant at all?
Yes. That’s the whole point of the tool. It’s a Python module and CLI first, with no dependency on Home Assistant being installed or running anywhere.

What this piece verified

This is a research-synthesis piece, not a bench test. There’s no owned Tuya hardware behind the tuya-sonoff-local-only cluster yet, so nothing here was run against a physical device for this article.

Sourced directly from tinytuya’s own GitHub README, its examples documentation, and the PyPI package listing: what the tool is, the install command, the wizard and scan commands, the contents the wizard writes to devices.json, the OutletDevice control methods and constructor form, the 3.1–3.5 protocol-version range, and the UDP/TCP ports the scan and control paths use. Also sourced from the README, and worth stating plainly because it’s often glossed over: the local protocol is community reverse-engineered, and the project ships an optional cloud module in addition to the local one.

What stays local, per that same documentation: every control call and the LAN scan, once the wizard step has already been completed. What still depends on Tuya’s cloud: that one wizard step itself — the Cloud Development project and linked app account that produces the local_key in the first place — plus anything you deliberately route through the cloud module.

If you’re building on this, two things are worth checking against your own setup before trusting a script. Confirm the exact CLI flags and output match the tinytuya version you actually installed, since project documentation can drift from a pinned release. And confirm the protocol version each of your devices negotiates, since that’s the detail most likely to produce a silent failure rather than a clear error.

local-firstHome Assistantno-cloud