iobroker.idm-multitalent_002 2.2.3 → 2.2.4

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.
package/README.md CHANGED
@@ -76,6 +76,10 @@ Example screenshots of objects:
76
76
  ![Status](resources/ioBrokerAdapter-Status.jpg)
77
77
 
78
78
  ## Changelog
79
+ ### 2.2.4 (2026-10-10)
80
+ * (zloe) fixed the same "marked read-only by mistake" bug for `Waermepumpe.Maximaltemperatur` on EVR752101 (found by cross-checking all six firmware definitions against each other, same pattern as the Heizkreis B fix below)
81
+ * (zloe) added a permanent automated test (`lib/idm_datablocks.test.js`) that cross-checks every bundled firmware definition against the others - same statename + register ("function") id must agree on `writable`/`length`/`factor` - so this class of bug is caught by CI from now on instead of needing a one-off manual check. A handful of genuine, still-unconfirmed differences (`Waermepumpe.MinimaleLaufzeit` writable, and three `length` mismatches around `Heizkreis-A.Absenkfaktor`/`Heizung.PufferVorhanden`/`Heizung.SchichttrennplatteVorhanden`) are documented as known exceptions in that test rather than guessed at - see the test file for why
82
+
79
83
  ### 2.2.3 (2026-10-10)
80
84
  * (zloe) fixed: "Heizkreis B" operating mode (Betriebsart) could not be changed on the S_H726100 and idm750100 control versions - it was incorrectly marked read-only in the data block definition (`writable: false` instead of `true`), while reading always worked fine. Found by cross-checking all six firmware definitions against each other: every other control version already had `function: 19` (same register, same min/max) correctly set to `writable: true`
