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

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 (55) hide show
  1. package/BUILD-INFO.json +23 -0
  2. package/CHANGELOG.md +53 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE +19 -0
  5. package/README.de.md +344 -0
  6. package/README.md +343 -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/GITHUB-UPDATE.md +76 -0
  14. package/docs/LOCAL-LAN.de.md +150 -0
  15. package/docs/LOCAL-LAN.md +150 -0
  16. package/docs/MESSAGE-API.md +111 -0
  17. package/docs/RELEASE.md +91 -0
  18. package/docs/TROUBLESHOOTING.de.md +230 -0
  19. package/docs/TROUBLESHOOTING.md +230 -0
  20. package/docs/images/README.md +10 -0
  21. package/docs/images/dashboard-details-en.png +0 -0
  22. package/docs/images/dashboard-overview-en.png +0 -0
  23. package/docs/official-api-contract.json +418 -0
  24. package/examples/01-first-steps-device-list.json +148 -0
  25. package/examples/02-events.json +165 -0
  26. package/examples/03-safe-read-write.json +155 -0
  27. package/examples/04-local-read-only.json +214 -0
  28. package/examples/README.de.md +62 -0
  29. package/examples/README.md +62 -0
  30. package/examples/dashboard-dishwasher-washer-oven.json +628 -0
  31. package/examples/dashboard-screenshot-simulated.json +405 -0
  32. package/examples/demo-home-connect-flowfuse-1.34.1-scriptless.json +390 -0
  33. package/lib/client.js +600 -0
  34. package/lib/constants.js +66 -0
  35. package/lib/dashboard-i18n.js +255 -0
  36. package/lib/device-label.js +39 -0
  37. package/lib/errors.js +51 -0
  38. package/lib/health.js +525 -0
  39. package/lib/local-client.js +1014 -0
  40. package/lib/local-crypto.js +122 -0
  41. package/lib/local-diagnostics.js +115 -0
  42. package/lib/local-profile.js +706 -0
  43. package/lib/local-trace.js +103 -0
  44. package/lib/oauth.js +23 -0
  45. package/lib/rate-limiter.js +186 -0
  46. package/lib/simulator.js +290 -0
  47. package/lib/sse.js +58 -0
  48. package/lib/validation.js +194 -0
  49. package/lib/websocket-transport.js +374 -0
  50. package/nodes/home-connect.html +795 -0
  51. package/nodes/home-connect.js +699 -0
  52. package/nodes/locales/de/home-connect.html +19 -0
  53. package/nodes/locales/de/home-connect.json +10 -0
  54. package/nodes/locales/en-US/home-connect.json +10 -0
  55. package/package.json +76 -4
