iobroker.miele-local 0.3.3 → 0.3.38

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 (49) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +355 -95
  3. package/README_de.md +274 -109
  4. package/admin/i18n/de.json +95 -0
  5. package/admin/i18n/en.json +95 -0
  6. package/admin/i18n/es.json +95 -0
  7. package/admin/i18n/fr.json +95 -0
  8. package/admin/i18n/it.json +95 -0
  9. package/admin/i18n/nl.json +95 -0
  10. package/admin/i18n/pl.json +95 -0
  11. package/admin/i18n/pt.json +95 -0
  12. package/admin/i18n/ru.json +95 -0
  13. package/admin/i18n/uk.json +95 -0
  14. package/admin/i18n/zh-cn.json +95 -0
  15. package/admin/jsonConfig.json +763 -511
  16. package/io-package.json +91 -48
  17. package/lib/api.js +39 -4
  18. package/lib/csv.js +315 -0
  19. package/lib/datenpunkte.js +322 -0
  20. package/lib/discovery.js +60 -1
  21. package/lib/dop2.js +92 -8
  22. package/lib/eco.js +173 -0
  23. package/lib/enums.js +19 -0
  24. package/lib/felder.js +216 -0
  25. package/lib/feldsuche.js +383 -0
  26. package/lib/ids.js +138 -0
  27. package/lib/kontrolle.js +166 -0
  28. package/lib/leafnamen.js +577 -0
  29. package/lib/leafscan.js +354 -0
  30. package/lib/leafverlauf.js +147 -0
  31. package/lib/names.js +440 -0
  32. package/lib/objects.js +218 -9
  33. package/lib/push.js +27 -2
  34. package/lib/sammler.js +199 -0
  35. package/lib/stats.js +237 -0
  36. package/main.js +3042 -71
  37. package/package.json +8 -8
  38. package/CHANGELOG_OLD.md +0 -7
  39. package/admin/i18n/de/translations.json +0 -54
  40. package/admin/i18n/en/translations.json +0 -54
  41. package/admin/i18n/es/translations.json +0 -54
  42. package/admin/i18n/fr/translations.json +0 -54
  43. package/admin/i18n/it/translations.json +0 -54
  44. package/admin/i18n/nl/translations.json +0 -54
  45. package/admin/i18n/pl/translations.json +0 -54
  46. package/admin/i18n/pt/translations.json +0 -54
  47. package/admin/i18n/ru/translations.json +0 -54
  48. package/admin/i18n/uk/translations.json +0 -54
  49. package/admin/i18n/zh-cn/translations.json +0 -54
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Immanuel
3
+ Copyright (c) 2026 Immanuel <github@freitag.online>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -15,122 +15,192 @@ account during operation, no detour through the Miele 3rd-party API.
15
15
  > local key (GroupID/GroupKey). After that the adapter runs offline, and the Miele app
16
16
  > keeps working unchanged.
17
17
 
18
- ## Features
18
+ **What it does:** reads the live state of every appliance in plain text, records each completed
19
+ programme with its consumption, and — if you allow it — starts, stops and pauses them.
20
+ **What it needs:** one login, and either mDNS on your network or the appliance IP addresses.
19
21
 
20
- - **Automatic credential retrieval** via a guided login (cloud OAuth), without
21
- re-provisioning the appliances.
22
- - **Automatic device discovery** via mDNS (`_mieleathome._tcp`).
23
- - **Live states** of all supported appliance classes (washing machine, dryer,
24
- dishwasher, oven, steam oven, hob, coffee machine and more) with plain-text decoding of
25
- status, program and phase.
26
- - **Real-time updates** via the SuperVision push channel (optional), with polling as a
27
- reliable fallback.
28
- - **Control** (optional): start/stop/pause, light, on/off through writable states — as
29
- long as "MobileStart/remote control" is enabled on the appliance.
30
-
31
- ## Installation
22
+ ## Quick start
32
23
 
33
24
  1. Install the adapter and create an instance.
