168 lines
16 KiB
Markdown
168 lines
16 KiB
Markdown
# 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).
|