81
85
 
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "idm-multitalent_002",
4
- "version": "2.2.3",
4
+ "version": "2.2.4",
5
5
  "news": {
6
+ "2.2.4": {
7
+ "en": "fixed the same \"marked read-only by mistake\" bug for \"Waermepumpe.Maximaltemperatur\" on EVR752101 (found by cross-checking all six firmware definitions against each other, same pattern as the Heizkreis B fix in 2.2.3); added a permanent automated test that cross-checks every bundled firmware definition against the others so this class of bug gets caught by CI from now on, instead of needing a one-off manual check",
8
+ "de": "denselben \"versehentlich nur lesbar markiert\"-Fehler auch für \"Waermepumpe.Maximaltemperatur\" bei EVR752101 behoben (gefunden durch Quervergleich aller sechs Firmware-Definitionen, gleiches Muster wie der Heizkreis-B-Fix in 2.2.3); ein dauerhafter automatisierter Test hinzugefügt, der alle mitgelieferten Firmware-Definitionen gegeneinander prüft, damit diese Fehlerklasse ab sofort von der CI erkannt wird statt eine manuelle Einzelprüfung zu brauchen",
9
+ "ru": "исправлена та же ошибка \"случайно помечено только для чтения\" для \"Waermepumpe.Maximaltemperatur\" на EVR752101 (найдена путём сверки всех шести определений прошивок друг с другом, та же схема, что и исправление Heizkreis B в 2.2.3); добавлен постоянный автоматический тест, сверяющий все встроенные определения прошивок друг с другом, чтобы такие ошибки теперь выявлялись CI",
10
+ "pt": "corrigido o mesmo erro de \"marcado como somente leitura por engano\" para \"Waermepumpe.Maximaltemperatur\" no EVR752101 (encontrado ao comparar todas as seis definições de firmware entre si, mesmo padrão da correção do Heizkreis B na 2.2.3); adicionado um teste automatizado permanente que compara todas as definições de firmware incluídas entre si, para que esse tipo de erro seja detectado pela CI a partir de agora",
11
+ "nl": "dezelfde \"per ongeluk alleen-lezen\"-fout ook opgelost voor \"Waermepumpe.Maximaltemperatur\" op EVR752101 (gevonden door alle zes firmware-definities onderling te vergelijken, zelfde patroon als de Heizkreis B-fix in 2.2.3); een permanente geautomatiseerde test toegevoegd die alle meegeleverde firmware-definities onderling controleert, zodat dit type fout voortaan door CI wordt opgemerkt",
12
+ "fr": "correction du même bug \"marqué par erreur en lecture seule\" pour \"Waermepumpe.Maximaltemperatur\" sur EVR752101 (trouvé en comparant les six définitions de firmware entre elles, même schéma que le correctif Heizkreis B de la 2.2.3) ; ajout d'un test automatisé permanent qui compare toutes les définitions de firmware fournies entre elles, afin que ce type de bug soit désormais détecté par la CI",
13
+ "it": "corretto lo stesso bug \"erroneamente contrassegnato come sola lettura\" per \"Waermepumpe.Maximaltemperatur\" su EVR752101 (trovato confrontando tutte le sei definizioni firmware tra loro, stesso schema della correzione Heizkreis B nella 2.2.3); aggiunto un test automatico permanente che confronta tutte le definizioni firmware incluse tra loro, così questo tipo di bug viene ora rilevato dalla CI",
14
+ "es": "corregido el mismo error de \"marcado como solo lectura por error\" para \"Waermepumpe.Maximaltemperatur\" en EVR752101 (encontrado al comparar las seis definiciones de firmware entre sí, mismo patrón que la corrección de Heizkreis B en 2.2.3); se añadió una prueba automatizada permanente que compara todas las definiciones de firmware incluidas entre sí, para que este tipo de error ahora lo detecte la CI",
15
+ "pl": "naprawiono ten sam błąd \"omyłkowo oznaczone jako tylko do odczytu\" dla \"Waermepumpe.Maximaltemperatur\" w EVR752101 (znaleziony przez wzajemne porównanie wszystkich sześciu definicji firmware, ten sam wzorzec co poprawka Heizkreis B w 2.2.3); dodano stały zautomatyzowany test porównujący wszystkie dołączone definicje firmware ze sobą, aby ten rodzaj błędu był odtąd wykrywany przez CI",
16
+ "zh-cn": "修复了 EVR752101 上 \"Waermepumpe.Maximaltemperatur\" 同样的\"误标记为只读\"问题(通过交叉核对全部六个固件定义发现,与 2.2.3 中 Heizkreis B 的修复模式相同);新增了一个永久性自动化测试,用于交叉核对所有内置固件定义,从此由 CI 自动捕获此类错误",
17
+ "uk": "виправлено ту саму помилку \"помилково позначено лише для читання\" для \"Waermepumpe.Maximaltemperatur\" на EVR752101 (знайдено шляхом звірки всіх шести визначень прошивок одне з одним, той самий шаблон, що й виправлення Heizkreis B у 2.2.3); додано постійний автоматизований тест, який звіряє всі вбудовані визначення прошивок одне з одним, щоб цей клас помилок відтепер виявляла CI"
18
+ },
6
19
  "2.2.3": {
7
20
  "en": "fixed: \"Heizkreis B\" (heating circuit B) operating mode (Betriebsart) could not be changed on the S_H726100 and idm750100 control versions - it was incorrectly marked as read-only in the data block definition (writable:false instead of true), while reading it always worked fine. All other known control versions already had this correct; the field now matches circuit A/C/D on every firmware",
8
21
  "de": "behoben: Die Betriebsart von \"Heizkreis B\" ließ sich bei den Steuerungsversionen S_H726100 und idm750100 nicht ändern - sie war in der Datenblock-Definition fälschlich als nur lesbar markiert (writable:false statt true), Lesen funktionierte dabei immer einwandfrei. Bei allen anderen bekannten Steuerungsversionen war das bereits korrekt; das Feld verhält sich jetzt überall wie Heizkreis A/C/D",
@@ -824,7 +824,7 @@
824
824
  "description": "WP Maximaltemp.",
825
825
  "length": 2,
826
826
  "factor": 1,
827
- "writable": false,
827
+ "writable": true,
828
828
  "function": 37
829
829
  },
830
830
  {
@@ -52,6 +52,99 @@ describe('idm_datablocks (data blocks loader)', () => {
52
52
  });
53
53
  });
54
54
 
