225 lines
12 KiB
Markdown
225 lines
12 KiB
Markdown
---
|
||
created: 2026-05-26
|
||
modified: 2026-10-20
|
||
type: area
|
||
status: active
|
||
tags:
|
||
- homeassistant
|
||
- tuya-local
|
||
- zha
|
||
- zigbee
|
||
- networking
|
||
- hardware
|
||
aliases:
|
||
- Tuya Local Notes
|
||
---
|
||
# Tuya Local Integration Notes
|
||
|
||
> **Strategic position (2026-10-20):** Wi-Fi Tuya lighting is a fragile stack — it depends on a stable DHCP lease, a non-rotating local key, a live cloud account *and* an AP that stays up. Zigbee has none of those dependencies. The Dining Room and Finn's room have already moved to Zigbee and work reliably. **Preferred path for any new or replacement light: Zigbee, not Wi-Fi Tuya.**
|
||
|
||
---
|
||
|
||
## 1. General Operating Principles
|
||
|
||
### Static DHCP reservations are mandatory
|
||
Tuya Wi-Fi bulbs publish no mDNS/NetBIOS hostname. `tuya_local` addresses them by **fixed IPv4 on TCP 6668**.
|
||
- Any lease change silently breaks the entry.
|
||
- Confirmed in practice: the Harry Overhead bulb drifted `.41 → .34` and stopped working.
|
||
- Reserve every bulb MAC on the primary router `192.168.20.1` → DHCP Server → Static IP.
|
||
|
||
### Single TCP connection per device
|
||
Tuya MCUs accept **one** connection on port 6668.
|
||
- An open Tuya/Smart Life/Genio app on a phone holds it.
|
||
- Force-close the phone app before configuring in Home Assistant.
|
||
|
||
### `tuya-local` vs `localtuya`
|
||
- **`tuya-local`** (`make-all`) — current choice. Supports protocol 3.4/3.5, uses TinyTuya, device profiles.
|
||
- **`xZetsubou/hass-localtuya`** — legacy fork. Bundles an old pytuya without 3.4/3.5; fails "session key negotiation failed on step 1". Avoid.
|
||
|
||
### TRAP: `setup_retry` does not reset when you edit the device
|
||
Editing a device's IP/key in the UI **saves correctly but does not restart the retry timer**. The entry stays in `setup_retry` with exponential backoff (up to ~15 min), and the UI reports success with no visible change. This looks exactly like "the change didn't work".
|
||
|
||
**Always reload after any edit:**
|
||
- UI: Settings → Devices & Services → Tuya Local → device → **⋮ → Reload**
|
||
- API: `POST /api/config/config_entries/entry/<entry_id>/reload`
|
||
|
||
---
|
||
|
||
## 2. Device Inventory
|
||
|
||
### Wi-Fi Tuya bulbs (require DHCP reservation)
|
||
|
||
| Device | MAC | Current IP | Device ID | Local Key |
|
||
| :--- | :--- | :--- | :--- | :--- |
|
||
| **Sams Bed Lamp** | `70:89:76:07:8b:bc` | `192.168.20.29` | `bf58284019c309e738m9ri` | *unverified* |
|
||
| **Harry Overhead Light** | `70:89:76:07:83:e1` | `192.168.20.34` | `bf10d9430854f401e7vw2w` | `!6E$CV@BAJNQ:mj*` ✅ verified with tinytuya |
|
||
| **Harry Genio Lamp** | `c4:82:e1:0f:b6:ed` | `192.168.20.33` | *unverified* (`bfce09bd…` or `bf40c2aa…`) | *unverified* |
|
||
| **Finn Main Light** | `f8:17:2d:c5:76:79` | `192.168.20.17` | `bfc8b022833308dd52ivfh` | `xZN4VwUPbAS?8~q` |
|
||
| **Finn Genio A60** | *unknown* | *unknown* | `bfa6dbca41b5ae3000cply` | *unverified* |
|
||
|
||
**Orphan:** the old Dining Room Wi-Fi bulb `1c:69:20:a2:ce:38` (`bfc8b2ffde5654946dhint`, key `R_>@>f&u@=?$=Jd^`) is still powered somewhere at `192.168.20.10` but refuses port 6668. Its `tuya_local` entry was deleted 2026-10-20. **Physically find and remove it** — it holds a lease and generates error spam.
|
||
|
||
### Zigbee (ZHA) — preferred
|
||
|
||
| Device | IEEE | Model | Area |
|
||
| :--- | :--- | :--- | :--- |
|
||
| **Dining Room Light** | `a4:c1:38:7b:5d:d7:80:6a` | TS0505B (Tuya) | `dining_room` |
|
||
|
||
Entity is `light.dining_room_main_light_local` (friendly name "Overhead Light" — rename pending).
|
||
|
||
**Coordinator:** SMLIGHT SLZB-07, **USB-serial** (`/dev/serial/by-id/usb-SMLIGHT_…-if00-port0`, 115200 baud), EZSP/bellows 1.0.1, firmware 7.3.0.0 build 131. Not network-attached — ZHA does not depend on TCP to reach it.
|
||
|
||
**Network:** PAN `1A62`, **channel 22** (~2460 MHz), tx_power 8. 26 nodes known — 21 available.
|
||
|
||
---
|
||
|
||
## 3. Failure Mode: all bulbs drop out simultaneously
|
||
|
||
**Symptom:** every Wi-Fi Tuya bulb becomes `unavailable` at once. Log shows:
|
||
|
||
```
|
||
ERROR [custom_components.tuya_local.device] Failed to refresh device state for <name>.
|
||
Device reported error 901: Network Error: Unable to Connect
|
||
```
|
||
|
||
**Simultaneous failure of all devices means one shared dependency broke** — not N dead bulbs. Check in this order:
|
||
|
||
1. **Is the AP radio actually on?** (This was the actual root cause on 2026-10-20 — see §4.)
|
||
2. Did the router hand out new leases? (IP churn → all entries point at wrong addresses)
|
||
3. Did the bulbs' stored SSID/security change? (Tuya bulbs retry forever, never fall back)
|
||
4. Only then: keys, device IDs, cloud account.
|
||
|
||
**Not the cause, don't waste time here:** the IoT Core developer subscription. It is valid to **2027-04-06** and was `In service` throughout. Australia maps to the **Central Europe** data centre — do not try to switch data centres.
|
||
|
||
---
|
||
|
||
## 4. Failure Mode: AP radio disabled (2026-10-20)
|
||
|
||
The secondary NetComm AP (`192.168.20.254`, AP mode, DHCP off, SSID `NetComm Wireless`, WPA2-PSK/AES) had **`Enable Wireless` unchecked**. Radio off ⇒ the SSID the bulbs had stored no longer existed ⇒ they sat dark, retrying forever, invisible to ARP and DHCP.
|
||
|
||
**Why it fooled everything:** Tuya cloud still reported the bulbs `is_online: true` (stale), and the phone app still listed them. Only the LAN told the truth.
|
||
|
||
**Diagnosis:** hunt the bulbs' MAC OUIs in the ARP table. Absent entirely = not associated to anything, not an IP or key problem.
|
||
|
||
**Fix:** tick `Enable Wireless`, keep SSID/security identical, then **do not re-pair** — the bulbs rejoin on their own within minutes and the entries clear their `setup_retry` state after a reload.
|
||
|
||
**Settings that can block Tuya association on a NetComm AP:**
|
||
- `Clients Isolation` — must be **off**, or bulbs associate but no LAN host can reach them
|
||
- `Max Clients` — 16 on the secondary (firmware-locked); fine for ~5 bulbs, watch headroom
|
||
- Verified **non**-issues: `54g Rate 1 Mbps` and `XPress Technology Enable` did not prevent association
|
||
|
||
**Unknown:** why the radio disabled itself. If it recurs, check the AP's system log — likely a firmware reset or config-restore event.
|
||
|
||
---
|
||
|
||
## 5. Zigbee (ZHA) recovery
|
||
|
||
### A bulb that was removed from ZHA does not come back on an off/on
|
||
A single power cycle just reboots it; it keeps retrying its old (now unknown) network and stays silent.
|
||
|
||
**Five-cycle reset into pairing mode:**
|
||
1. Switch **ON**
|
||
2. Then 5×: **OFF, wait 5 s, ON, wait 5 s**
|
||
3. Finish **ON** and leave it
|
||
|
||
**Then open the network:**
|
||
- UI: Settings → Devices & Services → ZHA → **Add device**
|
||
- API: `zha/devices/permit` with `duration: 254` (**254 s is the maximum**; requesting more returns `success: false`)
|
||
|
||
The bulb rejoins within ~60 s. It comes back with a **new IEEE but the same `entity_id`**, and ZHA restores the area automatically.
|
||
|
||
### Gotcha: dining-room automations are AREA-scoped, not entity-scoped
|
||
`Switch Button Dining On` / `Off` (Zigbee remote `a4:c1:38:37:57:e9:2f:1e`) call `light.turn_on` / `light.turn_off` against **`area_id: dining_room`**.
|
||
|
||
If no light is assigned to that area, the physical button silently does nothing. **Deleting a light entity from an area looks exactly like "the Zigbee button broke".** Always check area assignment before diagnosing the remote.
|
||
|
||
---
|
||
|
||
## 6. Diagnostics — what to actually query
|
||
|
||
Home Assistant REST API (long-lived token from Profile → Security):
|
||
|
||
| Purpose | Endpoint |
|
||
| :--- | :--- |
|
||
| Integration health per entry | `GET /api/config/config_entries/entry` |
|
||
| **Logs (works on HA OS)** | `GET /api/hassio/core/logs` |
|
||
| ~~Logs~~ | `GET /api/error_log` → **404 on HA OS**, don't use |
|
||
| Entity states | `GET /api/states` |
|
||
| History incl. deleted entities | `GET /api/history/period/<iso>?filter_entity_id=<id>` |
|
||
| Force a retry after an edit | `POST /api/config/config_entries/entry/<id>/reload` |
|
||
|
||
`config_entries` state values: `loaded` = working · `setup_retry` = backoff, needs reload · `setup_error` = failed outright.
|
||
|
||
WebSocket (`ws://<ha>/api/websocket`) for registry detail: `zha/devices`, `config/device_registry/list`, `config/entity_registry/list`, `config/area_registry/list`, `zha/devices/permit`.
|
||
|
||
**Verify a bulb independently of HA** — this proves whether a key/ID is correct while HA still fails:
|
||
```python
|
||
import tinytuya
|
||
d = tinytuya.BulbDevice(dev_id='bf10d9430854f401e7vw2w',
|
||
address='192.168.20.34',
|
||
local_key='!6E$CV@BAJNQ:mj*', version=3.3)
|
||
print(d.status())
|
||
```
|
||
Note: a wrong `dev_id` against a live Tuya host returns `Unexpected Payload from Device`; an unreachable host returns `Network Error: Device Unreachable`.
|
||
|
||
**Reading port 6668 results:** `111` (RST) = host up, not listening — usually a *different* device holding that IP, not a "socket lockup". A **timeout** is the anomaly worth chasing (implies silent filtering).
|
||
|
||
---
|
||
|
||
## 7. Tuya Cloud API — local key retrieval
|
||
|
||
- **Portal:** <https://iot.tuya.com/>
|
||
- **Data centre:** Central Europe (`openapi.tuyaeu.com`) — correct for Australia, do not change
|
||
- **Access ID:** `pwwd8y3v7kj8j9c3nnj5`
|
||
- **IoT Core status:** `In service`, expires **2027-04-06**
|
||
|
||
**To get a local key:**
|
||
1. **Cloud → API Explorer**
|
||
2. Left panel → **Device Management** (not Device Control) → **Get Device Details**
|
||
3. Enter the Device ID → Submit
|
||
4. Copy `"local_key"` from the response
|
||
|
||
**Pitfalls:**
|
||
- `GET /v2.0/cloud/thing/<id>/model` returns the **datapoint model**, not the key
|
||
- The key is **not** shown in the web device table, the Debug Device page, or the logs — API Explorer is the only place
|
||
- Device ID after a re-pair is the **virtual ID from the phone app** (Device Information), and the key **rotates** — always re-fetch
|
||
- `code: 28841002` means the *resource pack* expired, not that the project is broken; `40001900 "No space permission"` means the app account isn't linked to the project space
|
||
|
||
---
|
||
|
||
## 8. Outstanding items
|
||
|
||
- [ ] **DHCP reservations** for the four Wi-Fi MACs (§2) — do this to prevent recurrence
|
||
- [ ] Physically remove the orphan Wi-Fi bulb at `192.168.20.10`
|
||
- [ ] Rename `light.dining_room_main_light_local` ("Overhead Light") → "Dining Room Light"
|
||
- [ ] **5 stale Zigbee entries** — see §9. These are *not* a mesh fault; three have been offline for 6–11 months
|
||
- [ ] **2.4 GHz interference:** ZHA is on **channel 22 (2460 MHz)**. Wi-Fi **ch 6** (2427–2447) does *not* collide — the secondary AP is correctly placed. Wi-Fi **ch 11** (2442–2462) **does** overlap: check what channel the primary router `Aussie Broadband 8729` is on and move it to ch 1 or 6 if it is on 11
|
||
- [ ] Find out why the AP radio disabled itself
|
||
|
||
---
|
||
|
||
## 9. Stale Zigbee entries (2026-10-20)
|
||
|
||
Five nodes show `unavailable`, but the `last_seen` dates show this is **bookkeeping, not a mesh problem**. Do not chase interference for these.
|
||
|
||
| Node | Type | Model | Area | Last seen | Read |
|
||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||
| Zigbee Router 1 | Router | esp32c6 | — | **2025-12-01** | retired/decommissioned |
|
||
| Harry Radar | Router | esp32c6 | bedroom_harry | **2025-11-28** | retired/decommissioned |
|
||
| Finn Room | EndDevice | esp32h2 | bedroom_finn | **2026-04-27** | retired/decommissioned |
|
||
| Zbeacon TS0505B | Router | TS0505B | — | 2026-10-06 | spare bulb, not installed |
|
||
| Hallway Bathroom Lights | EndDevice | esp32c6 | hallway | 2026-09-29 | recent — possibly still deployed |
|
||
|
||
**Action:** if the physical device is gone, delete the ZHA entry (Settings → Devices & Services → ZHA → device → Delete). If it still exists, power it on. Leaving them causes recurring "unavailable" noise.
|
||
|
||
Healthy baseline for comparison — everything else is fine:
|
||
|
||
| Node | Type | LQI | RSSI |
|
||
| :--- | :--- | :--- | :--- |
|
||
| Dining Room Light | Router | 140 | -65 |
|
||
| Router 2 Hallway | Router | 100 | -75 |
|
||
| Switch Button Dining Room | EndDevice | 128 | -68 |
|
||
| Bathroom light / Toilet light | Router | 72 | -82 |
|
||
|
||
LQI 72–180 across 15 routers is a normal, well-populated mesh. No orphaned routes, no single point of failure.
|