@@ -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.
@@ -0,0 +1,111 @@
1
+ # Message API
2
+
3
+ All functional nodes preserve unrelated incoming properties. Output 2 contains a structured error in `msg.payload`. Confirmation values are removed from success and error output.
4
+
5
+ ## Normalized health envelope
6
+
7
+ Every functional output includes:
8
+
9
+ ```js
10
+ msg.health = 'ok'; // ok | running | info | warning | action_required | offline | error
11
+ msg.warnings = [{
12
+ source: 'status', // status | event | connection | api | oauth
13
+ code: 'HC_...', // stable integration code
14
+ severity: 'warning', // info | warning | error | critical
15
+ text: { de: '...', en: '...' },
16
+ transient: false
17
+ }];
18
+ msg.homeConnect.health = msg.health;
19
+ msg.homeConnect.warnings = msg.warnings;
20
+ msg.homeConnect.healthSchema = 'home-connect-health/v1';
21
+ msg.homeConnect.healthAt = '...';
22
+ ```
23
+
24
+ Snapshots and discovery entries also carry `payload.health`, `payload.warnings`, `payload.healthSchema`, and `payload.healthAt`. Raw API status and event values are retained. Transient notices are deduplicated and expire; disconnected/offline state remains until a later connection or snapshot confirms recovery. Errors are assessed for that error output and do not poison later successful messages.
25
+
26
+ ## discover
27
+
28
+ ```js
29
+ msg.haId = '...'; // single mode
30
+ msg.force = true; // bypass five-minute capability cache
31
+ ```
32
+
33
+ No real appliance ID, name, program or capability is built into the package. The editor's **Load devices** button and discovery output use the appliances returned by the connected account or protected local profiles. For local connections, Host/IP and the optional Auto/AES/TLS choice exist only in `home-connect-config`; Read and Events nodes store only a selected appliance/profile ID.
34
+
35
+ Documented appliance fields such as `name`, `brand`, `type`, `vib` and `connected` remain available under `appliance`. Firmware version/history/update is not part of the public API and has no message operation.
36
+
37
+ ## read
38
+
39
+ ```js
40
+ msg.haId = '...';
41
+ msg.operation = 'snapshot';
42
+ msg.key = '...'; // resource key, map ID or cleaning-summary counter
43
+ msg.subKey = '...'; // detected object, room or zone ID
44
+ ```
45
+
46
+ Operations:
47
+
48
+ - `snapshot`, `capabilities`, `appliances`, `appliance`
49
+ - `status`, `status-item`, `settings`, `setting`
50
+ - `all-programs`, `programs`, `program`
51
+ - `active-program`, `active-options`, `active-option`
52
+ - `selected-program`, `selected-options`, `selected-option`
53
+ - `commands`, `images`, `image`
54
+ - `maps`, `map`, `map-occupancy-grid`, `map-layout`, `map-cleaning-status`
55
+ - `map-detected-objects`, `map-detected-object`, `map-rooms`, `map-room`, `map-zones`, `map-zone`
56
+ - `map-robot-position`, `map-config-restrictions`, `map-cleaning-summaries`, `map-cleaning-summary`
57
+
58
+ `image` returns:
59
+
60
+ ```js
61
+ msg.payload = {
62
+ contentType: 'image/jpeg',
63
+ data: Buffer.from(/* JPEG bytes */)
64
+ };
65
+ ```
66
+
67
+ The operation may select only a predefined resource; it cannot provide a URL or HTTP method. Local connections support appliance/snapshot/status/settings/program reads derived from `/ro/*` values. Cloud-only image/map operations return `LOCAL_READ_UNSUPPORTED` locally.
68
+
69
+ ## events
70
+
71
+ An input message with `msg.haId` restarts the stream for that appliance. Use `*` for all configured appliances. The cloud client reconnects SSE with bounded exponential backoff and sends `Last-Event-ID` when supplied. The local client maintains per-appliance WebSockets and emits `STATUS`. Both emit synthetic `CONNECTED` and `DISCONNECTED` lifecycle events. Local event envelopes include `connection.local: true` and `connection.readOnly: true`.
72
+
73
+ ## write
74
+
75
+ A request can be supplied in `msg.payload`; top-level `msg.haId`, `msg.action`, `msg.program`, `msg.key`, `msg.value` and `msg.options` take precedence.
76
+
77
+ ### Start a program
78
+
79
+ ```js
80
+ msg.payload = {
81
+ action: 'start-program',
82
+ haId: '...',
83
+ program: 'Cooking.Oven.Program.HeatingMode.HotAir',
84
+ options: [{ key: 'Cooking.Oven.Option.SetpointTemperature', value: 180 }],
85
+ confirm: 'CONFIRM'
86
+ };
87
+ ```
88
+
89
+ ### Other actions
90
+
91
+ ```js
92
+ msg.payload = { action: 'stop-program', haId: '...', confirm: 'CONFIRM' };
93
+ msg.payload = { action: 'select-program', haId: '...', program: '...', options: [], confirm: 'CONFIRM' };
94
+ msg.payload = { action: 'set-setting', haId: '...', key: 'BSH.Common.Setting.PowerState', value: 'BSH.Common.EnumType.PowerState.Standby', confirm: 'CONFIRM' };
95
+ msg.payload = { action: 'command', haId: '...', key: 'BSH.Common.Command.PauseProgram', value: true, confirm: 'CONFIRM' };
96
+ msg.payload = { action: 'set-active-option', haId: '...', key: 'BSH.Common.Option.Duration', value: 600, confirm: 'CONFIRM' };
97
+ msg.payload = { action: 'set-selected-option', haId: '...', key: '...', value: '...', confirm: 'CONFIRM' };
98
+ ```
99
+
100
+ Options may also be an object shorthand:
101
+
102
+ ```js
103
+ msg.options = {
104
+ 'Cooking.Oven.Option.SetpointTemperature': 180,
105
+ 'BSH.Common.Option.Duration': { value: 1800, unit: 'seconds' }
106
+ };
107
+ ```
108
+
109
+ Arbitrary methods, paths, body envelopes and bulk writes are intentionally unsupported. The confirmation token is stored in Node-RED credentials rather than in the flow JSON.
110
+
111
+ Local configurations reject every write with `LOCAL_READ_ONLY` before capability validation or network I/O. This cannot be overridden by `msg`, editor fields, or `allowWrite`.
@@ -0,0 +1,91 @@
1
+ # Release procedure for 0.2.0
2
+
3
+ The feature set is frozen at build `hc-0.2.0-final`. Only release-blocking corrections are allowed, followed by the complete validation sequence.
4
+
5
+ ## Package identity
6
+
7
+ - npm package: `@impact0815/node-red-contrib-home-connect`
8
+ - version: `0.2.0`
9
+ - GitHub repository: `impact0815/node-red-contrib-home-connect`
10
+ - npm homepage: `https://www.npmjs.com/package/@impact0815/node-red-contrib-home-connect`
11
+ - npm TGZ filename: `impact0815-node-red-contrib-home-connect-0.2.0.tgz`
12
+
13
+ ## Required local checks
14
+
15
+ ```bash
16
+ npm ci --ignore-scripts
17
+ npm run release:check
18
+ npm run validate
19
+ npm pack --json
20
+ sha256sum impact0815-node-red-contrib-home-connect-0.2.0.tgz
21
+ tar -tzf impact0815-node-red-contrib-home-connect-0.2.0.tgz | sort
22
+ npm publish --dry-run ./impact0815-node-red-contrib-home-connect-0.2.0.tgz --access public --registry https://registry.npmjs.org/
23
+ ```
24
+
25
+ The screenshots in `docs/images/` must remain the reviewed simulated images. The examples and package payload must not contain credentials, account data, real appliance identifiers, or private network data.
26
+
27
+ ## Existing GitHub repository: normal additional commit
28
+
29
+ Follow [GITHUB-UPDATE.md](GITHUB-UPDATE.md). In summary:
30
+
31
+ ```bash
32
+ git switch main
33
+ git pull --ff-only origin main
34
+ npm ci --ignore-scripts
35
+ npm run release:check
36
+ npm run validate
37
+ git add -A
38
+ git diff --cached --check
39
+ git commit -m "Use scoped npm package and finalize 0.2.0"
40
+ git push origin main
41
+ ```
42
+
43
+ Do not force-push. Wait until GitHub Actions CI succeeds on `main`.
44
+
45
+ ## Tag and GitHub Release
46
+
47
+ Confirm that `v0.2.0` does not already exist. Then:
48
+
49
+ ```bash
50
+ git tag -a v0.2.0 -m "@impact0815/node-red-contrib-home-connect v0.2.0"
51
+ git push origin v0.2.0
52
+
53
+ ARTIFACT_DIR="/absolute/path/to/release-files"
54
+ gh release create v0.2.0 \
55
+ "$ARTIFACT_DIR/impact0815-node-red-contrib-home-connect-0.2.0-source.zip" \
56
+ "$ARTIFACT_DIR/impact0815-node-red-contrib-home-connect-0.2.0.tgz" \
57
+ "$ARTIFACT_DIR/SHA256SUMS.txt" \
58
+ --repo impact0815/node-red-contrib-home-connect \
59
+ --verify-tag \
60
+ --title "@impact0815/node-red-contrib-home-connect 0.2.0" \
61
+ --notes-file ./RELEASE_NOTES.md
62
+ ```
63
+
64
+ ## npm publication
65
+
66
+ ```bash
67
+ ARTIFACT_DIR="/absolute/path/to/release-files"
68
+ npm config get registry
69
+ npm whoami --registry https://registry.npmjs.org/
70
+ npm view @impact0815/node-red-contrib-home-connect versions --json --registry https://registry.npmjs.org/ || true
71
+ npm publish "$ARTIFACT_DIR/impact0815-node-red-contrib-home-connect-0.2.0.tgz" --access public --registry https://registry.npmjs.org/
72
+ npm view @impact0815/node-red-contrib-home-connect@0.2.0 name version dist.integrity dist.tarball --registry https://registry.npmjs.org/
73
+ ```
74
+
75
+ If npm requires two-factor authentication, complete its prompt or add a current one-time password with `--otp=<code>`. Never put an npm token or OTP into the repository.
76
+
77
+ ## Post-publication smoke test
78
+
79
+ ```bash
80
+ TMPDIR="$(mktemp -d)"
81
+ cd "$TMPDIR"
82
+ npm init -y
83
+ npm install @impact0815/node-red-contrib-home-connect@0.2.0
84
+ node -p "require('@impact0815/node-red-contrib-home-connect/BUILD-INFO.json')"
85
+ ```
86
+
87
+ The package name must be `@impact0815/node-red-contrib-home-connect`, the version `0.2.0`, and the build ID `hc-0.2.0-final`.
88
+
89
+ ## Node-RED Flow Library
90
+
91
+ After npm publication, sign in at `https://flows.nodered.org/add/node`, enter the exact package name `@impact0815/node-red-contrib-home-connect`, and submit it. The package must already be public on npm and must retain the `node-red` keyword, README, license, and `node-red.nodes` metadata.
@@ -0,0 +1,230 @@
1
+ # Home Connect: Troubleshooting und FAQ
2
+
3
+ Zurück zur [deutschen README](../README.de.md).
4
+
5
+ Diese Anleitung beginnt beim sichtbaren Symptom. Zugangsdaten, Hostnamen, Geräte-IDs und OAuth-Codes gehören nie in Fehlerberichte.
6
+
7
+ ## 1. Vorprüfung in zwei Minuten
8
+
9
+ 1. Läuft wirklich der erwartete Build?
10
+ ```bash
11
+ cd ~/.node-red
12
+ node -p "require('./node_modules/@impact0815/node-red-contrib-home-connect/BUILD-INFO.json')"
13
+ ```
14
+ Erwartet: Version `0.2.0`, Build `hc-0.2.0-final`, `pkce: false`.
15
+ 2. Ist die richtige Connection ausgewählt und eindeutig benannt?
16
+ 3. Wurde nach der letzten Änderung **Fertig → Deploy** ausgeführt?
17
+ 4. Sind Redirect URI im Developer Portal und in Node-RED exakt gleich?
18
+ 5. Ist die Node-RED-Adresse vom verwendeten Browser erreichbar?
19
+ 6. Wurde OAuth bis zur Seite **Home Connect authorized** abgeschlossen?
20
+ 7. Enthalten die Scopes mindestens `IdentifyAppliance` und die benötigten gerätespezifischen Rechte?
21
+
22
+ ## 2. Autorisieren ist deaktiviert
23
+
24
+ Das ist Absicht: Die Config-Node prüft, ob genau diese gespeicherte Konfiguration bereits in der laufenden Node-RED-Runtime existiert.
25
+
26
+ - **Neue/änderte Verbindung:** Fertig klicken, deployen, Verbindung erneut öffnen.
27
+ - **Integrierter Simulator:** OAuth ist nicht erforderlich.
28
+ - **Weiterhin deaktiviert:** Node-RED vollständig neu starten und Browser hart neu laden. Prüfen, ob eine zweite/alte Paketinstallation existiert.
29
+
30
+ ## 3. `CONFIG_NOT_FOUND`
31
+
32
+ **Bedeutung:** Die URL enthält die ID einer Config-Node, welche die laufende Runtime nicht kennt.
33
+
34
+ Typische Ursachen:
35
+
36
+ - Beispiel- oder Simulator-Config wurde referenziert.
37
+ - Verbindung wurde zwar im Editor angelegt, aber nicht deployt.
38
+ - Alter Browser-Tab enthält eine Autorisierungs-URL einer gelöschten Config.
39
+ - Nach Paket-/Flow-Wechsel läuft noch eine andere Runtime-Instanz.
40
+
41
+ **Behebung:**
42
+
43
+ 1. Alten Auth-Tab schließen.
44
+ 2. Im verwendeten Home-Connect-Node die richtige Connection wählen.
45
+ 3. Connection eindeutig benennen.
46
+ 4. Fertig → Deploy.
47
+ 5. Connection erneut öffnen.
48
+ 6. Autorisierung über den dortigen Knopf neu starten.
49
+
50
+ ## 4. Falsche oder irreführende Verbindungsbezeichnung
51
+
52
+ Technisch steuert das Feld **Umgebung** den API-Host, nicht der Name. Ein Name wie `Simulator` bei Umgebung `Production` erhöht aber das Fehlerrisiko. Die Oberfläche zeigt deshalb eine Warnung. Name korrigieren, speichern und deployen.
53
+
54
+ ## 5. Redirect URI: exakter Vergleich
55
+
56
+ Folgende Teile müssen identisch sein:
57
+
58
+ | Teil | Häufiger Fehler |
59
+ |---|---|
60
+ | Protokoll | `http` im Portal, `https` in Node-RED |
61
+ | Host | DNS-Name im Portal, IP-Adresse in Node-RED |
62
+ | Port | Port fehlt auf einer Seite |
63
+ | Pfad | Tippfehler in `/home-connect/oauth/callback` |
64
+ | Ende | zusätzlicher Slash `/callback/` |
65
+ | Groß-/Kleinschreibung | abweichende Zeichen im Pfad |
66
+
67
+ Am sichersten die vollständige URI aus einem Feld kopieren und im anderen einfügen.
68
+
69
+ ## 6. Nach Login: localhost, `ERR_EMPTY_RESPONSE`, Verbindung fehlgeschlagen
70
+
71
+ `localhost` zeigt aus Sicht des Browsers auf das Browser-Gerät. Läuft Node-RED auf NAS, Server, Raspberry Pi, Home Assistant, ioBroker oder in einem anderen Rechner/Container, erreicht der Browser dort nicht Node-RED.
72
+
73
+ 1. Einen vom Browser erreichbaren Hostnamen oder eine erreichbare IP-Adresse für Node-RED wählen.
74
+ 2. Callback-Pfad anhängen: `/home-connect/oauth/callback`.
75
+ 3. URI im Developer Portal ändern.
76
+ 4. Dieselbe URI in Node-RED eintragen.
77
+ 5. Fertig → Deploy.
78
+ 6. OAuth neu starten.
79
+
80
+ ## 7. `unauthorized_client` / `client or redirection URI not authorized`
81
+
82
+ Prüfreihenfolge:
83
+
84
+ 1. Wurde Client ID aus der richtigen Developer-Portal-Application kopiert?
85
+ 2. Stimmt die Redirect URI Zeichen für Zeichen?
86
+ 3. Verwendet die Application den Authorization Code Grant?
87
+ 4. Ist PKCE in dieser Application deaktiviert?
88
+ 5. Läuft der Build `hc-0.2.0-final`?
89
+ 6. Wurde nach jeder Änderung eine neue Autorisierung gestartet?
90
+
91
+ Wurde das Client Secret sichtbar, nicht weiter testen: Secret regenerieren, in Node-RED ersetzen, deployen und neu autorisieren.
92
+
93
+ ## 8. Nach Update läuft alter Code
94
+
95
+ Ein Flow-Deploy lädt keinen neuen Node.js-Runtime-Code. Zusätzlich zur npm-Version BUILD-INFO prüfen, um doppelte Installationen zu erkennen.
96
+
97
+ ```bash
98
+ cd ~/.node-red
99
+ npm uninstall @impact0815/node-red-contrib-home-connect
100
+ rm -rf node_modules/@impact0815/node-red-contrib-home-connect
101
+ npm install --force /absoluter/pfad/impact0815-node-red-contrib-home-connect-0.2.0.tgz
102
+ node -p "require('./node_modules/@impact0815/node-red-contrib-home-connect/BUILD-INFO.json')"
103
+ ```
104
+
105
+ Danach Node-RED/Container vollständig neu starten, Browser hart neu laden und OAuth neu beginnen. Authorization Codes und State-Links sind kurzlebig/einmalig und dürfen nicht wiederverwendet werden.
106
+
107
+ ## 9. `No refresh token is stored. Complete OAuth authorization first.`
108
+
109
+ Der Refresh Token wird erst beim erfolgreichen Callback gespeichert.
110
+
111
+ - Autorisierung aus der deployten Connection starten.
112
+ - Login und Zustimmung abschließen.
113
+ - Auf **Home Connect authorized** warten.
114
+ - Erst dann Test/Discover/Read/Events ausführen.
115
+
116
+ Bleibt der Fehler, den Fehler auf der Callback-Seite notieren, aber keine Codes/Tokens veröffentlichen.
117
+
118
+ ## 10. `insufficient_scope`
119
+
120
+ Der Token darf die angeforderte Ressource nicht lesen oder steuern.
121
+
122
+ 1. `IdentifyAppliance` beibehalten.
123
+ 2. Passenden gerätespezifischen Scope ergänzen, etwa `Dishwasher-Monitor`, `Washer-Monitor` oder `Oven-Monitor`.
124
+ 3. Für Aktionen zusätzlich nur benötigtes `*-Control` und/oder `*-Settings` ergänzen.
125
+ 4. Fertig → Deploy.
126
+ 5. Neu autorisieren; ein alter Refresh Token erhält nicht automatisch neue Rechte.
127
+
128
+ Der allgemeine Scope `Monitor` kann je nach Application/Konto unzureichend sein.
129
+
130
+ ## 11. Events zeigt dauerhaft `reconnecting`
131
+
132
+ `reconnecting` ist ein Folgezustand, nicht die eigentliche Ursache. Den zweiten Ausgang des Events-Nodes mit Debug verbinden.
133
+
134
+ Häufig:
135
+
136
+ - kein Refresh Token
137
+ - fehlender Monitor-Scope
138
+ - abgelaufene/entzogene Autorisierung
139
+ - Netzwerk-/Proxy-Unterbrechung des SSE-Streams
140
+ - zu viele parallele Event-Streams
141
+
142
+ Zuerst Geräteliste/Test reparieren, dann einen einzigen Events-Node mit Appliance ID `*` verwenden.
143
+
144
+ ## 12. Erfolgsbild
145
+
146
+ Die Einrichtung ist abgeschlossen, wenn:
147
+
148
+ 1. die Browserseite **Home Connect authorized** anzeigt,
149
+ 2. Test ohne Fehler eine Geräteanzahl meldet,
150
+ 3. **Geräte laden** die Auswahl befüllt,
151
+ 4. der Events-Node nach Verbindung Events ausgibt statt dauerhaft zu reconnecten.
152
+
153
+ ## 13. Sicheres Fehlerreporting
154
+
155
+ Erlaubt:
156
+
157
+ - Paketversion und Build-ID
158
+ - Node.js- und Node-RED-Version
159
+ - Umgebungstyp (Production/Simulator), ohne privaten Hostnamen
160
+ - Fehlercode und bereinigte Fehlermeldung
161
+ - Schritte zur Reproduktion mit Simulator
162
+
163
+ Nicht veröffentlichen:
164
+
165
+ - Client ID oder Client Secret
166
+ - Access-/Refresh-Token
167
+ - Authorization Code oder OAuth-State
168
+ - echte Redirect-URI/Hostname/IP
169
+ - echte `haId`, Serien-/Gerätenummern
170
+ - vollständige Events oder Debug-Ausgaben mit persönlichen Daten
171
+
172
+
173
+ ## 14. Health- und Warning-Codes verstehen
174
+
175
+ Jede funktionale Ausgabe enthält das Schema `home-connect-health/v1`:
176
+
177
+ - `ok`: keine aktuelle Auffälligkeit
178
+ - `running`: Programm läuft
179
+ - `info`: nützlicher, nicht dringender Hinweis
180
+ - `warning`: Einschränkung oder pausierter/abbrechender Zustand
181
+ - `action_required`: Eingriff oder neue Autorisierung erforderlich
182
+ - `offline`: Gerät, Event-Stream oder Netzwerk nicht erreichbar
183
+ - `error`: Geräte-/API-Fehler
184
+
185
+ `warnings` ist dedupliziert. Jeder Eintrag enthält mindestens `source`, `code`, `severity`, `text.de`, `text.en` und `transient`. Kurzlebige Hinweise besitzen zusätzlich Zeitfelder und verschwinden nach Ablauf. Statusbedingungen bleiben nur so lange bestehen, wie der aktuelle Status sie bestätigt.
186
+
187
+ Wichtige Codes:
188
+
189
+ | Code | Bedeutung | Maßnahme |
190
+ |---|---|---|
191
+ | `HC_OPERATION_ACTION_REQUIRED` | Gerät fordert lokalen Eingriff | Gerätedisplay/Tür/Vorräte prüfen |
192
+ | `HC_STREAM_DISCONNECTED` | SSE-Verbindung getrennt | Reconnect abwarten; bei Dauerfehler Ausgang 2 prüfen |
193
+ | `HC_OAUTH_INSUFFICIENT_SCOPE` | Token hat zu wenige Rechte | Scope korrigieren, deployen, neu autorisieren |
194
+ | `HC_OAUTH_AUTHORIZATION_EXPIRED` | Autorisierung fehlt/abgelaufen/widerrufen | neu autorisieren |
195
+ | `HC_API_RATE_LIMITED` | HTTP 429/API-Limit | Polling reduzieren, `retryAfter` beachten |
196
+ | `HC_NETWORK_ERROR` | Netzwerk/Timeout | DNS, Proxy, TLS und Erreichbarkeit prüfen |
197
+
198
+ Der Events-Node erzeugt zusätzlich synthetische `CONNECTED`-/`DISCONNECTED`-Lebenszyklusereignisse für den tatsächlichen SSE-Stream. Sie enthalten keine Zugangsdaten.
199
+
200
+ ## 15. HTTP 429 / `1000 calls in 1 day`
201
+
202
+ Der lokale Limiter verhindert standardmäßig, dass dieses Paket mehr als 900 API-Versuche in einem rollierenden 24-Stunden-Fenster sendet. In der Verbindung werden der aktuelle Verbrauch und eine eventuelle Sperrzeit angezeigt. Bei ausgeschöpftem Budget erscheint `LOCAL_RATE_LIMITED`; `retryAfter` und `resetAt` geben die verbleibende Wartezeit an.
203
+
204
+ Wenn Home Connect bereits HTTP 429 mit einer Meldung wie `remaining period of 75502 seconds` zurückgegeben hat, übernimmt das Paket diese Sperrzeit und sendet bis dahin keine weiteren API-Aufrufe. Der Zustand bleibt über Deploys und Neustarts erhalten. Nicht wiederholt auf **Test** oder Inject-Nodes klicken. Polling deutlich reduzieren, nach Möglichkeit einen All-Appliance-Event-Stream verwenden und Snapshots nicht alle 15 Sekunden abrufen. Andere Rechner oder Programme mit derselben Developer-Portal-Application separat prüfen, da sie nicht vom lokalen Zähler erfasst werden.
205
+
206
+ ## 16. Firmwareversion oder Firmwareupdate fehlt
207
+
208
+ Das ist kein Konfigurationsfehler. Die öffentlich dokumentierte Home-Connect-API stellt Firmwareversion, Firmwarehistorie und Firmwareupdate nicht bereit. Das Paket verwendet keine undokumentierten Endpunkte und bietet absichtlich keinen Firmware-Button. Sichtbar sind nur dokumentierte Geräteinformationen wie Name, Marke, Typ, `vib`/E-Nummer und Verbindungsstatus, sofern geliefert. Firmware ausschließlich über offizielle Hersteller-/Home-Connect-Kanäle prüfen.
209
+
210
+ ## 17. Lokale LAN-Profile und Verbindungsfehler
211
+
212
+ Bei lokalen Konfigurationen sind OAuth-, Autorisierungs-, Refresh-Token- und Cloud-Tagesbudget-Bereiche absichtlich ausgeblendet. Erfolgreich ist die Einrichtung, wenn **Verbindung testen** in jeder Gerätekarte eine lokale Read-only-Verbindung meldet und Discover/Read/Events `local: true` sowie `readOnly: true` liefern.
213
+
214
+ In dieser Reihenfolge prüfen:
215
+
216
+ 1. Prüfen, ob der Profilordner im Node-RED-Host/-Container wirklich unter dem eingetragenen Pfad gemountet und für das Servicekonto lesbar ist. Dann ausdrücklich **Profile suchen / Geräte erkennen** anklicken.
217
+ 2. Geräte-JSON, `DeviceDescription.xml` und `FeatureMapping.xml` desselben Profile-Downloader-Exports zusammenlassen. `LOCAL_PROFILE_FILES_INCOMPLETE` nennt die fehlende Datei; `LOCAL_PROFILE_ORPHAN_COMPANION` weist auf unpassende Ordner/Dateinamen hin.
218
+ 3. Bei einer doppelten Profil-ID nennt der Scanner die betroffenen Dateien. Keine Zuordnung raten: doppelten/alten Export entfernen und erneut suchen.
219
+ 4. Nach erfolgreicher Suche muss zum Beispiel **3 Geräteprofile gefunden** erscheinen. Für jedes vollständige Profil gibt es genau eine Karte. Fehlt eine Karte, die Hinweise direkt unter dem Suchergebnis lesen.
220
+ 5. In jeder Karte Host/IP eintragen. Fehlende, formal falsche oder öffentliche IPs werden rot markiert. Hostnamen werden beim Test serverseitig aufgelöst. Nur private/link-lokale Ziele sind erlaubt; Loopback und öffentliche Ziele werden mit `LOCAL_HOST_NOT_PRIVATE` abgewiesen. Ein Einzeltest verwendet sofort die aktuellen Felder genau dieser Karte, benötigt noch kein Fertig/Deploy und wird nicht von leeren Nachbarkarten blockiert.
221
+ 6. **Verbindungsart: Auto (empfohlen)** beibehalten. Eine ausdrückliche Profilangabe AES/TLS/PSK hat Vorrang; IV-Anwesenheit ist nur der Fallback. `LOCAL_PROFILE_IV_MISSING` bei AES-Override bedeutet, dass das Original-IV fehlt.
222
+ 7. Pro Karte **Verbindung testen** anklicken und die sieben Stufen lesen. `LOCAL_NETWORK_ERROR`/Timeout stoppt bei **IP erreichbar**. `EHOSTUNREACH` ist ein fehlender Netzwerkpfad und kein Protokollfehler: Gerät aus/Standby, IP, WLAN, VLAN-Routing und Firewall prüfen. `LOCAL_TLS_PSK_UNAVAILABLE`/`LOCAL_TRANSPORT_UNSUPPORTED` stoppt bei **Transport erkannt**. `LOCAL_WEBSOCKET_UPGRADE_FAILED` bedeutet: TCP beziehungsweise TLS war erfolgreich, aber das Gerät lehnte den Upgrade auf `/homeconnect` ab. Der Test liest nur lokale Ressourcen.
223
+ 8. Erst wenn alle Karten gültig sind: **Fertig → Deployen**. Dabei wird `localDeviceMappings` nochmals gespeichert und nach Deploy wiederhergestellt. Host/IP nie in Read-/Events-Nodes eintragen; dort die verständliche Bezeichnung wählen, während intern die volle ID erhalten bleibt.
224
+ 9. `LOCAL_CRYPTO_AUTH_FAILED`, `LOCAL_PROFILE_KEY_REJECTED` oder `LOCAL_PROFILE_AUTHENTICATION_FAILED` bedeutet falsches/unvollständiges Profil; PSK/IV weder bearbeiten noch raten. `LOCAL_PROTOCOL_REJECTED` ist davon getrennt und nennt Ressource, Aktion, ausgehandelte Version und Gerätecode. Einzelne nicht unterstützte `/ro/*`-Pfade sind zulässig, sobald ein anderer Read oder ein Geräte-`NOTIFY` Werte geliefert hat. Werden alle fünf Pfade abgelehnt, enthält `rejectedResources` die komplette Folge. `/ci/services` muss Version 1 ohne leeres `data`-Array verwenden.
225
+ 10. Werden nur numerische UIDs angezeigt, die XML-Dateien desselben Downloads wiederherstellen und neu suchen.
226
+ 11. Manuelles Profil-JSON ist nur der optionale Bereich **Erweitert**. Bei gesetztem Ordner ist es deaktiviert/ignoriert. Bei Bedarf mit der Schaltfläche aus erkannten Profilen und Zuordnungen erzeugen statt selbst schreiben.
227
+ 12. Für reale Firmwareabweichungen unter **Erweitert** den sicheren Protokoll-Mitschnitt kurz aktivieren. Ohne Pfad erscheint nur der begrenzte, bereinigte Speicherpuffer beim Test; ein absoluter Pfad schreibt unter POSIX JSONL mit `0600`. Unter Windows den Mitschnittpfad per NTFS-ACL auf das Node-RED-Dienstkonto beschränken, weil POSIX-Rechte dort nicht zuverlässig sind. PSK, IV, Schlüssel, Nonces sowie Geräte-/Netzwerkkennungen werden entfernt. Vor Weitergabe trotzdem prüfen, danach ausschalten und Datei löschen.
228
+ 13. `LOCAL_HEARTBEAT_TIMEOUT` oder `LOCAL_DISCONNECTED` nach zunächst erfolgreicher Verbindung deutet auf Strom/Standby, WLAN-Roaming, VLAN/Firewall oder Paketverlust. Die Runtime baut Transport, Kryptozustand, `sID`, `msgID`, Services und Read-Session mit Backoff vollständig neu auf.
229
+
230
+ `LOCAL_READ_ONLY` ist kein Verbindungsfehler, sondern die erfolgreiche lokale Schreibsperre. Für abgesicherte Schreibzugriffe eine separate Cloud-Konfiguration verwenden. Siehe [Lokale LAN-Verbindung](LOCAL-LAN.de.md).