55
+ // Catches transcription bugs like the one fixed in 2.2.3 (and again in 2.2.4): a field
56
+ // was accidentally copy-pasted with the wrong "writable" flag into exactly one of the six
57
+ // bundled files, so "Heizkreis B" could be read but never changed on that control version.
58
+ // A field is the "same" field across firmware files when it shares both a statename AND a
59
+ // function (register) id there - the protocol has no other way to say "this register is
60
+ // the same thing on every control version". writable/length/factor MUST then match: they
61
+ // describe how the register is framed on the wire and whether writes are even attempted,
62
+ // none of which plausibly differs by firmware revision for the same physical register.
63
+ // min/max are treated more leniently: today only S_H726100 (and partially EVR752101) has
64
+ // hand-verified limits (see README - "neither source documents official min/max limits"),
65
+ // so one side being undocumented (undefined) while the other has a real value is expected,
66
+ // not a bug. Two DIFFERENT non-undefined bounds for the same register would still fail.
67
+ describe('cross-firmware consistency of the bundled definitions', () => {
68
+ // Each entry: a (statename, function) pair found to genuinely differ across firmware
69
+ // files as of 2026-10, left alone rather than "fixed" because changing length/factor
70
+ // risks silently breaking a wire format nobody has reverse-engineered a second time,
71
+ // and because S_H726100 is the one control version with hand-verified field lengths
72
+ // (confirmed live via the wireLength-learning mechanism) - so when it disagrees with
73
+ // the other five, it is at least as likely that THEY are the ones mistranscribed, not
74
+ // S_H726100. Needs confirming against source documentation (makki's KNX-forum data)
75
+ // or a live capture on the affected hardware before touching either side.
76
+ const KNOWN_UNRESOLVED_DIFFERENCES = new Set([
77
+ 'Waermepumpe.MinimaleLaufzeit/38', // writable: true on idm712100/idm722100, false elsewhere
78
+ 'Heizkreis-A.Absenkfaktor/50', // length: 1 on S_H726100, 2 everywhere else
79
+ 'Heizung.PufferVorhanden/56', // length: 2 on S_H726100+idm701100, 1 elsewhere
80
+ 'Heizung.SchichttrennplatteVorhanden/55', // length: 2 on S_H726100+idm701100, 1 elsewhere
81
+ ]);
82
+
83
+ it('keeps writable/length/factor identical for the same (statename, function) across every firmware', () => {
84
+ /** @type {Map<string, {file: string, writable: boolean, length: number, factor: number, min?: number, max?: number}[]>} */
85
+ const byKey = new Map();
86
+ for (const { file, data } of idm_datablocks.readVersionFilesFrom(idm_datablocks.BUNDLED_DIR)) {
87
+ for (const block of data.data_blocks) {
88
+ for (const field of block.definition) {
89
+ if (!field.statename || field.function === -1) continue; // padding/unidentified
90
+ const key = field.statename + '/' + field.function;
91
+ let list = byKey.get(key);
92
+ if (!list) {
93
+ list = [];
94
+ byKey.set(key, list);
95
+ }
96
+ list.push({ file, writable: field.writable, length: field.length, factor: field.factor, min: field.min, max: field.max });
97
+ }
98
+ }
99
+ }
100
+
101
+ const problems = [];
102
+ for (const [key, entries] of byKey) {
103
+ if (entries.length < 2 || KNOWN_UNRESOLVED_DIFFERENCES.has(key)) continue;
104
+
105
+ for (const attr of ['writable', 'length', 'factor']) {
106
+ const values = new Set(entries.map(e => e[attr]));
107
+ if (values.size > 1) {
108
+ problems.push(key + ': ' + attr + ' differs - ' + entries.map(e => e.file.split('/').pop() + '=' + e[attr]).join(', '));
109
+ }
110
+ }
111
+ for (const attr of ['min', 'max']) {
112
+ const defined = entries.map(e => e[attr]).filter(v => v !== undefined);
113
+ if (new Set(defined).size > 1) {
114
+ problems.push(key + ': ' + attr + ' disagrees (not just undocumented) - ' + entries.map(e => e.file.split('/').pop() + '=' + e[attr]).join(', '));
115
+ }
116
+ }
117
+ }
118
+
119
+ expect(problems, 'found inconsistent field definitions for what should be the same register - see KNOWN_UNRESOLVED_DIFFERENCES above if this is an intentional, confirmed difference').to.deep.equal([]);
120
+ });
121
+
122
+ it('fails loudly if a KNOWN_UNRESOLVED_DIFFERENCES entry stops being a real difference (so it gets removed, not forgotten)', () => {
123
+ /** @type {Map<string, Set<string>>} */
124
+ const byKey = new Map();
125
+ for (const { data } of idm_datablocks.readVersionFilesFrom(idm_datablocks.BUNDLED_DIR)) {
126
+ for (const block of data.data_blocks) {
127
+ for (const field of block.definition) {
128
+ if (!field.statename || field.function === -1) continue;
129
+ const key = field.statename + '/' + field.function;
130
+ if (!KNOWN_UNRESOLVED_DIFFERENCES.has(key)) continue;
131
+ let seen = byKey.get(key);
132
+ if (!seen) {
133
+ seen = new Set();
134
+ byKey.set(key, seen);
135
+ }
136
+ seen.add(JSON.stringify({ writable: field.writable, length: field.length, factor: field.factor }));
137
+ }
138
+ }
139
+ }
140
+ for (const key of KNOWN_UNRESOLVED_DIFFERENCES) {
141
+ const seen = byKey.get(key);
142
+ expect(seen, key + ' is listed as unresolved but no longer appears in the bundled files - remove it from the list').to.not.be.undefined;
143
+ expect(seen ? seen.size : 0, key + ' is listed as unresolved but all bundled files now agree - remove it from the list').to.be.greaterThan(1);
144
+ }
145
+ });
146
+ });
147
+
55
148
  describe('validateVersionFile', () => {
56
149
  it('rejects a non-object root', () => {
57
150
  expect(idm_datablocks.validateVersionFile(null)).to.not.be.empty;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.idm-multitalent_002",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
4
4
  "description": "Read and write values of a iDM heatpump with multitalent.002 control.",
5
5
  "author": {
6
6
  "name": "zloe",