34
- 2. On the **Login** tab, choose your country and follow the 3-step login guide below.
35
- 3. Paste the captured `miele://…` redirect address and click **Fetch GroupKey**. GroupID and
36
- GroupKey are stored securely (GroupKey encrypted).
37
- 4. Save. The adapter discovers your devices and creates the states.
38
-
39
- ### Step-by-step: Capturing the redirect URL
40
-
41
- Because the final `miele://` address is a custom mobile-app scheme, desktop browsers cannot open
42
- it automatically and will hang on a spinning wheel. Capture the URL using browser DevTools:
43
-
44
- 1. **Prepare DevTools:** Click **Open login page** to open the Miele login in a new tab. In that
45
- new tab, press **F12** to open Developer Tools and switch to the **Network** tab. Enable
46
- persistent logging:
47
- - **Chrome / Edge / Brave:** Check **Preserve log**.
48
- - **Firefox:** Click the gear icon ⚙️ and check **Persist Logs**.
49
- 2. **Sign In:** Enter the email and password of your Miele app account and submit the login.
50
- *Note:* The page will remain on a spinning wheel (or report a failed load) — this is completely
51
- expected and indicates success.
52
- 3. **Copy & Submit:** In the Network tab (F12), scroll to the very last (usually red) request. It
53
- starts with `redirect?redirect_uri=miele...` or `miele://oauth2-code/...`. Right-click this
54
- entry → **Copy URL** (or **Copy link address**). Paste it into the **miele:// redirect URL**
55
- field in ioBroker and click **Fetch GroupKey**.
56
-
57
- ## Required ports / firewall
25
+ 2. On the **Login** tab pick your country and follow the three steps below.
26
+ 3. Paste the captured `miele://…` address and click **Fetch GroupKey**.
27
+ 4. Save. The adapter finds your appliances and creates their states.
58
28
 
