Files
bf-infra/docs/tp17-shelly-go-runbook.md

168 lines
16 KiB
Markdown
Raw Permalink 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.
# TP17 — Shelly VLAN1→VLAN30 Go-Runbook
**Stand: 2026-07-11 (live-verifiziert, go-ready).** Morgen = nur noch ausführen.
Steuerung/Scope: `docs/teilprojekte.md` → TP17 · Tooling: `mcp/shelly/` · Memory: `project_shelly_migration_tp17`.
---
## TL;DR
Alles Nicht-Disruptive ist gestellt. **Morgen früh nur noch:** die **16 verbliebenen VLAN1-Shellys**
per `WiFi.SetConfig` auf `dirtyvader-iot` umklemmen (Batch), je Gerät auf `.30.x` verifizieren,
dann HA/ioBroker gegenchecken. Pilot ist faktisch schon bestanden (5 Geräte laufen seit ~2,5 Tagen stabil auf VLAN30).
---
## Ist-Stand Shelly-Flotte (live 2026-07-11)
26 Shellys total (25 mit MAC + s24 ohne MAC-Eintrag). Verteilung **jetzt**:
| Gruppe | Anzahl | Geräte | Status |
|---|---|---|---|
| ✅ **Schon auf VLAN30** | 5 (+1?) | s14 `.30.221`, s16 `.30.223`, s18 `.30.225`, s22 `.30.202`, s23 `.30.203` (+ `.30.204` unbekannt, evtl. s24) | stabil ~2,5 d, reservierte IP getroffen |
| 🔜 **Noch auf VLAN1 → morgen** | 16 | s4–s11, s13, s15, s17, s19, s20, s21, s25, s26 | live auf `10.40.41.2xx`, alle Gen3/4 |
| ⏸️ **Aus (später)** | 4 | s1/s2/s3 `weihnachten_*` (saisonal ausgesteckt), s12 `dieleug_steckdosen` | migrieren beim nächsten Einstecken |
## Was heute schon gestellt ist (Prereqs ✅)
- **DHCP-Reservierungen** aller Shellys in `dhcp-v30` angelegt (letztes Oktett erhalten, `.30.2xx`, außerhalb Pool `.100–.200` → kollisionsfrei).
- **s6-Reservierung `.30.213` MAC korrigiert** (war falsch `0C:72:74:36:90:9E` → real `D0:CF:13:D8:18:FC`) — 2026-07-11.
- **IOT-SSID live verifiziert:** `w2-iot` (ap1, 2,4 GHz, VLAN30) `running:true`, **8 echte Clients** authorized, Signal −26…−68 dBm. ⚠️ `wifi2`-Master zeigt `running:false` — **kosmetisch**, VAP trägt nachweislich Traffic.
- **Tooling** `mcp/shelly/` fertig: `discover.py` (read-only Sweep) + `server.py` (MCP: `discover`/`get`/`set_sta`/`verify`/`reservation_plan`), **PSK in `.env` gesetzt**.
## Migrationsliste — die 16 (morgen)
Ziel-IP = letztes Oktett erhalten; alle Reservierungen existieren & MAC-verifiziert.
| id | Name | jetzt (VLAN1) | MAC | Ziel (VLAN30) |
|---|---|---|---|---|
| s4 | kueche_mikrowelle | 10.40.41.211 | e4:b0:63:e4:0f:30 | 10.40.30.211 |
| s5 | kueche_backofen | 10.40.41.212 | d8:85:ac:0c:5c:c8 | 10.40.30.212 |
| s6 | kueche_kuehlschrank | 10.40.41.213 | d0:cf:13:d8:18:fc | 10.40.30.213 |
| s7 | kueche_tiefkuehlschrank | 10.40.41.214 | d0:cf:13:d9:94:e8 | 10.40.30.214 |
| s8 | kueche_geschirrspueler | 10.40.41.215 | e4:b0:63:fa:fd:e0 | 10.40.30.215 |
| s9 | wohnzimmer_sideboard | 10.40.41.216 | e8:f6:0a:61:88:08 | 10.40.30.216 |
| s10 | bad_sonos | 10.40.41.217 | e4:b0:63:fc:57:e4 | 10.40.30.217 |
| s11 | kinderzimmer_sonos | 10.40.41.218 | e4:b0:63:fa:6c:00 | 10.40.30.218 |
| s13 | arbeitszimmer_technik | 10.40.41.220 | 48:f6:ee:dd:6a:f4 | 10.40.30.220 |
| s15 | technik_pve | 10.40.41.222 | e4:b0:63:fa:d5:34 | 10.40.30.222 |
| s17 | bad_steckdosen | 10.40.41.224 | 10:20:ba:fb:6d:64 | 10.40.30.224 |
| s19 | kueche_steckdosen3 | 10.40.41.226 | 10:20:ba:fb:70:fc | 10.40.30.226 |
| s20 | kueche_steckdosen2 | 10.40.41.206 | 98:a3:16:7c:5f:ec | 10.40.30.206 |
| s21 | kueche_steckdosen4 | 10.40.41.207 | 20:6e:f1:01:f9:f8 | 10.40.30.207 |
| s25 | bad_lichtschalter | 10.40.41.205 | 98:a3:16:79:38:50 | 10.40.30.205 |
| s26 | bad_luefter | 10.40.41.233 | 98:a3:16:7a:a3:54 | 10.40.30.233 |
---
## GO-Sequenz (morgen)
1. **Baseline-Sweep** (beweist Ausgangslage): `python3 discover.py 10.40.41.180-250` → 16 erwartet.
2. **Frischer Pilot 1 Gerät** (unkritisch, always-on): **s26 bad_luefter** `.41.233`.
- `set_sta` (confirm) → Gerät rebootet → nach ~30–60 s auf `dirtyvader-iot`.
- **Verify:** `python3 discover.py 10.40.30.233` → muss antworten; SSID=`dirtyvader-iot`, IP `.30.233`.
- Gegencheck HA/ioBroker: Entity `bad_luefter` wieder erreichbar (neue IP).
3. **Batch Rest** (15) — Küche-Cluster ggf. in 2 Wellen, damit nicht die halbe Küche gleichzeitig kurz offline ist.
Reihenfolge unkritisch; Kühl-/Tiefkühlschrank (s6/s7) sind nur kurz WLAN-offline (Kompressor läuft weiter).
4. **Verify-Sweep:** `python3 discover.py 10.40.30.100-250` → erwartet 5 (alt) + 16 (neu) = 21 (+ evtl. s24).
5. **HA/ioBroker re-point** aller 16 (+ die 5 bereits migrierten gegenchecken): neue `.30.2xx`-IPs.
6. **cudy-Alt-WLAN abbauen** (erst wenn keine Shelly/Streu-Clients mehr in VLAN1 hängen).
7. **Nachziehen:** `inventory/bf.csv`, `docs/ip-plan.md`, ap1 `bf.lan`-DNS (falls Shelly-Namen), SSoT TP17.
### Pro Gerät: 3 Änderungen (nicht nur 2!)
Der User-Auftrag ist **WLAN + IP + Geräte-Passwort** vereinheitlichen — nicht nur WLAN/IP.
Reihenfolge je Gerät, damit Recovery immer möglich bleibt:
**(1) WLAN→VLAN30 mit STATISCHER IP** (Gen2+ RPC, identisch für alle Gen3/4, `auth=off`).
**Entscheidung (User 2026-07-11): alle Shellys bekommen ausnahmslos eine statische IP** (letztes Oktett erhalten → `10.40.30.<octet>`, gw/dns `.30.1`), **plus** die passende **in-pool** DHCP-Reservierung als Backup:
```bash
curl -s -X POST http://<VLAN1-IP>/rpc \
-H 'Content-Type: application/json' \
-d '{"id":1,"method":"WiFi.SetConfig","params":{"config":{"sta":{
"ssid":"dirtyvader-iot","pass":"<IOT-PSK>","enable":true,
"ipv4mode":"static","ip":"10.40.30.<octet>","netmask":"255.255.255.0",
"gw":"10.40.30.1","nameserver":"10.40.30.1"}}}}'
curl -s -X POST http://<VLAN1-IP>/rpc -d '{"id":1,"method":"Shelly.Reboot"}'
```
Oder via `shelly-mcp` **`set_sta {ip, confirm:true}`** (Default `static=true`, rechnet die Ziel-IP aus dem Oktett).
→ Gerät kommt exakt auf `10.40.30.<octet>`. **Verify** (`discover.py <neue-IP>`).
**Pool-Konvention:** `pool-v30` wurde 2026-07-11 auf `10.40.30.100-.250` erweitert, damit die Reservierungen (`.202–.244`) **in-pool** liegen (eine DHCP-Neuconfig liefert dann sofort brauchbare IPs).
**(2) Einheitliches Geräte-Passwort** (erst NACH erfolgreichem VLAN30-Verify):
```bash
# realm = Geräte-ID (shellyXXXX-<mac>), ha1 = SHA256("admin:<realm>:<PW>")
curl -s -X POST http://<VLAN30-IP>/rpc \
-d '{"id":1,"method":"Shelly.SetAuth","params":{"user":"admin","realm":"<device-id>","ha1":"<sha256>"}}'
```
⚠️ **Realm ist geräteindividuell** (`shellyplugsg3-<mac>` etc.) → `ha1` muss pro Gerät berechnet werden (gleiches Klartext-PW, unterschiedlicher Hash). Das gehört als Tool `set_auth` in `shelly-mcp/server.py` (heute noch nicht drin — die Migration selbst braucht es nicht, nur der Passwort-Schritt).
**(3) HA-Credential nachziehen:** Shelly-Integration im HA-Config-Entry Username `admin` + PW eintragen (sonst werden die Entities nach Schritt 2 `unavailable`). **Entity-IDs bleiben unverändert** (HA keyt per Geräte-MAC, nicht IP/PW) — genau wie gefordert.
**ENTSCHIEDEN (User 2026-07-11): eigenes Shelly-Admin-PW**, bewusst **getrennt** vom WLAN-PSK.
→ Christoph liefert den Wert bzw. legt ihn selbst als `SHELLY_DEVICE_PW` in `mcp/shelly/.env` ab (chmod600),
nicht ins Repo/Log. PW/PSK niemals spiegeln. (User `admin`, Klartext-PW einheitlich, `ha1` pro Gerät gehasht.)
Alternativ Schritt 1 via **shelly-mcp** `set_sta {ip, confirm:true}` (Dry-Run ohne `confirm`); IOT-PSK in `mcp/shelly/.env` (`SHELLY_IOT_PSK`).
---
## Risiken & Rollback
- **Falsches PSK → Gerät in AP-Mode** (`Shelly*`-SSID) → physischer Reset am Gerät nötig. Deshalb: **immer erst Dry-Run/1 Pilot**, PSK aus `.env` (verifiziert gesetzt, len 12).
- **Gerät kurz offline** während Reboot (~30–60 s) → HA/ioBroker zeigen es kurz `unavailable`, kommt selbst zurück.
- **Coverage:** IOT ist 2,4-GHz-only. ap1 `w2-iot` trägt bereits 8 Clients gut (−26…−68 dBm), ap2 spiegelt die SSID. Küche/Bad = nah an ap1 → unkritisch. Falls ein Gerät nach `set_sta` **nicht** auf `.30.x` auftaucht: an VLAN1-IP zurück-pingen (kommt es nach Timeout in AP-Mode zurück?), Signal/Standort prüfen.
- **Rollback je Gerät:** `WiFi.SetConfig` zurück auf altes cudy-WLAN/SSID (nur solange cudy noch steht). Nach cudy-Abbau kein WLAN-Rollback mehr → dann nur AP-Mode/Reset.
---
## HA-Cross-Check (2026-07-11, alle Geräte mit IPv4 in Home Assistant)
Sweep über `configuration_url` aller HA-Devices (Container/Portainer/pve-„Geräte" zeigen alle auf `.41.100`/`.126` = deren UI-URL, keine echten LAN-Hosts → ignoriert). Physische LAN-Geräte:
- **Shellys:** vollständig deckungsgleich mit dem Netz-Sweep. **`.30.204` aufgelöst:** = Shelly **„Bad/Spiegelschrank" (Shelly Plus RGBW PM, `14:33:5c:d8:2d:7c`)** → sehr wahrscheinlich das inventarlose **s24**; damit **6 Shellys** bereits auf VLAN30 (s14/16/18/22/23/24). ✅ Inventar `bf.csv` ergänzen (s24-MAC).
- **Naming-Drift (nur kosmetisch, KEINE Entity-Änderung ohne Freigabe):** s10 HA-Label „schlafzi/teststecker" vs. Inventar `bad_sonos`; s18-Label „küche/steckdose 3-2" bei `.30.202` (=s22-Reservierung). Vor cudy-Abbau Labels/Inventar konsolidieren.
- **Sonos ×7** → VLAN40 `.40.19x` (korrekt, MEDIA). **Brother MFC-J5720DW** `.10.199` (VLAN10), **Brother PT-P750W** `.30.5` (VLAN30) — beide korrekt platziert. **CCU/RaspberryMatic** `.20.232` (VLAN20/INFRA, korrekt).
- ⚠️ **`tasmota_plug9` (Nous A1, Tasmota)** in HA mit `.41.206` — aber `.41.206` gehört per ARP s20 (Shelly), also **stale**. **Vollständiger Scan 2026-07-11:** Tasmota-MAC `08:f9:e0:72:12:b6` ist **nicht in ap1-ARP** → **kein aktives Gerät** (bestätigt User-Ruling) → **kein VLAN30-Mover; stale HA-Device entfernen** (User).
**Nachtrag — vollständiger HA-Device-Scan (alle Geräte mit MAC-Connection, nicht nur `configuration_url`):** fördert **2 Media-Geräte ohne IP in der URL** zutage, die der erste Sweep verpasst hat:
- **Apple TV 4K** (`2a:77:fe:14:ad:5f`, „Wohnzimmer (2)") und **Philips TV 50PUS7304** (`44:d8:78:30:b8:d5`) — beide **aktuell aus/Standby** (nicht in ap1-ARP). Zielnetz = **VLAN40/MEDIA**. ⚠️ **Vor cudy-Abschuss verifizieren**, ob sie schon auf `dirtyvader-media` hängen oder **noch am cudy/VLAN1** — sonst fällt der Wohnzimmer-TV/AppleTV mit dem cudy weg. (Einschalten → ap1-ARP prüfen.)
- **Apple TV 4K = sehr wahrscheinlich der aktive Thread-Border-Router** für die bestehende IKEA-Matter-over-Thread-Flotte (22 Matter-Geräte in HA) → relevant für TP17.1 (Thread-Onboarding liefe über diesen BR bzw. einen HA-eigenen OTBR).
- Alle übrigen MAC-Geräte sind zugeordnet: Shellys, Sonos ×7 (VLAN40), Brother MFC (VLAN10). Radio-only-Geräte (Zigbee/Matter-over-Thread/BLE) haben keine IP → kein VLAN-Thema.
## Netzweite Aufstellung → „ist VLAN1 danach leer?" (Netz-Wahrheit via ap1 ARP+DHCP, 2026-07-11)
**Live in VLAN1 (bridge), klassifiziert nach Ziel-VLAN** (Phantom-/`failed`-ARP-Leichen weggelassen):
| Gerät | VLAN1-IP | MAC | Ziel | Move-Trigger |
|---|---|---|---|---|
| **16 Shellys** | .41.205–.233 | s.o. | **VLAN30** | morgen (dieses Runbook) |
| SwitchBot floor-lamp | .41.184 | b8:f8:62:09:f2:04 | **VLAN30** | Nicht-Shelly (Reservierung `.30.227` steht) |
| Twinkly-Girlande | (saisonal off) | 98:f4:ab:2c:a7:88 | **VLAN30** | Nicht-Shelly (Reservierung `.30.228` steht); beim Einstecken |
| Creality Ender (3D-Drucker) | (aktuell off?) | ? | **VLAN30** | Nicht-Shelly, „fehlt noch" (User) — beim nächsten Einschalten verorten + reservieren |
| MacBook-Air-M5 (Christoph) | .41.182 | c0:c7:db:de:71:be | **VLAN10** | joint `dirtyvader-main` / cudy-Abbau |
| iPhone (Christoph) | .41.186 | f2:62:4c:e1:af:09 | **VLAN10** | dito |
| pve-Host-Mgmt | .41.100 | 00:d0:b4:04:19:24 | **VLAN20** | Host-Migration (nach Shellys) |
| sw1 ALLNET | .41.11 | 00:08:54:71:98:2d | **VLAN99** | MGMT-Cutover (eigener Schritt) |
| sw2 Omada | .41.12 | a8:29:48:ee:b9:04 | **VLAN99** | MGMT-Cutover |
| ap2-Mgmt | .41.2 | — | **VLAN99** | MGMT-Cutover |
| ❓ **unidentifiziert** | .41.246 | 80:af:ca:7d:e5:b1 | **?** | **morgen identifizieren** (nicht in HA) |
**Nicht-Shelly-Mover (User-bestätigt 2026-07-11):** SwitchBot floor-lamp + Twinkly-Girlande + Creality Ender (fehlt noch, off). **Withings = schon übersiedelt** (nicht mehr offen). **Tasmota = existiert nicht mehr** (kein aktives Gerät). **EOS-Smartwatches / eBook-Reader** kommen später und macht **Christoph selbst** (eBook-Reader gehört lt. VLAN-Modell eher nach **VLAN50/UNTRUSTED**, nicht VLAN30) — nicht Teil dieses Go.
**`.41.246`/`.247` identifiziert (User):** = **cudy1/cudy2** (die alten WLAN-APs, zum Abbau freigegeben, nur für VLAN1). Kein Fremdkörper → das ist der cudy-Teardown selbst.
**Verdikt:** Nach TP17 (16 Shellys + SwitchBot/Twinkly/Ender → VLAN30) + pve-Host → VLAN20 + Christophs Endgeräte → VLAN10 ist VLAN1 **frei von Endgeräten**. **Prüf-Prereq vor cudy-Abschuss:** **Apple TV + Philips TV** (aktuell aus) einschalten und verifizieren, dass sie auf `dirtyvader-media`/VLAN40 hängen und **nicht** noch am cudy/VLAN1 — sonst mit umziehen. **Nicht** automatisch leer bleiben: die **Switch-/AP-Mgmt-IPs (sw1/sw2/ap2)** sind *by design* interim in VLAN1 bis zum separaten **MGMT-VLAN99-Cutover** (kein TP17-Scope). `defconf`/pool-v1-DHCP erst abschalten, wenn all das erledigt ist.
## Projektion: Gen4-Shellys auf Thread/Matter (experimentell, separate Spur)
User-Wunsch: prüfen, ob die **Gen4-Geräte** (S4PL Power-Strip-4, S4SW 2PM Gen4 — ~11 Stück: s9,s13,s16,s17,s18,s19,s20,s21,s24?,s25,s26) auf **Thread-Firmware geflasht** und dann via **Matter-over-Thread** in HA eingebunden werden können.
- **Voraussetzung/Recherche (offen):** Ob/welche Shelly-Gen4-Modelle offizielle Thread-/Matter-Firmware haben (Shelly kündigte Matter/Thread für Gen4 an — pro Modell verifizieren, Firmware-Verfügbarkeit + Rückweg zu WiFi-FW prüfen). **Ohne verfügbare Firmware: Spur ruht.**
- **Infra-Voraussetzung:** ein **Thread Border Router** (HA-fähig: z.B. Home Assistant Connect ZBT / SkyConnect mit OTBR, oder Apple-TV/HomePod als BR) — im Netz vorhanden? Muss geklärt werden.
- **Konsequenz Netz:** Thread-Geräte hängen **NICHT** im WLAN/VLAN30, sondern im Thread-Mesh (802.15.4) → sie fallen aus der `dirtyvader-iot`-WLAN-Migration heraus. D.h. **erst entscheiden welche Gen4 auf Thread gehen**, bevor man sie unnötig aufs WLAN migriert (sonst doppelte Arbeit).
- ⚠️ **KONFLIKT mit „Entities bleiben unverändert":** Matter-Onboarding erzeugt in HA **neue** Entities (andere Integration) → die alten Shelly-WiFi-Entity-IDs würden brechen. Vor jedem Thread-Flash klären, ob Entity-Stabilität oder Thread-Migration Vorrang hat (ggf. Entity-`unique_id` manuell übernehmen / bewusst neu benennen). **Solange „Entities unverändert" gilt, ist die Thread-Spur nachrangig zur WiFi-Migration.**
- **Reihenfolge-Empfehlung:** morgen erst die **WiFi+IP+PW-Migration** komplett durchziehen (stabiler Zielzustand, HA unverändert). Thread als **eigene Folge-Spur (TP17.1)** danach evaluieren — nichts davon blockt den Go.
## Erledigt heute (2026-07-11, nicht-disruptiv)
- ✅ **s24** MAC (`14:33:5c:d8:2d:7c`) ins `inventory/bf.csv` + **`.30.204`-Reservierung** angelegt.
- ✅ **pool-v30 → `.100-.250`** erweitert (Reservierungen in-pool).
- ✅ **`set_sta` auf statische IP** umgestellt (Default), **`set_auth`-Tool** vorhanden (Digest-Auth-Fallback).
- ✅ **Tasmota** als nicht-existent bestätigt (MAC nicht im Netz).
## Offen / To-verify (nicht go-blockierend)
- **Apple TV + Philips TV** (aus): beim Einschalten VLAN prüfen (Ziel VLAN40) — **vor cudy-Abschuss** (s.o.).
- **Creality Ender** (3D-Drucker) beim nächsten Einschalten verorten (MAC/IST-IP) + `.30.x`-Reservierung anlegen.
- **Stale HA-Device `tasmota_plug9`** entfernen (kein reales Gerät mehr) — macht Christoph.
- **`SHELLY_DEVICE_PW`** (eigenes Admin-PW, Wert liegt vor) von Christoph in `mcp/shelly/.env` ablegen (chmod600).
- Die **6 bereits Migrierten**: Reservierung `waiting/never` trotz Erreichbarkeit auf reservierter IP → wahrscheinlich **statische IP** am Gerät. Entscheiden: auf DHCP-Reservierung vereinheitlichen (Konsistenz) oder statisch belassen (funktional egal).