Files
obsidian-vault/300 areas/310 Home Assistant/Tuya Local Integration Notes.md

225 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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.