59
- | Direction | Port | Purpose | Required |
60
- |---|---|---|---|
61
- | inbound | TCP *push port* (default 18082) | Devices send real-time updates to ioBroker (callback target) | only when push is enabled |
62
- | in/out | UDP 5353 (mDNS) | device discovery and push registration | yes |
63
- | outbound | TCP 80 → devices | read states / send control commands | yes |
64
- | outbound | TCP 443 → miele-iot.com | only during login (fetch GroupKey) | login only |
29
+ If the adapter runs in a Docker container with bridge networking, discovery will find nothing —
30
+ enter the IP addresses by hand on the **Appliances** tab. See [Network](#network-ports-docker-push).
65
31
 
66
- Without push, **no inbound port** is required. ioBroker and the devices must be in the same
67
- broadcast segment (no VLAN/Docker-bridge separation) for mDNS to work.
32
+ ### The login, step by step
68
33
 
69
- ### Running in Docker (important)
34
+ The final address uses the `miele://` scheme of the mobile app. Desktop browsers cannot open it,
35
+ so the page stops at a spinning wheel and you read the address out of the browser yourself.
70
36
 
71
- If ioBroker runs in a **Docker container with bridge networking** (the default, e.g. the
72
- buanet image), the automatic **mDNS discovery cannot reach the devices** — multicast is not
73
- bridged. Direct communication (TCP 80) does work, because it is routed. In that case:
37
+ 1. **Prepare DevTools.** Click **Open login page** — a new tab opens. Press **F12** there, switch
38
+ to the **Network** tab and keep the log:
39
+ - **Chrome / Edge / Brave:** tick **Preserve log**.
40
+ - **Firefox:** gear icon ⚙️ → **Persist Logs**.
41
+ 2. **Sign in.** Enter the email and password of your Miele app account. The page will then hang on
42
+ a spinning wheel or report a failed load — that is what success looks like here.
43
+ 3. **Copy the address.** In the Network tab scroll to the last (usually red) entry, starting with
44
+ `redirect?redirect_uri=miele…` or `miele://oauth2-code/…`. Right-click → **Copy URL**, paste it
45
+ into the **miele:// redirect URL** field and click **Fetch GroupKey**.
74
46
 
75
- - **Enter the device IPs manually on the "Devices & polling" tab.** Polling then works
76
- normally.
77
- - **Push** does not work in a bridge network (the devices cannot reach the container). For
78
- push, run the container in **host networking** (`network_mode: host`) — the push port is
79
- then automatically open. In a bridge network you additionally have to map `-p
80
- 18082:18082`, but the callback return address sits behind NAT, so push stays unreliable.
47
+ GroupID and GroupKey are then stored in the instance configuration, the key encrypted. You never
48
+ need this procedure again.
81
49
 
82
- ### How push works (technical)
50
+ ## What you get
83
51
 
84
- When push is enabled, the adapter performs an **enrollment** per device: `PUT
85
- /Devices/<series>/SuperVision/<own-fab>` (registers the adapter as a peer) and several
86
- `POST /Subscriptions` with a **callback URL** `http://<ioBroker-LAN-IP>:<push-port>/…`. The
87
- device then sends state changes to that URL unsolicited (sub-second). Subscriptions are
88
- renewed periodically. If the device cannot reach the callback URL (bridge NAT), no pushes
89
- arrive — the adapter then falls back to polling.
52
+ Every appliance becomes one device with its serial number as the ID. Below it:
90
53
 
91
- ## Privacy
54
+ ### `state` — what the appliance is doing right now
55
+
56
+ | State | Meaning |
57
+ |---|---|
58
+ | `status` | operating state. The number carries the plain text as a value list, so the object browser and VIS show "In use" instead of `5`. |
59
+ | `statusText` | the same as text. Kept for setups that already read it. |
60
+ | `programId` / `programText` | running programme |
61
+ | `programPhase` / `programPhaseText` | phase within the programme |
62
+ | `remainingMinutes`, `elapsedMinutes`, `startInMinutes` | times in minutes |
63
+ | `remainingSeconds`, `elapsedSeconds` | to the second, if enabled |
64
+ | `estimatedEndTime` / `estimatedEndTimeText` | projected finish (timestamp in ms / `HH:MM`) |
65
+ | `temperature`, `targetTemperature` (plus zones 2 and 3) | temperatures |
66
+ | `signalDoor`, `signalInfo`, `signalFailure` | door and signal flags |
67
+ | `mobileStart` | whether the appliance currently accepts remote control |
68
+ | `light`, `spinningSpeed`, `dryingStepText` | appliance-specific |
69
+
70
+ The raw numbers and their `…Text` counterparts exist side by side on purpose: the raw value is
71
+ what you compare and chart, the text is what you display. Since 0.3.37 the raw value itself
72
+ carries the plain-text list, so in most places the text state is no longer needed.
73
+
74
+ ### `info` — what the appliance is
75
+
76
+ `connected`, `techType`, `fabNumber`, `matNumber`, `deviceType`, `xkmType`, `xkmVersion`,
77
+ `protocolVersion`, `operatingHours`, and the poll counters `pollTotal`, `pollErrors`,
78
+ `pollRetries`, `pollErrorRate`. `lastError` holds the reason the last request failed.
79
+
80
+ ### `eco` — energy and water
81
+
82
+ `eco.energy` (kWh), `eco.energyWh` (Wh), `eco.water` (l) where the appliance provides them, plus
83
+ `eco.quelle` naming which source a value came from. Read over DOP2; so far washing machines
84
+ deliver it. **The value the appliance reports is its own expectation, not a measurement.** For a
85
+ real figure, enter a metering plug's counter state on the **Polling & values** tab — the adapter
86
+ then records what each programme actually drew.
87
+
88
+ ### `history` and `stats` — what has run
89
+
90
+ Every completed programme is recorded with duration, programme, energy and water. The appliances
91
+ themselves keep nothing, so the history starts when you switch the feature on and cannot be
92
+ filled retroactively. `history.cyclesJson` holds the last programmes, `stats.week`, `stats.month`,
93
+ `stats.year` and `stats.total` the sums beside it.
92
94
 
93
- **No personal data** is stored by the adapter. GroupID, GroupKey and refresh token live
94
- only in the (encrypted) instance configuration or in ioBroker objects. No data is
95
- transmitted to third parties; in normal operation there is no cloud connection.
95
+ ### `control` — only if you allow it
96
96
 
97
- ## States (excerpt)
97
+ `start`, `stop`, `pause`, `powerOn`, `powerOff`, `lightOn`, `lightOff`. Writing `true` triggers
98
+ the command; the state resets itself. Commands only work while **MobileStart / remote control**
99
+ is enabled on the appliance, and some firmwares reject DOP2 writes outright.
98
100
 
99
- Per device under `<serial>.info` (static/connectivity), `<serial>.state` (live) and `<serial>.eco`:
101
+ ## Settings
100
102
 
101
- - `info.connected` — connectivity status (true when appliance responds)
102
- - `state.statusText` / `state.status` — operating state (plain text + raw value)
103
- - `state.programText` / `state.programId` — running program
104
- - `state.programPhaseText` / `state.programPhase` — program phase
105
- - `state.remainingMinutes`, `state.elapsedMinutes`, `state.startInMinutes`
106
- - `state.remainingSeconds`, `state.elapsedSeconds` (optional second-precise DOP2 values)
107
- - `state.estimatedEndTime` / `state.estimatedEndTimeText` — projected finish time
108
- - `state.temperature[Zone2/3]`, `state.targetTemperature[Zone2/3]`
109
- - `state.signalDoor`, `state.signalInfo`, `state.signalFailure`
110
- - `state.mobileStart` — whether remote control is enabled on the device
111
- - `state.light`, `state.spinningSpeed` (washing machine), `state.dryingStepText` (dryer)
112
- - `eco.energy` (kWh), `eco.energyWh` (Wh), `eco.water` (l) (where supported)
113
- - `info.techType`, `info.fabNumber`, `info.xkmType`, `info.xkmVersion`, `info.deviceType`
103
+ | Tab | What it holds |
104
+ |---|---|
105
+ | **Login** | country, the guided login, the captured address |
106
+ | **Appliances** | mDNS discovery, the fallback IP scan, manual IP addresses |
107
+ | **Polling & values** | poll intervals, German state names, second-precise times, EcoFeedback, energy meter, device internals |
108
+ | **Push & ports** | the optional real-time channel and its inbound port |
109
+ | **Control** | the switch that creates the writable states |
110
+ | **History** | recording of completed programmes, ring buffer, retention, History adapter |
111
+ | **Diagnostics** | everything for fault finding and field mapping — off by default |
112
+ | **Advanced** | GroupID and GroupKey by hand |
114
113
 
115
- With control enabled, additionally `<serial>.control.*` (start, stop, pause, powerOn,
116
- powerOff, lightOn, lightOff).
114
+ Every field carries its explanation underneath it in the admin; this page does not repeat them.
117
115
 
118
- ## Compatibility / limits
116
+ ## Network: ports, Docker, push
119
117
 
120
- - Tested against a washing machine (WCR860/EK037), dishwasher (G5840/EK037) and oven
118
+ | Direction | Port | Purpose | Required |
119
+ |---|---|---|---|
120
+ | inbound | TCP *push port* (default 18082) | appliances send updates to ioBroker | only with push |
121
+ | in/out | UDP 5353 (mDNS) | discovery and push registration | for discovery |
122
+ | outbound | TCP 80 → appliances | read states, send commands | yes |
123
+ | outbound | TCP 443 → miele-iot.com | fetch the GroupKey | login only |
124
+
125
+ Without push **no inbound port** is needed. For mDNS, ioBroker and the appliances must sit in the
126
+ same broadcast segment.
127
+
128
+ **Docker.** In a container with bridge networking multicast is not forwarded, so discovery finds
129
+ nothing — enter the IP addresses by hand, polling then works normally. Push does not work there at
130
+ all, because the appliances cannot reach the container: the callback address sits behind NAT.
131
+ Reliable push needs `network_mode: host`.
132
+
133
+ **How push works.** The adapter registers itself per appliance as a household peer
134
+ (`PUT /Devices/<series>/SuperVision/<own-fab>`) and subscribes with a callback URL. The appliance
135
+ then sends changes unsolicited, within a second. Not every module can do this: the older XKM
136
+ EK037 and EK057 accept the subscription and send nothing. Polling remains the reliable path.
137
+
138
+ ## Privacy
139
+
140
+ The adapter stores **no personal data**. GroupID, GroupKey and refresh token live only in the
141
+ encrypted instance configuration or in ioBroker objects. Nothing is transmitted to third parties;
142
+ in normal operation there is no cloud connection at all.
143
+
144
+ One exception worth knowing: the diagnostic data collection records the **start and end time of
145
+ every programme**. That stays in your instance — but if you pass the collection or its CSV export
146
+ on to someone, you pass those times along with it.
147
+
148
+ ## Compatibility and limits
149
+
150
+ - Tested against a washing machine (WCR860/EK037), a dishwasher (G5840/EK037) and an oven
121
151
  (H2469BP/EK057).
122
- - Refrigeration/freezer appliances are usually **read-only** locally (firmware rejects
123
- write commands).
124
- - Control commands require "MobileStart/remote control" on the device; some firmwares
125
- answer DOP2 writes with HTTP 404/500.
126
- - The SuperVision push is a best-effort addition; polling is the reliable default path.
152
+ - Refrigeration appliances are usually read-only locally; the firmware rejects writes.
153
+ - Control needs MobileStart on the appliance; some firmwares answer DOP2 writes with 404 or 500.
154
+ - EcoFeedback is not available everywhere. The dishwasher tested here provides no energy or water
155
+ counter over any readable leaf — for that appliance the values have to come from the cloud.
156
+ - Push is a best-effort addition, polling the default.
157
+
158
+ ## Diagnostics
159
+
160
+ Everything in this section is **off by default** and is not needed for day-to-day operation. It
161
+ exists for one question: which raw field of *your* appliance holds energy and water. The field
162
+ numbers differ per series, and the defaults in the adapter come from a WCR860.
163
+
164
+ **Raw fields.** Writes all fields of the eco leaf to `eco.felderJson` instead of only the two
165
+ evaluated ones.
166
+
167
+ **Data collection.** Records one dataset per completed programme — model, programme, all raw
168
+ fields, and at the end the final state of every answering leaf. To turn that into a mapping the
169
+ adapter needs a reference value: either from a cloud adapter, or entered by hand into
170
+ `sammlung.eingabeEnergie` and `sammlung.eingabeWasser` after a programme. `sammlung.fortschritt`
171
+ says what is still missing, `sammlung.befund` holds the result: which field fits, with which
172
+ divisor, and how closely.
173
+
174
+ **Leaf scan.** DOP2 addresses data as `unit/attribute`, and only a handful of those addresses are
175
+ documented anywhere. The scan works through the address space gently enough not to overwhelm the
176
+ module; `sammlung.leafScanJson` collects what answered. `sammlung.leafVerlaufFein` records a
177
+ single leaf closely while a programme runs — the field whose value grows with consumption is the
178
+ one you are looking for.
179
+
180
+ **CSV export.** The button on the Diagnostics tab writes two tables into the instance's file area
181
+ and opens the first:
182
+
183
+ - `sammlung-<date>.csv` — one row per programme: times, programme, the reference values, every raw
184
+ field in its own column, and for each leaf field the reading at start, at end and the difference
185
+ between them. For lifetime counters only that difference means anything.
186
+ - `befund-<date>.csv` — one row per field: how well it matches the reference, the best divisor,
187
+ the average and the largest deviation. This is the answer the collection exists for.
188
+
189
+ Semicolon separated, decimal comma, BOM — a double click opens them in a spreadsheet.
190
+
191
+ **Exploring an unknown appliance.** [docs/geraet-erkunden.md](docs/geraet-erkunden.md) (German)
192
+ describes the whole procedure in order: when to scan, how to tell a refusal from a busy signal,
193
+ how to read a series of numbers once you have one, and what a newly understood field needs before
194
+ it becomes a state. It also records what did *not* work, so nobody repeats it.
127
195
 
128
196
  ## Legal / disclaimer
129
197
 
130
- This is an **unofficial, privately developed** project and is **not affiliated with Miele
131
- & Cie. KG**, nor endorsed or reviewed by them. "Miele", "Miele@home" and related names are
132
- trademarks of Miele & Cie. KG and are used here only descriptively to indicate
133
- compatibility.
198
+ This is an **unofficial, privately developed** project and is **not affiliated with
199
+ [Miele & Cie. KG](https://www.miele.com/)**, nor endorsed or reviewed by them. "Miele",
200
+ "Miele@home" and related names are trademarks of
201
+ [Miele & Cie. KG](https://www.miele.com/) and are used here only descriptively to indicate
202
+ compatibility. Information about the appliances themselves is available from the
203
+ manufacturer at <https://www.miele.com/>.
134
204
 
135
205
  The adapter uses a local protocol that has been publicly documented through **reverse
136
206
  engineering**. Use is **at your own risk**; depending on device/firmware it may affect
@@ -152,6 +222,196 @@ engineering work of the projects `MieleRESTServer` (akappner),
152
222
 
153
223
  ## Changelog
154
224
 
225
+ ### 0.3.38
226
+ - **Object IDs are now consistently English.** The diagnostics channel was named `sammlung`
227
+ and carried German datapoint names throughout (`befund`, `fortschritt`, `datenJson`,
228
+ `leafVerlaufFein` …), plus four German ones in the otherwise English `history` channel
229
+ (`laufendSeit`, `zaehlerStart`, `gemessenLetzter`, `gemessenTotal`) - 57 of 446 objects in
230
+ total. In the repository request the reviewer therefore took them for hand-made script
231
+ datapoints. `sammlung` became `collection`, `befund` became `finding`, `laufendSeit`
232
+ became `runningSince`.
233
+ - **On first start the adapter migrates.** Every existing value moves to its new ID, and only
234
+ then is the old datapoint removed. Collected data is not lost - in a running installation
235
+ that is the field-search records, the leaf scan over 882 probed addresses and the trend
236
+ recording. A freshly set up instance finds nothing to migrate and writes nothing.
237
+ - **Recorded history stays**, but under the old ID: history is attached to the object and does
238
+ not move with it. Only the four numbers in the `history` channel are affected.
239
+ - **Anyone using the old IDs in their own scripts must follow suit.** Checked before renaming:
240
+ none of them appeared in 56 ioBroker scripts or in the operator's Android app.
241
+ - The IDs now live in one place, `lib/ids.js`, instead of scattered through the source.
242
+
243
+ ### 0.3.37
244
+ - **Plain text on the raw values.** `status`, `programType`, `programPhase` and `programId` now
245
+ carry their value list in `common.states`, built from the same tables the `…Text` states come
246
+ from, so the two cannot drift apart. The object browser and VIS show the text, the value stays a
247
+ number. The `…Text` states remain unchanged. Programme lists above 64 entries are left out - an
248
+ oven has 168 of them, and they do not belong inside every object.
249
+ - **Descriptions where they were missing.** Not one of the 446 objects carried a `common.desc`.
250
+ Everything writable now does, plus the whole diagnostic branch, the three timestamps in
251
+ milliseconds, and the five raw values whose meaning is documented nowhere. At
252
+ `sammlung.leafVerlaufFein` the format and an example are part of the description - without them
253
+ nobody could guess what to type in.
254
+ - **Admin rearranged.** "Appliances & polling" carried 25 fields from six unrelated topics and is
255
+ now split into **Appliances** and **Polling & values**; the three eco field indices moved to
256
+ Diagnostics, next to the collection that determines them. 18 blocks of running text disappeared:
257
+ their content now sits as one or two sentences under the field it belongs to, where the admin
258
+ shows it. Seven of 69 fields had a help text before, 28 have one now.
259
+ - **The diagnostic branch is only created when it is used.** Its fourteen states per appliance
260
+ used to appear for everyone. They now require data collection or the leaf scan to be switched
261
+ on; the scan and the close recording bring the channel with them so they cannot fail silently.
262
+ - **CSV: start, end and difference per leaf field.** Until now a record only held the final state
263
+ of the other leaves. For a lifetime counter like `hoursOfOperation` that says nothing about a
264
+ single programme - only the difference does, and those leaves are the only route for appliances
265
+ that do not answer 2/6195 at all. The adapter now reads the state at the start of a programme as
266
+ well. The export also gained the serial number as its own column, the adapter version, the
267
+ divisor and unit in the field headings, a unit on the temperature, and a note on records that
268
+ predate timestamps instead of silently empty cells.
269
+ - **Second file with the analysis.** `befund-<date>.csv` holds one row per field: match against the
270
+ reference, best divisor, average and largest deviation. That is the question the collection
271
+ exists for, and it no longer has to be rebuilt by hand in a spreadsheet.
272
+ - **Fix: role `value.volume` had returned.** A newly added table reintroduced a role the ioBroker
273
+ catalogue does not know; the repository check reports it as E1008. It is `value` again.
274
+ - **Object IDs of the diagnostic branch in one table.** They are not renamed yet, but they now live
275
+ in `lib/ids.js` instead of scattered through 180 kB of source, so a later rename is an edit to a
276
+ table rather than a search.
277
+ - **Device internals as datapoints.** The adapter now carries the field tables of every DOP2 leaf
278
+ documented by the public reverse-engineering projects `MieleRESTServer` (akappner) and
279
+ `ha-miele-at-lan` (tiehfood) - 52 structures, including those for ovens, coffee machines,
280
+ failures and the communication module, not just washing machines. A datapoint is created only
281
+ when the appliance actually delivers the field; nothing is created blindly. The values are
282
+ written from polls that already run, so no additional requests are made. New branch per device:
283
+ `detail.<channel>.<field>`. Off switch in the Diagnostics tab.
284
+ - **Fix: Generic value wrappers were read at the wrong position.** Miele wraps every measurement
285
+ in a small structure, and there are two shapes: `[mask, value, interpretation]` and
286
+ `[mask, min, max, current, step]`. The adapter always read the second entry - correct for the
287
+ first shape, the *minimum* for the second, which is 0 on every observed field. Seven fields of
288
+ the eco leaf were affected, among them `heatingTargetTemperature`: during a 40 °C programme the
289
+ appliance reported `[9, 0, 0, 40, 0, 0]` and the adapter 0. Field numbers inside structures are
290
+ now preserved and used.
291
+ - **Water: the appliance's own EcoFeedback comes first.** Where DOP2 2/1585 exists, its value for
292
+ the last programme is used; only where it does not does the adapter fall back to counting flow
293
+ meter impulses (field 21 / 200, verified against the house water meter over 24 programmes). The
294
+ new datapoint `eco.quelle` says which of the two a value came from.
295
+ - **The collector records every leaf.** At the end of a programme the adapter reads each answering
296
+ leaf once, gently (five seconds between requests, in the background), and appends the final state
297
+ to the record. This is what makes the collection useful for appliances that do not answer 2/6195
298
+ at all - a dishwasher that stays silent there answers nineteen other addresses. The CSV export
299
+ lists them as columns named `2/119.1 hoursOfOperation`.
300
+ - **Leaf scan covers unit 14.** An oven that answered all 882 scanned addresses with 404 was being
301
+ asked in the wrong units: `ha-miele-at-lan` documents the cooking programme lists at 14/1570 and
302
+ 14/1571.
303
+
304
+ ### 0.3.36
305
+ - **Fix: the final water reading of short programs is no longer missed.** With the regular ten-minute interval the last reading of a 35-minute
306
+ program fell up to nine minutes before the end, and the appliance resets its counters
307
+ immediately afterwards - the intermediate value was then stored as the final one. Eco
308
+ readings now switch to a one-minute interval for the last ten minutes of a program.
309
+ - A reading that still did not catch the end is **kept but marked**: it counts as a gap
310
+ rather than as a deviation, so a correct field assignment no longer looks faulty.
311
+ - **Admin translations completed.** Twelve texts of the settings page had no translation
312
+ entry and showed German to every other language; six stale keys were removed and the
313
+ language files moved to the short format (`admin/i18n/<lang>.json`). A test now keeps
314
+ the translations and `jsonConfig.json` in step.
315
+ - Configurable intervals are capped at runtime - Node fires a timer above 2^31-1 ms
316
+ immediately instead of late.
317
+ - `npm run test:unit` now picks up every test file; three of them had never run.
318
+ - Leaf scan: a pass aborted because the appliance is busy is now logged as info instead of a
319
+ warning - it is expected during programmes and resumes automatically from the saved progress.
320
+ - **Object structure check:** the data points added since 0.3.5 (data collection, metering
321
+ socket, operating hours) now carry names in all eleven languages, and the two input fields
322
+ for values from the Miele app use the writable role `level` instead of read-only `value.*`
323
+ roles. Existing objects are updated on start; a new test fails whenever a data point name
324
+ lacks one of the eleven languages.
325
+ - Repository checker: `common.news` limited to published versions and translated into all eleven
326
+ languages, size attributes for the new settings, `node:http` instead of `http`, contact e-mail
327
+ address in `package.json`, `io-package.json` and README.
328
+ - **Fix: water field divisor.** The setting was a unit select with 1, 10 or 100, while the default
329
+ for field 21 is 200 (5 ml per step) - the correct value could not be selected, and a missing
330
+ value fell back to 100 in one place and 10 in another. It is now a free number ("water field
331
+ divisor", decimals allowed, default 200); invalid values fall back to 200.
332
+ - **Fix: the measured energy of the metering socket was dropped** before it reached the
333
+ field check - every collected cycle lacked it. The field check now compares energy
334
+ fields against the measurement instead of the cloud value rounded to 0.1 kWh.
335
+
336
+ ### 0.3.18
337
+ - Leaf scan now separates a genuine refusal from a fault - a 503 or dropped socket no longer marks an address as checked that was never really asked.
338
+
339
+ ### 0.3.17
340
+ - Leaf scan with short timeout and incremental saving - a full pass takes minutes instead of hours.
341
+
342
+ ### 0.3.16
343
+ - Leaf scan: systematically probes the appliance for DOP2 leaves and records their fields - two passes (idle and running) reveal which fields move with the programme.
344
+
345
+ ### 0.3.15
346
+ - Ongoing check: compares delivered values against cloud or app readings after each cycle and reports when the field mapping drifts.
347
+
348
+ ### 0.3.14
349
+ - Water consumption verified: field 21 at 5 ml per count matches within 0.5% (8 cycles against the cloud) - field 26 was configured before and carries no measurement at all.
350
+
351
+ ### 0.3.13
352
+ - Field analysis detects empty fields and rigidly coupled values - a field that is a fixed multiple of another carries no measurement of its own.
353
+
354
+ ### 0.3.12
355
+ - Field analysis: evaluates collected cycles and reports which field carries energy and water - a field that stays constant is rejected.
356
+
357
+ ### 0.3.11
358
+ - Renaming of the energy fields now actually takes effect - it was reset by object creation as soon as a programme was running.
359
+
360
+ ### 0.3.10
361
+ - Total operating hours from DOP2 leaf 2/119.
362
+
363
+ ### 0.3.9
364
+ - Water field corrected (#26); hold rule no longer keeps stale values during a run.
365
+
366
+ ### 0.3.8
367
+ - Water value of a finished programme is kept instead of falling back to zero.
368
+
369
+ ### 0.3.7
370
+ - Water consumption read from the correct field; eco field indices are now configurable.
371
+
372
+ ### 0.3.6
373
+ - Optional raw eco field recording for diagnosing model-specific field indices.
374
+
375
+ ### 0.3.5
376
+ - **Fix: appliance status no longer flips to "off" during a running program.** A failed status
377
+ request was reported as a state change instead of being retried; every twenty-fifth poll
378
+ produced a spurious "off". Requests now get a second attempt, and an implausible jump from
379
+ "running" to "off" is discarded when the remaining time says the program is still going.
380
+ The retry count is exposed as `info.pollRetries`.
381
+ - **Fix: remaining time was read from the wrong field.** `remainingSeconds` carries only the
382
+ seconds component - at "2:01" it reads 0. The plausibility check now uses
383
+ `remainingMinutes`.
384
+ - **Fix: frozen EcoFeedback values are no longer booked as consumption.** When an appliance
385
+ keeps reporting the previous cycle's figures, the unchanged value is skipped instead of
386
+ being added to the new cycle.
387
+ - Requests are serialised per appliance, and a cycle now survives an adapter restart.
388
+ - **EcoFeedback is only requested while an appliance is actually running.** The DOP2 leaf
389
+ only answers while the appliance is awake - a switched-off machine returns HTTP 500. The
390
+ washing machine's last reading came in mid-programme; afterwards every poll ran into the
391
+ void, one per minute for days, each one occupying the XKM module that answers only one
392
+ request at a time. Polling now happens while a programme runs, during a ten-minute
393
+ follow-up afterwards (the final reading is not settled the moment the status flips), and
394
+ once at startup so that appliances without the leaf can still be identified. The follow-up
395
+ ends early once two consecutive readings are identical.
396
+ - **All object names are complete in eleven languages.** The repository check reported 147
397
+ W1001 warnings for `common.name`; channels, EcoFeedback data points, appliance names and the
398
+ instance objects were still English- or German-only. Appliance categories are translated
399
+ while model and serial number stay untouched - they are proper names.
400
+
401
+ ### 0.3.4
402
+ - **New: cycle history.** Every completed program is recorded with duration, program name,
403
+ energy and water. The appliances do not keep finished cycles themselves, so the history
404
+ starts when the feature is enabled - it cannot be filled retroactively. Recent cycles are
405
+ kept as JSON in `<serial>.history.cyclesJson`, alongside running totals for cycle count,
406
+ runtime, energy and water. Optionally each cycle is also written to the history adapter,
407
+ timestamped at the end of the cycle, so charts can cover any period.
408
+ Configurable on the new **History** tab: ring buffer size (default 50), retention in days
409
+ (default 730) and the history instance.
410
+ - The step-by-step login instructions were stored in English in nine of the eleven language
411
+ files. All eight texts are now translated into es, fr, it, nl, pl, pt, ru, uk and zh-cn.
412
+ - `common.news` no longer lists versions that were never published to npm.
413
+ - Dependabot: raised the PR limit, spread the schedule over a cron slot, added automerge.
414
+
155
415
  ### 0.3.3
156
416
  - Fix E3005: states declared as `number` no longer receive `null` when the appliance does not
157
417
  report a value - the datapoint keeps its default instead. `estimatedEndTime` is cleared with
@@ -200,7 +460,7 @@ Most of the above was contributed by [meistermopper](https://github.com/meisterm
200
460
 
201
461
  MIT License
202
462
 
203
- Copyright (c) 2026 Immanuel
463
+ Copyright (c) 2026 Immanuel <github@freitag.online>
204
464
 
205
465
  Permission is hereby granted, free of charge, to any person obtaining a copy of this
206
466
  software and associated documentation files (the "Software"), to deal in the Software