Files
bf-infra/docs/tp17-shelly-go-runbook.md
Christoph Emsenhuber dcdf299568 docs 📄 TP17 go-ready + Netz-Abgleich 2026-07-11: HA-IPv4-Sweep, VLAN1-Leerstand-Analyse, Runbook
Live-Abgleich Projektstatus vs. echter Netzstand (MikroMCP/pve-mcp/HA):
- VLAN20 verifiziert: NAS .20.10, Mac mini .20.167, pve-LXC .20.126 alle live;
  einziger Host noch in VLAN1 = pve-Host-Mgmt .41.100 (verbleibende Host-Migration).
- TP17 Shelly: Migration schon ~6/26 durch (s14/16/18/22/23/24 stabil auf .30.2xx);
  16 VLAN1-Shellys verbleiben; alle VLAN30-Reservierungen stehen; s6-Reservierung
  .30.213 MAC gefixt (0C:72..9E -> real D0:CF:13:D8:18:FC).
- HA-Cross-Check aller IPv4-Geräte: .30.204 = Shelly Bad/Spiegelschrank (=vmtl. s24);
  Nicht-Shelly-VLAN30-Mover SwitchBot/.184 + Tasmota Nous A1 + 1 offen; .41.246 unident.
- Neues Runbook docs/tp17-shelly-go-runbook.md: 16er-Migrationstabelle, Go-Sequenz,
  Scope-Erweiterung (WLAN+IP+einheitliches Geräte-PW via Shelly.SetAuth),
  Gen4->Thread/Matter-Projektion (TP17.1, experimentell), VLAN1-Leerstand-Verdikt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 07:14:29 +02:00

150 lines
14 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.
# 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** (Gen2+ RPC, identisch für alle Gen3/4, aktuell `auth=off`):
```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":"dhcp"}}}}'
curl -s -X POST http://<VLAN1-IP>/rpc -d '{"id":1,"method":"Shelly.Reboot"}'
```
→ Gerät kommt auf `.30.x` (letztes Oktett = Reservierung). **Verify** (`discover.py <neue-IP>`).
**(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**. **User-Ruling 2026-07-11:** „wenn kein aktives Tasmota-Gerät auffindbar, gibt es keine mehr" → **kein VLAN30-Mover**; **stale HA-Device/Entity entfernen** (User macht Aufräumen selbst).
## 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.
**Verdikt:** Nach TP17 (16 Shellys + SwitchBot/Twinkly/Ender → VLAN30) + pve-Host → VLAN20 + Christophs Endgeräte → VLAN10 (cudy-Abbau) ist VLAN1 **frei von Endgeräten**. **Nicht** automatisch leer: die **Switch-/AP-Mgmt-IPs (sw1/sw2/ap2)** sind *by design* interim in VLAN1 bis zum separaten **MGMT-VLAN99-Cutover** (kein TP17-Scope). Plus **`.41.246` muss identifiziert werden**, bevor „leer" behauptet werden kann. `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.
## Offene Datenlücken / To-verify (nicht go-blockierend)
- **`.41.246` (80:af:ca:7d:e5:b1)** identifizieren — einziges unklares Live-Gerät in VLAN1, nicht in HA.
- **Creality Ender** (3D-Drucker) beim nächsten Einschalten verorten (MAC/IST-IP) + `.30.x`-Reservierung anlegen (existiert noch nicht).
- **s24** MAC (`14:33:5c:d8:2d:7c`) ins Inventar `bf.csv` nachtragen; **`.30.204`-Reservierung fehlt noch** (Gerät hat aktuell vmtl. statische IP).
- **Stale HA-Device `tasmota_plug9`** entfernen (kein reales Gerät mehr) — macht Christoph.
- `SHELLY_DEVICE_PW` (eigenes Admin-PW) von Christoph in `mcp/shelly/.env`; `set_auth`-Tool in `server.py` nachrüsten (ha1-Berechnung je realm).
- 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).