@impact0815/node-red-contrib-home-connect 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/BUILD-INFO.json +23 -0
  2. package/CHANGELOG.md +52 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE +19 -0
  5. package/README.de.md +326 -0
  6. package/README.md +325 -2
  7. package/SECURITY.md +47 -0
  8. package/docs/API-COVERAGE.md +59 -0
  9. package/docs/ARCHITECTURE.md +95 -0
  10. package/docs/DASHBOARD.de.md +121 -0
  11. package/docs/DASHBOARD.md +121 -0
  12. package/docs/FIRST-PUSH.md +42 -0
  13. package/docs/LOCAL-LAN.de.md +150 -0
  14. package/docs/LOCAL-LAN.md +150 -0
  15. package/docs/MESSAGE-API.md +111 -0
  16. package/docs/RELEASE.md +78 -0
  17. package/docs/TROUBLESHOOTING.de.md +230 -0
  18. package/docs/TROUBLESHOOTING.md +230 -0
  19. package/docs/images/README.md +10 -0
  20. package/docs/images/dashboard-details-en.png +0 -0
  21. package/docs/images/dashboard-overview-en.png +0 -0
  22. package/docs/official-api-contract.json +418 -0
  23. package/examples/01-first-steps-device-list.json +148 -0
  24. package/examples/02-events.json +165 -0
  25. package/examples/03-safe-read-write.json +155 -0
  26. package/examples/04-local-read-only.json +214 -0
  27. package/examples/dashboard-dishwasher-washer-oven.json +628 -0
  28. package/examples/dashboard-screenshot-simulated.json +405 -0
  29. package/examples/demo-home-connect-flowfuse-1.34.1-scriptless.json +390 -0
  30. package/lib/client.js +600 -0
  31. package/lib/constants.js +66 -0
  32. package/lib/dashboard-i18n.js +255 -0
  33. package/lib/device-label.js +39 -0
  34. package/lib/errors.js +51 -0
  35. package/lib/health.js +525 -0
  36. package/lib/local-client.js +1014 -0
  37. package/lib/local-crypto.js +122 -0
  38. package/lib/local-diagnostics.js +115 -0
  39. package/lib/local-profile.js +706 -0
  40. package/lib/local-trace.js +103 -0
  41. package/lib/oauth.js +23 -0
  42. package/lib/rate-limiter.js +186 -0
  43. package/lib/simulator.js +290 -0
  44. package/lib/sse.js +58 -0
  45. package/lib/validation.js +194 -0
  46. package/lib/websocket-transport.js +374 -0
  47. package/nodes/home-connect.html +795 -0
  48. package/nodes/home-connect.js +699 -0
  49. package/nodes/locales/de/home-connect.html +19 -0
  50. package/nodes/locales/de/home-connect.json +10 -0
  51. package/nodes/locales/en-US/home-connect.json +10 -0
  52. package/package.json +71 -4
@@ -0,0 +1,121 @@
1
+ # Dashboard 2.0: Übersicht und Gerätedetails
2
+
3
+ ## Import
4
+
5
+ 1. `node-red-contrib-home-connect` und `@flowfuse/node-red-dashboard` installieren.
6
+ 2. In Node-RED **Import → Zwischenablage** wählen und `examples/dashboard-dishwasher-washer-oven.json` laden.
7
+ 3. Deployen.
8
+ 4. `/dashboard/overview` öffnen; der genaue Präfix folgt der Dashboard-Konfiguration.
9
+
10
+ Der Flow läuft sofort mit dem integrierten Simulator und enthält keine echten Geräte- oder Zugangsdaten.
11
+
12
+ ## Sprache und Anzeigewerte
13
+
14
+ Das Dashboard zeigt immer genau **eine** Sprache. Die Auswahl erfolgt in dieser Reihenfolge: ein ausdrücklich in `msg.uiLocale`/`msg.locale` gesetzter Wert, die im Dashboard-Sprachwähler gespeicherte Auswahl, die Dashboard-/Vuetify-Sprache, die Sprache des HTML-Dokuments und schließlich die Browser-Sprache. Unterstützt werden `de` und `en`; ohne erkennbaren Wert wird Englisch verwendet. Der Sprachwähler auf Übersicht und Detailseite speichert die Auswahl browserlokal unter `home-connect-dashboard-language` und synchronisiert alle Karten der geöffneten Seite.
15
+
16
+ Lokalisierte Anzeige umfasst UI-Beschriftungen, Gerätetypen, Health-Zustände, Warntexte, bekannte Common- und gerätespezifische Enum-Werte sowie bekannte Programm- und Optionsnamen. Marken, Modellnummern, `haId`, VIB/E-Nummern, Home-Connect-Schlüssel und Rohwerte werden nicht verändert. Ein unbekanntes, nicht aufgelöstes Profil-Enum erscheint zum Beispiel als **Unbekannter Wert (Rohwert: 91)**; ein unbekannter, aber bereits semantischer Schlüssel wird aus seinem letzten Schlüsselteil lesbar formatiert. Diese Fallbacks ändern niemals den intern verwendeten Wert.
17
+
18
+ Die importierten Node-RED-Seiten heißen sprachneutral **Home Connect** und **Home Connect · Details**. Gruppentitel sind ausgeblendet; alle sichtbaren Karten-, Navigations- und Aktionsbeschriftungen kommen aus der Laufzeitlokalisierung.
19
+
20
+ ### FlowFuse Dashboard 1.34.1
21
+
22
+ Die `ui-template`-Komponente von FlowFuse erweitert benutzerdefinierte Vue-Komponenten um eigene Template-Scope-Werte und eingebaute Methoden. Deshalb verwenden die korrigierten Templates keinen kurzen Übersetzungsfunktionsnamen wie `tr` mehr. Sämtliche sichtbaren Texte liegen in der reaktiven Computed-Eigenschaft `hcLabels`; das Template bindet nur Werte wie `hcLabels.connection`. Damit kann ein vom Dashboard-Scope bereitgestellter Wert namens `tr` die Übersetzung nicht überschreiben oder beim Rendern als Funktion aufgerufen werden. Der Regressionstest baut beide Flows mit einem absichtlich nicht aufrufbaren `tr`-Scopewert auf und wertet alle `hcLabels`-Renderbindungen aus.
23
+
24
+ ## Enthaltene Seiten
25
+
26
+ ### Übersicht
27
+
28
+ - eine kompakte Karte pro Geschirrspüler, Waschmaschine und Backofen
29
+ - normierter Health-Status, wichtigste Warnung in der gewählten Sprache, Betriebszustand, aktives Programm und Restzeit
30
+ - dokumentierte Geräteinformationen: Name, Marke und Typ
31
+ - responsive Anordnung für Smartphone, Tablet und Desktop
32
+ - Klick bzw. Enter auf einer Karte öffnet die Detailseite
33
+
34
+ ### Gerätedetails
35
+
36
+ - getrennte Karten für Geschirrspüler, Waschmaschine und Backofen
37
+ - Health-Chip und deduplizierte Warnungen in der gewählten Sprache
38
+ - Name, Marke, Typ, `vib`/E-Nummer und Verbindungsstatus, soweit von der API geliefert
39
+ - initialer Snapshot nach dem Deploy und sparsames Polling alle 30 Minuten
40
+ - ein gemeinsamer lokaler WebSocket-/Cloud-SSE-Event-Stream; partielle `STATUS`-/`NOTIFY`-Updates werden nur in das betroffene Gerät eingemischt
41
+ - bekannte Felder bleiben bei Offline sichtbar und werden nach Reconnect automatisch aktualisiert
42
+ - Programmauswahl aus den erkannten Capabilities
43
+ - Geschirrspüler-Option VarioSpeed, Waschtemperatur und Backofen-Solltemperatur
44
+ - Start/Stopp über abgesicherte Schreib-Nodes
45
+ - Start wird bei offline/error/action_required, lokaler Bedienung oder fehlender Fernstartfreigabe deaktiviert
46
+ - Link zurück zur Übersicht
47
+ - strukturierte Fehler im Debug-Fenster
48
+
49
+ ## Zustands- und Aktualisierungspfad
50
+
51
+ `Snapshot/Event → Zustand zusammenführen` ist der gemeinsame Reducer für Read-Ergebnisse, Events und strukturierte Fehler. Er hält pro unveränderter `haId` genau ein Zustandsobjekt im Flow-Kontext. Ein vollständiger Snapshot mischt Geräteinformationen, `status.items`, `settings.items`, alle `values.items`, Programme, Health und Warnungen ein. Ein Teil-Event ändert nur die in `event.data.items` enthaltenen Schlüssel; nicht enthaltene Status-, Einstellungs-, Options- und Programmfelder bleiben erhalten.
52
+
53
+ Der Inject-Node fordert nach dem Deploy die initialen Snapshots an. Lokale `STATUS`-Events mit `connection.action: "NOTIFY"` und Cloud-`STATUS`-Events verwenden denselben Reducer. `DISCONNECTED` oder ein Offline-Read markiert das Gerät als offline/veraltet, ohne die letzten Werte zu löschen. `CONNECTED` entfernt die Veraltet-Markierung; der lokale Client aktualisiert weiter und liefert neue Events. Die vier Reducer-Ausgänge versorgen Geschirrspüler, Waschmaschine, Backofen und die gemeinsame Übersicht.
54
+
55
+ Lokale Snapshots tragen `readOnly: true`. Die Detailkarten stellen diese Geräte als reine Statusansicht dar: Betriebs- und Türzustand, aktives/ausgewähltes Programm, Restzeit, die drei Fern-/Lokalbedienungs-Flags, aktuelle Optionen und verfügbare Programme. Unaufgelöste Werte werden ausdrücklich mit Rohwert angezeigt. Programmauswahl, Optionseditoren sowie Start-/Stopp-Schaltflächen werden bei lokalen Read-only-Geräten gar nicht gerendert; die Oberfläche suggeriert damit keinen verfügbaren Steuerpfad. Nur **Aktualisieren** beziehungsweise **Refresh** in der gewählten Sprache bleibt bedienbar. Simulator und Cloud-Pfad verwenden weiterhin denselben Flow; Cloud-Bedienelemente bleiben mit den abgesicherten Write-Nodes verbunden.
56
+
57
+ Eine Restzeit wird nur angezeigt, wenn das passende Element vorhanden ist. Die lokale Umrechnung verwendet die Einheit aus dem Geräteprofil; ein fehlender Wert erscheint als `—` statt als erfundene `0 min`. OperationState-Rohwert `0` erscheint als das vom jeweiligen Profil definierte semantische Mitglied. Fehlt ein Enum-Mitglied tatsächlich im Profil, zeigt die Karte den unaufgelösten Rohwert und die Health-Schicht leitet daraus keine semantische Warnung ab.
58
+
59
+ ## Reproduzierbarer Dokumentations-Screenshot
60
+
61
+ `examples/dashboard-screenshot-simulated.json` ist ein separater, direkt importierbarer Flow nur für Screenshots und UI-Prüfungen. Er benötigt ausschließlich Node-RED und `@flowfuse/node-red-dashboard`; Home-Connect-Credentials, echte Geräte-IDs, IP-Adressen und lokale Profile sind weder erforderlich noch enthalten. Alle Flow-, Tab-, Page-, Group-, UI- und Demo-Node-Namen beginnen mit **Demo**. Eigene IDs mit dem Präfix `demo-hc-r9-fix1-`, die Dashboard-Basisroute `/demo-home-connect-r9-fix1` und eigene Unterseiten verhindern Verwechslungen oder Referenzen auf die produktive Installation. Ein Function-Node erzeugt bei jedem Deploy deterministisch drei klar mit **SIMULIERT**/**SIMULATED** markierte Beispielgeräte: einen laufenden Geschirrspüler, eine bereite Waschmaschine mit Warnhinweis und einen laufenden Backofen. Start-/Stopp-Bedienung wird in diesem Flow nicht angeboten.
62
+
63
+ 1. `@flowfuse/node-red-dashboard` installieren.
64
+ 2. `dashboard-screenshot-simulated.json` über **Import → Zwischenablage** importieren und deployen.
65
+ 3. `/demo-home-connect-r9-fix1/demo-overview` öffnen.
66
+ 4. Im Sprachwähler **Deutsch** oder **English** auswählen. Die Auswahl bleibt für die Detailseite gespeichert.
67
+ 5. Für vergleichbare Aufnahmen ein Browserfenster von etwa 1440 × 900 Pixeln, 100 % Zoom und das mitgelieferte helle Theme verwenden. Nach dem Deploy ungefähr eine Sekunde warten und dann **Demo Übersicht** oder **Demo Gerätedetails** aufnehmen.
68
+
69
+ Alle Namen, IDs und Modellangaben dieses Flows beginnen erkennbar mit `Studio`, `simulated-` oder `DEMO-`. Der Flow führt keine Netzwerkzugriffe aus und kann über **Aktualisieren/Refresh** jederzeit auf denselben Ausgangszustand zurückgesetzt werden.
70
+
71
+ ## Isolierte FlowFuse-1.34.1-Demo
72
+
73
+ `examples/demo-home-connect-flowfuse-1.34.1-scriptless.json` ist der separat importierbare, vom Benutzer bereitgestellte Demo-Flow. Er behält eigene Fix-spezifische IDs sowie Route `/demo-home-connect-ff1341-fix2`, Browserspeicher, Seiten, Gruppen, Theme und deterministische Simulationsdaten. Die `ui-template`-Nodes enthalten nur Darstellung; Lokalisierung und Zustand werden vor dem Rendern vorbereitet, sodass kein `tr(...)`-Helper mit dem Dashboard-Scope kollidieren kann. Enthalten sind weder Home-Connect-Konfigurations-Node noch Zugangsdaten, echte IDs, Adressen, Profile oder Netzwerkzugriffe.
74
+
75
+ ## Enthaltene Dokumentationsaufnahmen
76
+
77
+ - `docs/images/dashboard-overview-en.png` — englische simulierte Übersicht
78
+ - `docs/images/dashboard-details-en.png` — englische simulierte Gerätedetails
79
+
80
+ Das Release-Gate prüft beide Dateien auf gültiges PNG-Format. Zusätzlich muss der vollständige Bildausschnitt vor der Veröffentlichung manuell auf private Browser-/Kontokontexte kontrolliert werden.
81
+
82
+ ## Wechsel zu echten Cloud-Geräten
83
+
84
+ 1. Eine eigene Production-Verbindung anlegen und vollständig autorisieren.
85
+ 2. Mit dem Beispiel `01-first-steps-device-list` die echten Geräte ermitteln.
86
+ 3. In allen Read-, Event- und Write-Nodes `hc-sim-config` durch die Production-Verbindung ersetzen.
87
+ 4. Die drei Simulator-IDs in Read- und Write-Nodes durch die jeweils erkannte `haId` ersetzen. Einen separaten Event-Switch gibt es nicht; nach dem initialen Snapshot routet der Reducer anhand des Gerätetyps.
88
+ 5. Echte IDs nicht in Screenshots, Issues oder öffentliche Flows übernehmen.
89
+ 6. Schreib-Nodes im Dry-Run lassen und jeden Dashboard-Button testen.
90
+ 7. Am Gerät lokale Fernsteuerungs-/Fernstart-Freigaben prüfen.
91
+ 8. Dry-Run erst danach einzeln und bewusst abschalten.
92
+ 9. Demonstrations-Bestätigungstoken durch einen eigenen Wert ersetzen.
93
+ 10. Dashboard mit Authentifizierung, HTTPS und Netzwerkregeln schützen.
94
+
95
+ ## Wechsel zu lokalen Read-only-Geräten
96
+
97
+ 1. Nach der Lokale-LAN-Anleitung eine **Local LAN (read-only)**-Verbindung erstellen und deployen.
98
+ 2. In den drei Read-Nodes und im gemeinsamen Events-Node `hc-sim-config` durch diese lokale Verbindung ersetzen.
99
+ 3. In den Read-Nodes die drei Simulator-IDs durch die konfigurierten lokalen Profil-IDs ersetzen. Write-Nodes können entfernt werden; bleiben sie bestehen, sind die Bedienelemente wegen `readOnly: true` deaktiviert und die Runtime erzwingt zusätzlich `LOCAL_READ_ONLY`.
100
+ 4. Den initialen Snapshot beim Deploy aktiviert lassen. Ein gerade nicht erreichbares Gerät erscheint offline, ohne die anderen Geräte zu blockieren.
101
+ 5. Profil-IDs, Hosts, Profildateien, PSKs und IVs nicht mit einem exportierten Produktionsflow veröffentlichen.
102
+
103
+ ## Scopes
104
+
105
+ Für alle angezeigten Werte werden `IdentifyAppliance` und passende gerätespezifische `*-Monitor`-Scopes benötigt. Für Start/Stopp kommen je nach Gerät `*-Control` und für Einstellungen `*-Settings` hinzu. Nach einer Scope-Änderung muss neu autorisiert werden.
106
+
107
+ ## Modellunterschiede
108
+
109
+ Nicht jedes Modell bietet jedes Programm oder jede Option. Programmlisten werden dynamisch geladen. Die drei optionalen Bedienelemente verwenden verbreitete API-Keys; nicht vorhandene Optionen werden vom Write-Node sicher abgelehnt. Bei Bedarf die Optionsbildung im jeweiligen `ui-template` an die tatsächlich erkannten Capabilities anpassen.
110
+
111
+ ## Health, Warnungen und Aktualisierung
112
+
113
+ Der Reducer übernimmt `payload.health` und `payload.warnings` in das kompakte View-Modell und nutzt bereits aufgelöste semantische Schlüssel; er erfindet keine Geräte-Enums. Der gemeinsame Config-Node hält den Health-Zustand pro Gerät. Events ändern nur die gelieferten Felder und lösen nicht bei jeder Änderung erneut fünf lokale Fallback-Reads aus. Kurzlebige Hinweise laufen automatisch ab; offline bleibt bis zu einem bestätigten Reconnect/Snapshot bestehen, während die letzten Anzeigewerte erhalten bleiben.
114
+
115
+ ## Keine Firmwarefunktion
116
+
117
+ Das Dashboard zeigt bewusst keine Firmwareversion, Firmwarehistorie oder Update-Schaltfläche. Diese Funktionen sind nicht Teil der öffentlich dokumentierten Home-Connect-API. Es werden keine undokumentierten Endpunkte verwendet.
118
+
119
+ ## Sicherheit
120
+
121
+ Start-/Stopp-Bedienung nie anonym ins Internet stellen. Vor einem Ofen-Fernstart den Garraum kontrollieren. Bei Wasch- und Spülmaschinen Türen, Wasserzufuhr und Herstellerhinweise prüfen. Die Remote-Control-/Remote-Start-Flags des Geräts bleiben maßgeblich.
@@ -0,0 +1,121 @@
1
+ # Dashboard 2.0: overview and appliance details
2
+
3
+ ## Import
4
+
5
+ 1. Install `node-red-contrib-home-connect` and `@flowfuse/node-red-dashboard`.
6
+ 2. In Node-RED choose **Import → Clipboard** and load `examples/dashboard-dishwasher-washer-oven.json`.
7
+ 3. Deploy.
8
+ 4. Open `/dashboard/overview`; the exact prefix follows the Dashboard configuration.
9
+
10
+ The flow runs immediately with the built-in simulator and contains no real appliance or credential data.
11
+
12
+ ## Language and display values
13
+
14
+ The Dashboard displays exactly **one** language at a time. Resolution order is: an explicit `msg.uiLocale`/`msg.locale`, the selection saved by the Dashboard language selector, the Dashboard/Vuetify locale, the HTML document language, and finally the browser language. `de` and `en` are supported; English is the fallback. The selector on the overview and detail pages stores the choice in browser-local storage under `home-connect-dashboard-language` and synchronizes all cards on the open page.
15
+
16
+ Localization covers UI labels, appliance types, health states, warning text, known common and appliance-specific enum values, and known program and option names. Brands, model numbers, `haId`, VIB/E-numbers, Home Connect keys, and raw values are never changed. An unknown unresolved profile enum is shown, for example, as **Unknown value (raw value: 91)**. An unknown but already semantic key is humanized from its last key segment. These fallbacks never modify the internal value.
17
+
18
+ The imported Node-RED pages use the language-neutral names **Home Connect** and **Home Connect · Details**. Group titles are hidden; all visible card, navigation, and action labels come from runtime localization.
19
+
20
+ ### FlowFuse Dashboard 1.34.1
21
+
22
+ FlowFuse's `ui-template` component extends user Vue components with its own template-scope values and built-in methods. The corrected templates therefore no longer expose or call a short translation method such as `tr`. All visible text is held in the reactive computed property `hcLabels`, and templates bind values such as `hcLabels.connection`. A Dashboard-scope value named `tr` can no longer replace the translation function or be called during rendering. The regression test constructs both flows with an intentionally non-callable `tr` scope value and evaluates every `hcLabels` render binding.
23
+
24
+ ## Included pages
25
+
26
+ ### Overview
27
+
28
+ - one compact card for dishwasher, washer, and oven
29
+ - normalized health state, most important warning in the selected language, operation state, active program, and remaining time
30
+ - documented appliance information: name, brand, and type
31
+ - responsive layout for phone, tablet, and desktop
32
+ - clicking a card, or pressing Enter while focused, opens the detail page
33
+
34
+ ### Appliance details
35
+
36
+ - separate cards for dishwasher, washer, and oven
37
+ - health chip and deduplicated warnings in the selected language
38
+ - name, brand, type, `vib`/E-number, and connection state when supplied by the API
39
+ - an initial snapshot after deploy and conservative polling every 30 minutes
40
+ - one shared local-WebSocket/cloud-SSE event stream; partial `STATUS`/`NOTIFY` updates are merged into only the affected appliance
41
+ - last-known fields remain visible while a device is offline and are refreshed automatically after reconnect
42
+ - program selection from discovered capabilities
43
+ - dishwasher VarioSpeed, washer temperature, and oven setpoint controls
44
+ - start/stop through guarded write nodes
45
+ - start is disabled while offline/error/action_required, locally controlled, or remote start is unavailable
46
+ - a link back to the overview
47
+ - structured errors in the Debug sidebar
48
+
49
+ ## State and update path
50
+
51
+ `Snapshot/Event → merge state` is the single reducer for Read results, Events results, and structured errors. It keeps one state object per unchanged `haId` in flow context. A full snapshot merges appliance metadata, `status.items`, `settings.items`, all `values.items`, programs, health, and warnings. A partial event changes only the keys present in `event.data.items`; omitted status, setting, option, and program fields are retained.
52
+
53
+ The deploy-time Inject node requests the initial snapshots. Local `STATUS` events with `connection.action: "NOTIFY"` and cloud `STATUS` events use the same reducer. `DISCONNECTED` or an offline read marks the device offline/stale without deleting the last values; `CONNECTED` clears the stale marker, while the local client refreshes and continues delivering events. The reducer has four outputs: dishwasher, washer, oven, and the combined overview.
54
+
55
+ Local snapshots carry `readOnly: true`. The detail cards render these devices as read-only status views: operation and door state, active/selected program, remaining time, the three remote/local-control flags, current options, and available programs. Unresolved values are shown explicitly with their raw value. Program selectors, option editors, and Start/Stop buttons are not rendered at all for local read-only devices, so the UI does not suggest an available control path. The Refresh button remains available. The simulator and cloud path still use the same flow; cloud controls remain connected to the guarded Write nodes.
56
+
57
+ Remaining time is displayed only when a corresponding item exists. Local conversion uses the unit from the appliance profile; a missing item is `—`, not `0 min`. OperationState raw `0` is displayed as the semantic member defined by that profile. If an enum member is genuinely absent from the profile, the card shows an unresolved raw value and the health layer does not derive a semantic warning from it.
58
+
59
+ ## Reproducible documentation screenshot
60
+
61
+ `examples/dashboard-screenshot-simulated.json` is a separate directly importable flow for screenshots and UI checks. It requires only Node-RED and `@flowfuse/node-red-dashboard`; it contains and requires no Home Connect credentials, real appliance IDs, IP addresses, or local profiles. Every flow, tab, page, group, UI, and demo-node name starts with **Demo**. Dedicated IDs prefixed with `demo-hc-r9-fix1-`, the Dashboard base route `/demo-home-connect-r9-fix1`, and separate subpages prevent confusion with or references to a production installation. On every deploy, one Function node deterministically creates three devices clearly marked **SIMULATED**/**SIMULIERT**: a running dishwasher, a ready washer with a warning, and a running oven. The screenshot flow exposes no start/stop controls.
62
+
63
+ 1. Install `@flowfuse/node-red-dashboard`.
64
+ 2. Import `dashboard-screenshot-simulated.json` with **Import → Clipboard**, then deploy.
65
+ 3. Open `/demo-home-connect-r9-fix1/demo-overview`.
66
+ 4. Select **Deutsch** or **English** in the language selector. The choice remains active on the detail page.
67
+ 5. For comparable captures, use an approximately 1440 × 900 browser viewport, 100% zoom, and the included light theme. Wait about one second after deploy, then capture **Demo Overview** or **Demo appliance details**.
68
+
69
+ Every name, ID, and model value in this flow visibly begins with `Studio`, `simulated-`, or `DEMO-`. It performs no network access and **Refresh/Aktualisieren** restores the same initial state at any time.
70
+
71
+ ## Isolated FlowFuse 1.34.1 Demo
72
+
73
+ `examples/demo-home-connect-flowfuse-1.34.1-scriptless.json` is the isolated, user-supplied Demo flow retained as a separate import. It has fix-specific IDs and its own `/demo-home-connect-ff1341-fix2` route, browser storage, pages, groups, theme, and deterministic simulated data. Its `ui-template` nodes contain presentation markup only; localization and state are prepared before rendering, so no `tr(...)` helper can collide with Dashboard scope. It contains no Home Connect configuration node, credentials, real IDs, addresses, profiles, or network calls.
74
+
75
+ ## Included documentation captures
76
+
77
+ - `docs/images/dashboard-overview-en.png` — English simulated overview
78
+ - `docs/images/dashboard-details-en.png` — English simulated appliance details
79
+
80
+ The release gate verifies that both files are PNGs. A human must also inspect the complete frame for private browser/account context before publication.
81
+
82
+ ## Move to real cloud appliances
83
+
84
+ 1. Create a Production connection and complete authorization.
85
+ 2. Use `01-first-steps-device-list` to discover the real appliances.
86
+ 3. Replace `hc-sim-config` in every Read, Events, and Write node with the Production connection.
87
+ 4. Replace the three simulator IDs in Read and Write nodes with the respective discovered `haId`. There is no separate event switch; the reducer routes by the appliance type from the initial snapshot.
88
+ 5. Do not place real IDs in screenshots, issues, or public flows.
89
+ 6. Keep write nodes in dry-run and test every Dashboard button.
90
+ 7. Verify local remote-control and remote-start enablement on each appliance.
91
+ 8. Disable dry-run one node at a time only after verification.
92
+ 9. Replace the demonstration confirmation token with a deployment-specific value.
93
+ 10. Protect the Dashboard with authentication, HTTPS, and network controls.
94
+
95
+ ## Move to local read-only appliances
96
+
97
+ 1. Create and deploy a **Local LAN (read-only)** connection as described in the Local LAN guide.
98
+ 2. Replace `hc-sim-config` in the three Read nodes and the shared Events node with that local connection.
99
+ 3. Replace the three simulator IDs in the Read nodes with the configured local profile IDs. Write nodes may be removed; if retained, the templates keep their controls disabled because snapshots declare `readOnly: true` and the runtime still enforces `LOCAL_READ_ONLY`.
100
+ 4. Keep the deploy-time initial snapshot enabled. An appliance that is currently unreachable appears as offline instead of blocking the other devices.
101
+ 5. Do not publish profile IDs, hosts, profile files, PSKs, or IVs with an exported production flow.
102
+
103
+ ## Scopes
104
+
105
+ Displayed values require `IdentifyAppliance` plus suitable appliance-specific `*-Monitor` scopes. Start/stop additionally needs the relevant `*-Control`; settings need `*-Settings`. Authorize again after changing scopes.
106
+
107
+ ## Model differences
108
+
109
+ Not every model provides every program or option. Program lists are loaded dynamically. The three optional controls use common API keys; the Write node safely rejects unavailable options. If necessary, adapt option construction in each `ui-template` to the capabilities actually discovered for that appliance.
110
+
111
+ ## Health, warnings, and refresh behavior
112
+
113
+ The reducer passes `payload.health` and `payload.warnings` into the compact view model and uses already-resolved semantic keys; it does not invent appliance enums. The shared configuration node maintains health state per appliance. Events update only supplied fields and do not force five local fallback reads for every change. Short-lived notices expire automatically; offline remains until a confirmed reconnect/snapshot, while last-known display values are retained.
114
+
115
+ ## No firmware feature
116
+
117
+ The Dashboard intentionally contains no firmware version, firmware history, or update button. These capabilities are not part of the publicly documented Home Connect API. No undocumented endpoints are used.
118
+
119
+ ## Safety
120
+
121
+ Never expose start/stop controls anonymously to the internet. Check an oven cavity before remote start. For washers and dishwashers, verify doors, water supply, and manufacturer instructions. Appliance remote-control and remote-start flags remain authoritative.
@@ -0,0 +1,42 @@
1
+ # First GitHub push
2
+
3
+ Target repository: `impact0815/node-red-contrib-home-connect`
4
+
5
+ The repository is intentionally prepared without a `.git` directory. Create an empty GitHub repository with that name, without adding a README, license, or `.gitignore`, then run from the extracted source directory:
6
+
7
+ ```bash
8
+ git init -b main
9
+ git add .
10
+ git status
11
+ git commit -m "Release: Home Connect nodes v0.2.0"
12
+ git remote add origin https://github.com/impact0815/node-red-contrib-home-connect.git
13
+ git push -u origin main
14
+ ```
15
+
16
+ If Git is already initialized locally:
17
+
18
+ ```bash
19
+ git branch -M main
20
+ git remote get-url origin 2>/dev/null \
21
+ && git remote set-url origin https://github.com/impact0815/node-red-contrib-home-connect.git \
22
+ || git remote add origin https://github.com/impact0815/node-red-contrib-home-connect.git
23
+ git add .
24
+ git commit -m "Release: Home Connect nodes v0.2.0"
25
+ git push -u origin main
26
+ ```
27
+
28
+ Optional annotated release tag after CI succeeds:
29
+
30
+ ```bash
31
+ git tag -a v0.2.0 -m "node-red-contrib-home-connect v0.2.0"
32
+ git push origin v0.2.0
33
+ ```
34
+
35
+ Before pushing, run:
36
+
37
+ ```bash
38
+ npm ci
39
+ npm run validate
40
+ ```
41
+
42
+ Never commit Client IDs, Client Secrets, refresh/access tokens, authorization codes, real Redirect URIs, hostnames, appliance IDs, serial numbers, or private event/debug data.
@@ -0,0 +1,150 @@
1
+ # Lokale LAN-Verbindung (nur lesend)
2
+
3
+ Zurück zur [deutschen README](../README.de.md) · [English](LOCAL-LAN.md)
4
+
5
+ ## Umfang und feste Sicherheitsgrenze
6
+
7
+ Version 0.2.0 ergänzt einen **experimentellen, inoffiziellen lokalen Transport** für Home-Connect-Geräte. Node-RED verbindet sich direkt mit einer privaten/link-lokalen LAN-Adresse des Geräts. Normale lokale Reads und Events benötigen zur Laufzeit keine Home-Connect-Cloud.
8
+
9
+ Das lokale Protokoll ist keine offizielle öffentliche API. Gerätefirmware kann es ohne Ankündigung ändern. Deshalb gilt technisch erzwungen:
10
+
11
+ - lokale Ressourcen werden **nur gelesen**;
12
+ - der lokale Client besitzt keine Schreibimplementierung;
13
+ - ein Write-Node an einer lokalen Verbindung liefert immer `LOCAL_READ_ONLY`, bevor Validierung oder Netzwerkzugriff stattfinden;
14
+ - lokal werden keine Programme, Optionen, Einstellungen, Befehle, Pairing-/Registrierungs-/Deregistrierungs-, Firmware- oder Update-Anfragen gesendet.
15
+
16
+ Der bestehende Cloud-Client bleibt unverändert: vertraulicher OAuth-Authorization-Code-Flow ohne PKCE, Rate-Limit-Schutz, SSE, Health/Warnungen, Dry-Run und abgesicherte Cloud-Schreibzugriffe funktionieren über separate Cloud-Config-Nodes weiter.
17
+
18
+ ## Unterstützte Transporte
19
+
20
+ `connectionType` und Schlüssel stammen aus dem Geräteprofil:
21
+
22
+ | Profil | Endpunkt | Schutz |
23
+ |---|---|---|
24
+ | `AES` mit IV | `ws://<privater-host>:80/homeconnect` | fortlaufender AES-256-CBC-Stream plus verketteter, gekürzter HMAC-SHA256 |
25
+ | `TLS`/`PSK` | `wss://<privater-host>:443/homeconnect` | TLS 1.2 PSK (Profilidentität oder `HCCOM_Local_App`; PSK-Cipher-Suites) |
26
+
27
+ Beide Varianten verwenden RFC-6455-WebSocket-Frames und den lokalen Home-Connect-Session-Handshake. Der Upgrade-Request entspricht den hcpy/homeconnect-websocket-Clients: exakt `GET /homeconnect HTTP/1.1`, `Upgrade: websocket`, `Connection: Upgrade`, `Host`, URL-abgeleitetes `Origin`, RFC-6455-Key und Version 13. Ping/Pong läuft alle 20 Sekunden. Nach einem Abbruch wird eine vollständig neue Session samt frischem AES-/HMAC-Zustand aufgebaut; exponentieller Backoff mit Jitter wächst von ungefähr einer bis höchstens 60 Sekunden. Beim ersten HMAC-, Padding- oder UTF-8-Fehler wird die AES-Verbindung sofort geschlossen, weil sich der fortlaufende Stream nicht sicher resynchronisieren lässt.
28
+
29
+ ## Profile beschaffen
30
+
31
+ Den Community-[Home Connect Profile Downloader](https://github.com/bruestel/homeconnect-profile-downloader) verwenden. Er meldet sich einmalig am Konto an und lädt je registriertem Gerät:
32
+
33
+ - `<seriennummer>.json` — Host-/Gerätedaten und PSK/IV;
34
+ - `<seriennummer>_DeviceDescription.xml`;
35
+ - `<seriennummer>_FeatureMapping.xml`.
36
+
37
+ Cloud/Konto wird nur zum Erzeugen oder Aktualisieren der Profile benötigt. Die lokale Node-RED-Runtime automatisiert keinen Konto-Login, liest keine Passwörter aus und lädt Profile nicht selbst herunter.
38
+
39
+ Die Dateien sind Geheimnisse. Nie committen, in Flows exportieren, an Issues anhängen oder in Screenshots zeigen.
40
+
41
+ ## Node-RED einrichten
42
+
43
+ 1. Das Downloader-Archiv auf dem Node-RED-Host entpacken.
44
+ 2. Verzeichnis auf das Node-RED-Servicekonto beschränken, z. B. `chmod 700 /data/homeconnect-profiles` und `chmod 600 /data/homeconnect-profiles/*`.
45
+ 3. Eine neue Home-Connect-Konfiguration erstellen und **Local LAN (read-only)** wählen.
46
+ 4. Den serverseitigen **Profilordner** eintragen und **Profile suchen / Geräte erkennen** anklicken. Gemeint ist der für den Node-RED-Prozess sichtbare Pfad; bei Docker also der Pfad im Container. Der Scanner durchsucht Unterordner und gruppiert `<basis>.json`, `<basis>_DeviceDescription.xml` und `<basis>_FeatureMapping.xml`. Liegt nur ein Geräte-JSON im Unterordner, werden auch unpräfixte XML-Namen akzeptiert.
47
+ 5. Das Ergebnis lesen, zum Beispiel **3 Geräteprofile gefunden**. Ein unvollständiger Satz, ungültiges JSON/Schlüsselmaterial, eine verwaiste Begleitdatei oder eine doppelte Profil-ID wird verständlich aufgeführt und nicht als mehrdeutige Gerätekarte übernommen. Die drei Originaldateien zusammenlassen und erneut suchen.
48
+ 6. In jeder Gerätekarte genau einen **Host bzw. eine IP** eintragen. Fehlende/formal falsche Werte und öffentliche IP-Literale werden sofort markiert. Ein Hostname wird beim Verbindungstest auf dem Server aufgelöst und muss ausschließlich private/link-lokale Adressen liefern. Ein im Profil gefundener Host ist nur ein Startvorschlag.
49
+ 7. **Verbindungsart: Auto (empfohlen)** übernimmt zuerst die ausdrückliche Profilangabe `AES`, `TLS` oder `PSK`; nur wenn sie fehlt, bedeutet ein IV AES und kein IV TLS-PSK. Damit wird ein in einem TLS-Export verbliebenes IV nicht mehr fälschlich als AES interpretiert. AES oder TLS nur bei einem bekannten Profilproblem bewusst überschreiben.
50
+ 8. In jeder Karte **Verbindung testen** anklicken. Der Test sendet die gerade sichtbare Host/IP und Auto/AES/TLS-Auswahl, legt sie über eine eventuell ältere versteckte/deployte Zuordnung und lädt ausschließlich das ausgewählte Profil. Noch leere Nachbarkarten können deshalb kein falsches `LOCAL_MAPPING_HOST_REQUIRED` mehr auslösen. Die Anzeige führt gestuft durch **IP erreichbar → Profil geladen → Transport erkannt → WebSocket verbunden → Session initialisiert → Services erkannt → Read-only-Daten empfangen**. Beim ersten Fehler stoppt sie; spätere Stufen werden übersprungen und es erscheint keine irreführende Folgemeldung `LOCAL_VALUES_UNAVAILABLE`.
51
+ 9. **Fertig → Deployen**. Jede Kartenänderung aktualisiert `localDeviceMappings`; beim Speichern werden alle Karten nochmals serialisiert. Nach erneutem Öffnen/Deploy stehen daher dieselben Zuordnungen bereit. Anwender bearbeiten kein Mapping-JSON.
52
+ 10. Im Read-Node nur das konfigurierte Gerät und die gewünschte Lese-Ressource auswählen; ein Host-/IP-Feld gibt es dort nicht. Im Events-Node ein konfiguriertes Gerät oder **Alle konfigurierten Geräte** wählen.
53
+
54
+ Die Zuordnung Profil zu Host/IP/Transport wird genau einmal als `localDeviceMappings` in der Config-Node gespeichert. Read- und Events-Nodes speichern nur die Profil-/Geräte-ID.
55
+
56
+ Alle Geräteauswahlen zeigen `Typ — verständlicher Name — Marke/Modell — …letzte vier ID-Zeichen`. Die vollständige technische ID bleibt unverändert der Optionswert und wird in Runtime/Requests nie gekürzt.
57
+
58
+ **Ausweichweg unter Erweitert:** **Profil-JSON manuell einfügen (optional)** nimmt ein vollständiges Profil nur dann aktiv in Betrieb, wenn kein Ordner gesetzt ist. Bei gesetztem Ordner ist der Bereich standardmäßig eingeklappt/deaktiviert und der Ordner hat Vorrang. **Aus erkannten Profilen und Zuordnungen erzeugen** erstellt das Credential-JSON nach vollständiger Kartenzuordnung. Einsteiger müssen weder Mapping-/Profil-JSON schreiben noch sehen sie einen Credential-Platzhalter als Beispieldaten.
59
+
60
+ Ebenfalls unter **Erweitert** liegt der standardmäßig ausgeschaltete **sichere Protokoll-Mitschnitt**. Ohne Dateipfad bleibt ein Ringpuffer nur im Speicher und erscheint nach dem Verbindungstest aufklappbar in der Gerätekarte. Ein absoluter serverseitiger Pfad schreibt unter POSIX JSONL mit Dateirecht `0600`; unter Windows muss der Pfad mit einer auf das Dienstkonto beschränkten NTFS-ACL geschützt werden, weil POSIX-Rechte dort nicht zuverlässig abgebildet werden. Aufgezeichnet werden entschlüsselte RX-/TX-Protokollnachrichten und Lifecycle-Ereignisse, aber PSK, IV, Schlüssel, OAuth-Daten, Nonces sowie Geräte-/Netzwerkkennungen werden entfernt. Mitschnitt nur während der Diagnose aktivieren, Datei vor Weitergabe trotzdem prüfen und anschließend wieder ausschalten.
61
+
62
+ Beispiel der zentralen Zuordnung in einer exportierten, anonymisierten Config-Node (vollständig fiktiv; Schlüssel bleiben im geschützten Profilordner und werden nicht exportiert):
63
+
64
+ ```json
65
+ "localDeviceMappings": "[{\"profileId\":\"dishwasher-local\",\"host\":\"192.168.x.x\",\"connectionType\":\"auto\"}]"
66
+ ```
67
+
68
+ Bei Docker den Ordner nur lesend einbinden und den Pfad im Container verwenden:
69
+
70
+ ```yaml
71
+ volumes:
72
+ - ./homeconnect-profiles:/data/homeconnect-profiles:ro
73
+ ```
74
+
75
+ ## Lese- und Event-Mapping
76
+
77
+ Nach dem Handshake liest der Client in dieser Reihenfolge und ausschließlich per `GET`:
78
+
79
+ - `/ro/allMandatoryValues`
80
+ - `/ro/values`
81
+ - `/ro/allDescriptionChanges`
82
+ - `/ro/activeProgram`
83
+ - `/ro/selectedProgram`
84
+
85
+ Numerische Laufzeit-UIDs werden gegen beide Profildateien aufgelöst. `FeatureMapping.xml` liefert semantische Feature-Namen und – soweit enthalten – Enum-Mitglieder; `DeviceDescription.xml` liefert hexadezimale `uid`-Werte, Kategorien, Access-/Typ-/Einheiten-Metadaten und Enum-Verweise. Der Resolver folgt sowohl direkten `enumerationType`-Attributen als auch verschachtelten `<enumerationType refENID="…"/>`-Elementen, `enid`-Verweisen und `subsetOf`-Vererbung. Manche reale Profile führen die maßgebliche Enum-Tabelle in `DeviceDescription.xml` als `<enumerationType enid="…">`; Enum- und Mitgliedsnamen stehen dort in den unmittelbar vorangestellten XML-Kommentaren. Sämtliche XML-Kennungen (`uid`, `refUID`, `enid`, `refENID`, `enumerationType`, `subsetOf`) sind hexadezimal, selbst wenn sie nur Ziffern enthalten: `2100` bedeutet deshalb dezimale UID 8448 und nicht 2100.
86
+
87
+ Es gibt keine globale Geräte-UID- oder Enum-Tabelle. Der Rohwert `0` von OperationState wird zum Beispiel nur übersetzt, wenn das ausgewählte Geräteprofil dieses Mitglied definiert. Eine profildefinierte Enum-Tabelle hat immer Vorrang vor der skalaren Behandlung. Profiltyp `Boolean` steuert die Normalisierung von RemoteControlActive, RemoteControlStartAllowed, LocalControlActive und booleschen Optionen. Jedes lokale Element behält `rawValue`; `valueResolved` ist nur bei einem tatsächlich gefundenen Enum-Mitglied, einem gültigen profildefinierten Boolean/Programm oder einem ausdrücklich typisierten Skalarwert `true`. Fehlt ein Enum-Mitglied, bleibt der Rohwert mit `valueResolved: false` sichtbar und diagnostizierbar. Status- und Event-Warnungen werden daraus nicht abgeleitet. `RemoteControlStartAllowed=false` bleibt bei einer lokalen Read-only-Verbindung ohne aktiven Steuerkontext eine neutrale Statusangabe und keine dauerhafte Warnung.
88
+
89
+ Aufgelöste Status-/Event-Features landen in `status.items`, Einstellungen in `settings.items`; jedes empfangene Element – einschließlich unaufgelöster numerischer UIDs – bleibt in `values.items`. Programmdefinitionen stammen aus den Profilmerkmalen. Werte von `BSH.Common.Root.ActiveProgram` und `BSH.Common.Root.SelectedProgram` werden auf Programmnamen aus dem Profil zurückgeführt; aktuell empfangene Optionswerte werden dem Programmzustand beigefügt. Ein 404 für den optionalen Pfad `/ro/activeProgram` oder `/ro/selectedProgram` löscht deshalb keine Programm-/Optionsdaten, die bereits über `/ro/allMandatoryValues` eingetroffen sind.
90
+
91
+ Snapshots enthalten die sichere Zusammenfassung `profileResolution` mit Anzahl der Features/Elemente/aufgelösten/unaufgelösten/Status-/Einstellungs-/Programmwerte und höchstens 20 unaufgelösten numerischen UIDs. Host, Gerätekennung, PSK, IV, Nonce und Credentials sind darin nicht enthalten. Ein abgelehnter optionaler Read steht in `refresh`; erfolgreich über einen anderen Pfad oder per Geräte-`NOTIFY` gelieferte Daten werden nicht verworfen.
92
+
93
+ Der lokale Events-Node liefert normierte `CONNECTED`-, `DISCONNECTED`- und `STATUS`-Nachrichten über dieselbe Health-/Warning-Schicht wie Cloud-Daten. Lokale Ausgaben enthalten `connection.local: true` und `connection.readOnly: true`.
94
+
95
+ Ist ein konfigurierter Host nicht erreichbar, liefern Snapshot-/Capability-Reads das Gerät mit `connected: false`, `connection.state: "offline"` und ausschließlich einem sicheren Netzwerkgrund statt eines sachfremden generischen Fehlers. Bereits in der Session bekannte Werte bleiben als veraltet markiert erhalten. Ein aktiver Events-Node verbindet mit begrenztem Backoff weiter und liefert nach Wiedererreichbarkeit automatisch neue Daten.
96
+
97
+ Der vollständige Ablauf orientiert sich an hcpy und homeconnect-websocket:
98
+
99
+ 1. Das Gerät sendet `POST /ei/initialValues`. Die Antwort verwendet dieselbe `sID`, dieselbe `msgID` und die EI-versionabhängige Application-Struktur. Der nächste eigene Zähler beginnt exakt bei `data.edMsgID`.
100
+ 2. `/ci/services` wird immer als `GET`, Version 1 und **ohne** `data`-Feld angefordert. Gerätecode `0`, `200` oder ein fehlender Code gilt als Erfolg.
101
+ 3. Die gelieferten Versionen werden pro Service verwendet. Bei `ci < 3` wird `/ci/authentication` zuerst wie hcpy per `POST` mit 32-Byte-Nonce angefragt; nur eine konkrete Ablehnung löst den dokumentierten `GET`-Fallback aus.
102
+ 4. `NOTIFY /ei/deviceReady` verwendet die ausgehandelte EI-Version und kein leeres `data`-Array.
103
+ 5. Geräteinformationen werden optional über `/iz/info` angefragt, wenn `iz` gemeldet wurde, sonst über `/ci/info`; `/ni/info` wird nur bei vorhandenem `ni` verwendet. Eine Ablehnung dieser optionalen Diagnosepfade blockiert die Read-only-Session nicht.
104
+ 6. Die fünf `/ro/*`-GETs verwenden die gemeldete `ro`-Version. Nicht jedes Modell unterstützt jeden Pfad: einzelne Codes 400 werden dokumentiert und die übrigen Lesepfade fortgesetzt.
105
+
106
+ Unaufgeforderte `NOTIFY`- oder `RESPONSE`-Nachrichten des Geräts werden sofort verarbeitet. Bereits vom Gerät gelieferte Initial-/Mandatory-/Value-Daten gelten als erfolgreicher Read-only-Empfang, auch wenn ein späterer Fallback-GET abgelehnt wird. Geräte-originierte `GET`-/`POST`-Housekeeping-Requests (zum Beispiel `/ei/keepAlive`) beantwortet der Client mit `RESPONSE`, derselben Session und derselben Geräte-`msgID`; dadurch wird der eigene `edMsgID`-Zähler nicht verschoben. WebSocket-Ping/Pong bleibt davon getrennt. Der Test bleibt technisch rein lesend: Nonce, `deviceReady` und Protokollantworten ändern weder Einstellungen noch Programme.
107
+
108
+ ## Netzwerkbeschränkung
109
+
110
+ Vor dem Verbindungsaufbau muss der Host ausschließlich auf RFC1918-, IPv4-Link-Local-, IPv6-Unique-Local- oder IPv6-Link-Local-Adressen auflösen. Loopback, URLs, Pfade, Zugangsdaten im Hostnamen und öffentliche DNS-Ziele werden abgewiesen. Das Profilfeld kann damit nicht als beliebiges Internet-Socket-Ziel missbraucht werden.
111
+
112
+ ## Fehlerbehebung
113
+
114
+ | Code/Symptom | Bedeutung/Maßnahme |
115
+ |---|---|
116
+ | `LOCAL_PROFILE_REQUIRED` | Profilordner oder optionales manuelles Profil-JSON setzen, speichern und deployen. |
117
+ | `LOCAL_PROFILE_FOLDER_REQUIRED` | Die automatische Erkennung benötigt einen Ordner statt einer einzelnen Datei. |
118
+ | `LOCAL_PROFILE_FILES_INCOMPLETE` | Geräte-JSON, `DeviceDescription.xml` und `FeatureMapping.xml` desselben Downloads zusammenlassen und neu suchen. |
119
+ | `LOCAL_PROFILE_ORPHAN_COMPANION` | Eine XML-/JSON-Begleitdatei passt nach Ordner/Basisname zu keinem Geräte-JSON; Originalnamen wiederherstellen. |
120
+ | `LOCAL_MAPPING_HOST_REQUIRED` | In der ausgewählten Karte selbst fehlt Host/IP. Leere Nachbarkarten blockieren den Einzeltest nicht. |
121
+ | `LOCAL_MAPPING_DUPLICATE` | Für jede Profil-ID genau eine zentrale Zuordnung behalten. |
122
+ | `LOCAL_MAPPING_PROFILE_NOT_FOUND` | Der Ordner wurde nach der Zuordnung geändert; neu scannen und veraltete Einträge entfernen. |
123
+ | `LOCAL_PROFILE_KEY_MISSING` / `LOCAL_PROFILE_IV_MISSING` | Vollständiges Profil neu laden; Schlüssel nicht erfinden. |
124
+ | `LOCAL_HOST_NOT_PRIVATE` | DNS/IP korrigieren; nur private/link-lokale Ziele sind zulässig. |
125
+ | `LOCAL_NETWORK_ERROR` / `LOCAL_CONNECT_TIMEOUT` | Gleiches LAN, DNS/IP, VLAN/Firewall, Geräte-WLAN und Port 80/443 prüfen. `EHOSTUNREACH` bedeutet fehlenden Netzwerkpfad; häufig sind Gerät aus/Standby, falsche IP, VLAN-Routing oder Firewall die Ursache. |
126
+ | `LOCAL_HANDSHAKE_TIMEOUT` / `LOCAL_REQUEST_TIMEOUT` | Socket geöffnet, aber Protokoll-/Leseantwort blieb aus. Profil, Transport, Firmwarekompatibilität und Paketverlust prüfen. |
127
+ | `LOCAL_TRANSPORT_UNSUPPORTED` | Auto beibehalten oder einen falschen AES-/TLS-Override korrigieren; außerdem PSK-Unterstützung von Node.js/OpenSSL prüfen. |
128
+ | `LOCAL_TLS_PSK_UNAVAILABLE` | Die Node.js/OpenSSL-Laufzeit bietet keine der benötigten TLS-1.2-PSK-Cipher-Suites; Laufzeit/OpenSSL wechseln oder korrekt bauen. |
129
+ | `LOCAL_PROFILE_KEY_REJECTED` / `LOCAL_PROFILE_AUTHENTICATION_FAILED` | Profil/Schlüssel passt nicht zu diesem Gerät oder ist unvollständig. Richtiges Profil neu laden; Schlüssel nie raten. |
130
+ | `LOCAL_PROTOCOL_REJECTED` | Das Gerät hat einen Protokollcode (z. B. 400) geliefert. Die Diagnose nennt konkret Ressource, Aktion, ausgehandelte Version und Gerätecode; bei vollständig abgelehnter Lesesequenz zusätzlich alle Versuche. Bereinigten Mitschnitt aktivieren und Transport/Profil/Firmware vergleichen. |
131
+ | `LOCAL_CONNECT_FAILED` | Verbindungsfehler ohne genauere Netzwerk-/Schlüssel-/Transportklasse; die vorherigen Punkte prüfen. |
132
+ | `LOCAL_WEBSOCKET_UPGRADE_FAILED` | TCP/TLS steht, aber das Gerät lehnt den RFC-6455-Upgrade auf `/homeconnect` ab. Profilzuordnung, Auto/AES/TLS, IP und eventuellen Proxy prüfen; ein vorhandener HTTP-Status wird ohne Header/Schlüssel angezeigt. |
133
+ | `LOCAL_CRYPTO_AUTH_FAILED` / `LOCAL_CRYPTO_PADDING_INVALID` | PSK/IV falsch oder AES-CBC/HMAC-Streamzustand verloren; der Client schließt sofort. Bei Wiederholung Profile neu laden. |
134
+ | `LOCAL_HEARTBEAT_TIMEOUT` / `LOCAL_DISCONNECTED` | Verbindung war aufgebaut, antwortet aber nicht mehr. Gerätestrom/Standby, WLAN, VLAN und Firewall prüfen; der Client baut die Session mit Backoff vollständig neu auf. |
135
+ | `LOCAL_TRACE_PATH_INVALID` | Für einen Dateimitschnitt einen absoluten Pfad im Node-RED-Host/Container angeben. Ohne Pfad ist nur der sichere Speicherpuffer aktiv. |
136
+ | TLS-Handshake-Fehler | TLS-Profil, PSK-Cipher-Suites der Node.js/OpenSSL-Laufzeit und fehlende TLS-Inspection prüfen. |
137
+ | nur numerische UIDs | XML-Mapping fehlt/passt nicht. Alle drei Downloader-Dateien zusammenlassen. |
138
+ | `LOCAL_READ_ONLY` | Erwartete Sicherheitsgrenze. Für abgesicherte Schreibvorgänge separate Cloud-Verbindung verwenden. |
139
+
140
+ ## Community-Protokollreferenzen
141
+
142
+ Die Implementierung wurde für dieses Node-RED-Paket eigenständig anhand öffentlich verfügbarer Community-Beobachtungen erstellt, insbesondere:
143
+
144
+ - `chris-mc1/homeconnect_local_hass` und `chris-mc1/homeconnect_websocket`
145
+ - `hcpy2-0/hcpy`
146
+ - `zibous/homeconnect-hcpy`
147
+ - `Asmir1975/homeconnect_local_hass-UI`
148
+ - Protokollnotizen in `SukramJ/go-homeconnect2mqtt`
149
+
150
+ Im Paket befinden sich weder echte Geräteprofile/Zugangsdaten/Seriennummern/Hosts/PSK/IV noch kopierter Quellcode dieser Projekte. Hinweise zu Zuordnung und Lizenzen stehen in [NOTICE](../NOTICE).
@@ -0,0 +1,150 @@
1
+ # Local LAN connection (read-only)
2
+
3
+ Back to the [English README](../README.md) · [Deutsch](LOCAL-LAN.de.md)
4
+
5
+ ## Scope and safety boundary
6
+
7
+ Version 0.2.0 adds an **experimental, unofficial local transport** for Home Connect appliances. Node-RED connects directly to the appliance's private/link-local LAN address. Normal runtime reads and events do not call the Home Connect cloud.
8
+
9
+ The local protocol is not an official public API. Device firmware may change it without notice. This implementation therefore has a hard boundary:
10
+
11
+ - local resources are **read-only**;
12
+ - the local client exposes no write method;
13
+ - a Write node connected to a local configuration always returns `LOCAL_READ_ONLY` before validation or network I/O;
14
+ - no program, option, setting, command, pairing, registration, deregistration, firmware, or update request is sent locally.
15
+
16
+ The existing cloud client is unchanged: confidential OAuth Authorization Code flow, no PKCE, rate-limit protection, SSE, health/warnings, dry-run, and guarded cloud writes remain available in separate cloud configuration nodes.
17
+
18
+ ## Supported transports
19
+
20
+ The `connectionType` and keys come from the appliance profile:
21
+
22
+ | Profile | Endpoint | Protection |
23
+ |---|---|---|
24
+ | `AES` plus IV | `ws://<private-host>:80/homeconnect` | persistent AES-256-CBC stream plus chained truncated HMAC-SHA256 |
25
+ | `TLS`/`PSK` | `wss://<private-host>:443/homeconnect` | TLS 1.2 PSK (profile identity or `HCCOM_Local_App`; PSK cipher suites) |
26
+
27
+ Both variants use RFC 6455 WebSocket framing and the local Home Connect session handshake. The upgrade matches the hcpy/homeconnect-websocket clients: exact `GET /homeconnect HTTP/1.1`, `Upgrade: websocket`, `Connection: Upgrade`, `Host`, URL-derived `Origin`, RFC 6455 key, and version 13. A 20-second ping/pong heartbeat is enabled. A disconnect builds an entirely new session with fresh AES/HMAC state; exponential backoff with jitter grows from about one second to at most 60 seconds. The first HMAC, padding, or UTF-8 failure closes the socket immediately because the stream cannot safely resynchronize.
28
+
29
+ ## Obtain profiles
30
+
31
+ Use the community [Home Connect Profile Downloader](https://github.com/bruestel/homeconnect-profile-downloader). It performs the required account sign-in and downloads, for each registered appliance:
32
+
33
+ - `<serial>.json` — host/device metadata and PSK/IV;
34
+ - `<serial>_DeviceDescription.xml`;
35
+ - `<serial>_FeatureMapping.xml`.
36
+
37
+ The account/cloud step is required only to obtain or refresh those profiles. The Node-RED local runtime does not automate account login, scrape passwords, or download profiles itself.
38
+
39
+ Treat the resulting files as secrets. Do not commit them, put them in flows, attach them to issues, or copy them into screenshots.
40
+
41
+ ## Configure Node-RED
42
+
43
+ 1. Extract the Profile Downloader archive on the Node-RED host.
44
+ 2. Restrict the directory to the Node-RED service account, for example `chmod 700 /data/homeconnect-profiles` and `chmod 600 /data/homeconnect-profiles/*`.
45
+ 3. Create a new Home Connect configuration and choose **Local LAN (read-only)**.
46
+ 4. Enter the server-side **Profile folder**, then click **Scan profiles / Detect devices**. This is a path seen by the Node-RED process; for Docker it is the path inside the container. The scanner searches subfolders and groups `<base>.json`, `<base>_DeviceDescription.xml`, and `<base>_FeatureMapping.xml`. It also accepts the unprefixed XML names when one appliance JSON is alone in its directory.
47
+ 5. Read the result, such as **3 device profiles found**. An incomplete set, invalid JSON/key, orphan companion file, or duplicate profile ID is listed clearly and is not turned into an ambiguous device card. Keep the three files from the same download together and scan again.
48
+ 6. For every detected device card, enter exactly one **Host/IP**. Missing/malformed values and public IP literals are marked immediately. A hostname is resolved by the server during the connection test and must resolve exclusively to private/link-local addresses. A host detected inside a profile is only an initial suggestion.
49
+ 7. Leave **Connection type** at **Auto (recommended)**. Auto first honors an explicit profile declaration of `AES`, `TLS`, or `PSK`; only when it is absent does an IV select AES and no IV select TLS-PSK. A retained IV in an explicitly TLS profile therefore no longer selects the wrong transport. Use AES/TLS only as a deliberate override.
50
+ 8. Click **Test connection** on every card. The test posts the Host/IP and Auto/AES/TLS value currently visible in that card, merges it over any older hidden/deployed mapping, and loads only the selected profile. Its stages are **IP reachable → Profile loaded → Transport detected → WebSocket connected → Session initialized → Services detected → Read-only data received**. It stops at the first root cause, marks later stages skipped, and never adds a misleading `LOCAL_VALUES_UNAVAILABLE` follow-up. Unfilled sibling cards cannot cause a false `LOCAL_MAPPING_HOST_REQUIRED`, and the test cannot call a write method.
51
+ 9. Click **Done → Deploy**. Every input change updates `localDeviceMappings`, and Done serializes all cards once more. The same assignments are therefore restored after reopening/deploy; users never edit mapping JSON.
52
+ 10. In a Read node, select the configured appliance and the desired read resource. There is no Host/IP field. In an Events node, select one configured appliance or **All configured appliances**.
53
+
54
+ The profile-to-Host/IP/transport assignment is serialized once as `localDeviceMappings` in the configuration node. Read and Events nodes store only the profile/appliance ID.
55
+
56
+ All appliance selectors show a descriptive display label in the form `type — friendly name — brand/model — …last four ID characters`. The complete technical ID remains the option value and is never shortened in runtime state or requests.
57
+
58
+ **Advanced fallback:** **Paste profile JSON manually (optional)** accepts a complete profile only when no folder is active. With a folder set, the section is collapsed/disabled by default and folder data takes precedence. **Generate from detected profiles and assignments** can create the credential JSON after all cards are valid. This prevents beginners from writing mapping/profile JSON and never presents a credential placeholder as sample data.
59
+
60
+ The **safe protocol trace** is also under **Advanced** and disabled by default. Without a file path, a bounded ring buffer remains in memory and is shown in a collapsible section after a connection test. An absolute server-side path writes JSONL with mode `0600` on POSIX; on Windows, use an account-restricted NTFS ACL because POSIX permission bits are not represented reliably. It records decrypted RX/TX protocol messages and lifecycle events, but removes PSKs, IVs, keys, OAuth data, nonces, and appliance/network identifiers. Enable it only while diagnosing, inspect the file before sharing, then disable it again.
61
+
62
+ Example central assignment in an exported, anonymized config node (all values fictional; keys remain in the protected profile folder and are not exported):
63
+
64
+ ```json
65
+ "localDeviceMappings": "[{\"profileId\":\"dishwasher-local\",\"host\":\"192.168.x.x\",\"connectionType\":\"auto\"}]"
66
+ ```
67
+
68
+ For Docker, mount the directory read-only and use the path inside the container:
69
+
70
+ ```yaml
71
+ volumes:
72
+ - ./homeconnect-profiles:/data/homeconnect-profiles:ro
73
+ ```
74
+
75
+ ## Read and event mapping
76
+
77
+ After the session handshake, the client reads only these `ro` resources, in this order and by `GET`:
78
+
79
+ - `/ro/allMandatoryValues`
80
+ - `/ro/values`
81
+ - `/ro/allDescriptionChanges`
82
+ - `/ro/activeProgram`
83
+ - `/ro/selectedProgram`
84
+
85
+ Numeric runtime UIDs are resolved against both profile files. `FeatureMapping.xml` supplies semantic feature names and, where present, enum members; `DeviceDescription.xml` supplies hexadecimal `uid` values, categories, access/type/unit metadata, and enum references. The resolver follows direct `enumerationType` attributes, nested `<enumerationType refENID="…"/>` elements, `enid` references, and `subsetOf` inheritance. Some real profiles place the authoritative enum table in `DeviceDescription.xml` as `<enumerationType enid="…">`, with enum/member names in the immediately preceding XML comments. All XML identifiers (`uid`, `refUID`, `enid`, `refENID`, `enumerationType`, and `subsetOf`) are hexadecimal even when they contain digits only: `2100` therefore means decimal UID 8448, not 2100.
86
+
87
+ No global appliance UID or enum table is used. For example, OperationState raw value `0` is translated only when the selected appliance profile defines that member. A profile enum always takes precedence over scalar handling. Profile-typed Boolean values are normalized for RemoteControlActive, RemoteControlStartAllowed, LocalControlActive, and Boolean options. Every local item keeps `rawValue`; `valueResolved` is true only for an actual enum member, a valid profile Boolean/program, or an explicitly typed scalar. If an enum member is missing, the raw value remains visible with `valueResolved: false`, and no status or event warning is inferred from it. On a local read-only connection without an active control context, `RemoteControlStartAllowed=false` remains neutral status rather than a persistent warning.
88
+
89
+ Resolved status/event features become `status.items`, settings become `settings.items`, and every received item—including unresolved numeric UIDs—remains in `values.items`. Program definitions come from profile features. `BSH.Common.Root.ActiveProgram` and `BSH.Common.Root.SelectedProgram` values are resolved back to program feature names, and currently received option items are attached to program state. A 404 from the optional `/ro/activeProgram` or `/ro/selectedProgram` path therefore does not erase program/options already present in `/ro/allMandatoryValues`.
90
+
91
+ Snapshots expose a safe `profileResolution` summary (feature/item/resolved/unresolved/status/setting/program counts and at most 20 unresolved numeric UIDs). It contains no host, appliance identity, PSK, IV, nonce, or credential. A rejected optional read is listed under `refresh`, but data received from another successful response or appliance `NOTIFY` remains authoritative and is not discarded.
92
+
93
+ The local Events node emits normalized `CONNECTED`, `DISCONNECTED`, and `STATUS` messages through the same health/warning layer used by cloud data. Local outputs include `connection.local: true` and `connection.readOnly: true`.
94
+
95
+ If a configured host is unreachable, snapshot/capability reads return the appliance with `connected: false`, `connection.state: "offline"`, and only a safe network reason instead of converting the device into an unrelated generic error. Values already held by the session are marked stale and retained. An active Events node keeps reconnecting with bounded backoff and emits recovery updates after the appliance becomes reachable again.
96
+
97
+ The complete sequence follows hcpy and homeconnect-websocket:
98
+
99
+ 1. The appliance sends `POST /ei/initialValues`. The response uses the same `sID`, the same `msgID`, and the EI-version-specific application structure. The client's next counter starts exactly at `data.edMsgID`.
100
+ 2. `/ci/services` is always requested as `GET`, version 1, with **no** `data` member. Device code `0`, `200`, or no code is accepted as success.
101
+ 3. Every returned service version is used for its service. For `ci < 3`, `/ci/authentication` first follows hcpy and sends `POST` with a 32-byte nonce; only a concrete rejection triggers the documented `GET` fallback.
102
+ 4. `NOTIFY /ei/deviceReady` uses the negotiated EI version and omits an empty `data` array.
103
+ 5. Device information is optionally read from `/iz/info` when `iz` is advertised, otherwise `/ci/info`; `/ni/info` is used only when `ni` exists. Rejection of these optional diagnostic paths does not block the read-only session.
104
+ 6. The five `/ro/*` GETs use the advertised `ro` version. Not every model supports every resource: an individual code 400 is recorded while the remaining read paths continue.
105
+
106
+ Unsolicited appliance `NOTIFY` and `RESPONSE` messages are processed immediately. Initial/mandatory/value data already delivered by the appliance counts as successful read-only reception even if a later fallback GET is rejected. Appliance-originated housekeeping `GET`/`POST` requests (for example `/ei/keepAlive`) receive a `RESPONSE` with the same session and appliance `msgID`; this does not advance the client's `edMsgID` counter. WebSocket ping/pong remains separate. The connection remains technically read-only: the nonce, device-ready notification, and protocol responses cannot change settings or programs.
107
+
108
+ ## Network restrictions
109
+
110
+ Before opening a socket, the configured hostname must resolve **only** to RFC1918, IPv4 link-local, IPv6 unique-local, or IPv6 link-local addresses. Loopback, URLs, paths, credentials in hostnames, and public DNS results are rejected. This prevents the local profile field from becoming an arbitrary Internet socket target.
111
+
112
+ ## Troubleshooting
113
+
114
+ | Code/symptom | Meaning/action |
115
+ |---|---|
116
+ | `LOCAL_PROFILE_REQUIRED` | Set a profile folder or optional manual profile JSON, then save and deploy. |
117
+ | `LOCAL_PROFILE_FOLDER_REQUIRED` | Automatic discovery needs a directory, not an individual file. |
118
+ | `LOCAL_PROFILE_FILES_INCOMPLETE` | Keep the appliance JSON, `DeviceDescription.xml`, and `FeatureMapping.xml` from the same download together, then scan again. |
119
+ | `LOCAL_PROFILE_ORPHAN_COMPANION` | An XML/JSON companion cannot be paired by folder/base name; restore the original downloader filenames. |
120
+ | `LOCAL_MAPPING_HOST_REQUIRED` | The selected card itself has no Host/IP. Enter it; unfilled sibling cards do not block an individual test. |
121
+ | `LOCAL_MAPPING_DUPLICATE` | Keep exactly one central assignment for each profile ID. |
122
+ | `LOCAL_MAPPING_PROFILE_NOT_FOUND` | The folder changed after mapping; rescan to remove stale assignments. |
123
+ | `LOCAL_PROFILE_KEY_MISSING` / `LOCAL_PROFILE_IV_MISSING` | Re-download the complete profile; do not invent keys. |
124
+ | `LOCAL_HOST_NOT_PRIVATE` | Correct DNS/IP; the appliance must resolve only to a private/link-local address. |
125
+ | `LOCAL_NETWORK_ERROR` / `LOCAL_CONNECT_TIMEOUT` | Check same-LAN reachability, DNS/IP, VLAN/firewall, appliance Wi-Fi, and port 80/443. `EHOSTUNREACH` means there is no network path, commonly because the appliance is off/asleep, the IP is stale, VLAN routing is missing, or a firewall blocks it. |
126
+ | `LOCAL_HANDSHAKE_TIMEOUT` / `LOCAL_REQUEST_TIMEOUT` | The socket opened, but the appliance did not complete the protocol/read response. Check profile, transport, firmware compatibility, and packet loss. |
127
+ | `LOCAL_TRANSPORT_UNSUPPORTED` | Keep Auto, or correct an explicit AES/TLS override; also verify Node.js/OpenSSL PSK support. |
128
+ | `LOCAL_TLS_PSK_UNAVAILABLE` | The Node.js/OpenSSL runtime exposes none of the required TLS 1.2 PSK cipher suites; use a runtime/OpenSSL build with PSK support. |
129
+ | `LOCAL_PROFILE_KEY_REJECTED` / `LOCAL_PROFILE_AUTHENTICATION_FAILED` | The profile/key does not match this appliance or is incomplete. Re-download and assign the correct profile; never guess keys. |
130
+ | `LOCAL_PROTOCOL_REJECTED` | The appliance returned a protocol code (for example 400). Diagnostics identify the exact resource, action, negotiated version, and device code; a completely rejected read sequence lists every attempt. Enable the sanitized trace and compare transport/profile/firmware. |
131
+ | `LOCAL_CONNECT_FAILED` | Connection failed without a more specific network/key/transport classification. Check the preceding items. |
132
+ | `LOCAL_WEBSOCKET_UPGRADE_FAILED` | TCP/TLS succeeded, but the appliance rejected the RFC 6455 upgrade on `/homeconnect`. Verify profile assignment, Auto/AES/TLS, IP, and proxies; a returned HTTP status is reported without exposing headers or keys. |
133
+ | `LOCAL_CRYPTO_AUTH_FAILED` / `LOCAL_CRYPTO_PADDING_INVALID` | PSK/IV mismatch or lost AES-CBC/HMAC stream state; the client closes immediately. Re-download profiles if persistent. |
134
+ | `LOCAL_HEARTBEAT_TIMEOUT` / `LOCAL_DISCONNECTED` | The connection was established but stopped responding. Check appliance power/standby, Wi-Fi, VLAN, and firewall; the client rebuilds the complete session with backoff. |
135
+ | `LOCAL_TRACE_PATH_INVALID` | Use an absolute path inside the Node-RED host/container for file tracing. With no path, only the safe in-memory ring buffer is used. |
136
+ | TLS handshake failure | Confirm the profile says TLS, Node.js/OpenSSL exposes PSK cipher suites, and no TLS-inspecting proxy is present. |
137
+ | raw numeric UIDs | XML mapping was absent or did not match the profile. Keep the three downloader files together. |
138
+ | `LOCAL_READ_ONLY` | Expected security behavior. Use a separate cloud connection only if guarded cloud writes are required. |
139
+
140
+ ## Community protocol references
141
+
142
+ The implementation was independently written for this Node-RED package using publicly available community protocol observations, especially:
143
+
144
+ - `chris-mc1/homeconnect_local_hass` and `chris-mc1/homeconnect_websocket`
145
+ - `hcpy2-0/hcpy`
146
+ - `zibous/homeconnect-hcpy`
147
+ - `Asmir1975/homeconnect_local_hass-UI`
148
+ - `SukramJ/go-homeconnect2mqtt` protocol notes
149
+
150
+ No appliance profile, user credential, serial number, host, PSK, IV, or code copied from those projects is included in this package. See [NOTICE](../NOTICE) for attribution and license reminders.