iobroker.e3oncan 0.6.14 → 0.6.15

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
@@ -102,6 +102,9 @@ Yes, that is possible under certain conditions:
102
102
  Placeholder for the next version (at the beginning of the line):
103
103
  ### **WORK IN PROGRESS**
104
104
  -->
105
+ ### 0.6.15 (2024-01-23)
106
+ * (MyHomeMyData) Fix for Utf8 codec for handling of special characters, e.g. umlauts
107
+
105
108
  ### 0.6.14 (2024-01-22)
106
109
  * (MyHomeMyData) Replace '.' by '_' in datapoint ids to avoid unwanted sub structure in data states
107
110
  * (MyHomeMyData) Added more informations about white list for writables in Readme.
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "e3oncan",
4
- "version": "0.6.14",
4
+ "version": "0.6.15",
5
5
  "news": {
6
+ "0.6.15": {
7
+ "en": "Fix for Utf8 codec for handling of special characters, e.g. umlauts",
8
+ "de": "Fix für Utf8-Codec zum Umgang mit Sonderzeichen, z.B. umlauts",
9
+ "ru": "Fix for Utf8 codec для обработки специальных символов, например, umlauts",
10
+ "pt": "Fix para Codec Utf8 para manipulação de caracteres especiais, por exemplo, umlauts",
11
+ "nl": "Fix voor Utf8 codec voor het hanteren van speciale tekens, bijvoorbeeld umlauts",
12
+ "fr": "Correction pour Codec Utf8 pour la manipulation de caractères spéciaux, p.ex. umlauts",
13
+ "it": "Fissaggio per Codice Utf8 per la gestione di caratteri speciali, ad esempio umlauts",
14
+ "es": "Corrección Utf8 codec para el manejo de caracteres especiales, por ejemplo umlauts",
15
+ "pl": "Napraw dla Kod Utf8 do obsługi znaków specjalnych, np. umlautów",
16
+ "uk": "Фіксація Utf8 кодека для обробки спеціальних символів, наприклад, umlauts",
17
+ "zh-cn": "修复 Utf8 处理特殊字符的编码器, 例如 umlauts"
18
+ },
6
19
  "0.6.14": {
7
20
  "en": "Replace '.' by '_' in datapoint ids to avoid unwanted sub structure in data states\nAdded more informations about white list for writables in Readme.\nRecognize loss of CAN connection.\nImproved handling of info.connection.",
8
21
  "de": "Ersetzen Sie '.' durch '_' in Datapoint ids, um unerwünschte Unterstruktur in Datenzuständen zu vermeiden\nWeitere Informationen zur Weißen Liste für schreibbare in Readme.\nErkennen Verlust der CAN-Verbindung.\nVerbesserte Handhabung von info.connection.",
@@ -80,19 +93,6 @@
80
93
  "pl": "Bugfix: Tylko Linux jest obsługiwany",
81
94
  "uk": "Помилки: Тільки Linux підтримується",
82
95
  "zh-cn": "错误修正 : 仅支持 Linux"
83
- },
84
- "0.6.8": {
85
- "en": "Initial npm version",
86
- "de": "Ursprüngliche npm-Version",
87
- "ru": "Первоначальная версия npm",
88
- "pt": "Versão inicial npm",
89
- "nl": "Eerste npm-versie",
90
- "fr": "Version initiale npm",
91
- "it": "Versione npm iniziale",
92
- "es": "Versión inicial npm",
93
- "pl": "Początkowa wersja npm",
94
- "uk": "Початкова версія npm",
95
- "zh-cn": "初始 npm 版本"
96
96
  }
97
97
  },
98
98
  "title": "Viessmann E3 CAN do",
package/lib/canUds.js CHANGED
@@ -365,17 +365,21 @@ class uds {
365
365
  switch (dcs.channel) {
366
366
  case 'json':
367
367
  // Change in json data
368
- byteArr = await this.storage.encodeDataCAN(ctx, this, did, JSON.parse(state.val));
369
- if (byteArr) {
370
- await this.pushCmnd(ctx, 'write', [[did,byteArr]]);
371
- setTimeout(function(ctxWorker,did){ctxWorker.cmndsQueue.push({'mode':'read', 'did': did});},2500,this,did); // Read value after 2500 ms
372
- } else {
373
- ctx.log.error('User command UDS WriteByDid on '+this.config.stateBase+': Encoding of data failed.');
368
+ try {
369
+ byteArr = await this.storage.encodeDataCAN(ctx, this, did, await JSON.parse(state.val));
370
+ if (byteArr) {
371
+ await this.pushCmnd(ctx, 'write', [[did,byteArr]]);
372
+ setTimeout(function(ctxWorker,did){ctxWorker.cmndsQueue.push({'mode':'read', 'did': did});},2500,this,did); // Read value after 2500 ms
373
+ } else {
374
+ ctx.log.error('User command UDS WriteByDid on '+this.config.stateBase+': Encoding of data failed.');
375
+ }
376
+ } catch(e) {
377
+ ctx.log.error('WriteByDid(): Encoding of data failed on '+this.config.stateBase+'.'+String(did)+'; err='+JSON.stringify(e));
374
378
  }
375
379
  break;
376
380
  case 'raw':
377
381
  // Change in raw data
378
- byteArr = this.storage.toByteArray(JSON.parse(state.val));
382
+ byteArr = this.storage.toByteArray(await JSON.parse(state.val));
379
383
  if (byteArr) {
380
384
  await this.pushCmnd(ctx, 'write', [[did,byteArr]]);
381
385
  setTimeout(function(ctxWorker,did){ctxWorker.cmndsQueue.push({'mode':'read', 'did': did});},2500,this,did); // Read value after 2500 ms
@@ -389,12 +393,16 @@ class uds {
389
393
  lenBaseId = ctx.namespace.length+dcs.device.length+dcs.channel.length+dcs.state.length+3;
390
394
  if (id.length == lenBaseId) {
391
395
  // Scalar value w/o sub structure
392
- byteArr = await this.storage.encodeDataCAN(ctx, this, did, JSON.parse(state.val));
393
- if (byteArr) {
394
- await this.pushCmnd(ctx, 'write', [[did,byteArr]]);
395
- setTimeout(function(ctxWorker,did){ctxWorker.cmndsQueue.push({'mode':'read', 'did': did});},2500,this,did); // Read value after 2500 ms
396
- } else {
397
- ctx.log.error('User command UDS WriteByDid on '+this.config.stateBase+': Encoding of data failed.');
396
+ try {
397
+ byteArr = await this.storage.encodeDataCAN(ctx, this, did, await JSON.parse(state.val));
398
+ if (byteArr) {
399
+ await this.pushCmnd(ctx, 'write', [[did,byteArr]]);
400
+ setTimeout(function(ctxWorker,did){ctxWorker.cmndsQueue.push({'mode':'read', 'did': did});},2500,this,did); // Read value after 2500 ms
401
+ } else {
402
+ ctx.log.error('User command UDS WriteByDid on '+this.config.stateBase+': Encoding of data failed.');
403
+ }
404
+ } catch(e) {
405
+ ctx.log.error('WriteByDid(): Encoding of data failed on '+this.config.stateBase+'.'+String(did)+'; err='+JSON.stringify(e));
398
406
  }
399
407
  break;
400
408
  }
@@ -402,21 +410,6 @@ class uds {
402
410
  // Build json object for complete object tree of changed did:
403
411
  await ctx.getStatesOf(dcs.device,dcs.device+'.'+dcs.channel,async function(err,obj) {
404
412
  // Get all states for changed device.channel
405
- /*
406
- // Probalby better solution based on numbers as marker for arrays. But not working yet.
407
- function isNumber(value) { return /^\d+$/.test(value); }
408
- function insertDictSubVal(dict, keyArr, isArray, val) {
409
- if (keyArr.length == 1) {
410
- const key = keyArr[0];
411
- if (isArray) dict[key].push(val); else dict[key] = val;
412
- } else {
413
- const key = keyArr.shift();
414
- const nextIsArray = (((keyArr.length) && (isNumber(keyArr[0]))) ? true : false);
415
- if (!(key in dict)) if (nextIsArray) dict[key] = []; else dict[key] = {};
416
- insertDictSubVal(dict[key],keyArr, nextIsArray, val);
417
- }
418
- }
419
- */
420
413
  function insertDictSubVal(dict, keyArr, val) {
421
414
  const listLabels = ['ListEntries','Schedules','TopologyElement'];
422
415
  if (keyArr.length == 1) {
@@ -437,12 +430,16 @@ class uds {
437
430
  insertDictSubVal(treeDict, label.split('.'), val);
438
431
  }
439
432
  }
440
- byteArr = await ctxWorker.storage.encodeDataCAN(ctx, ctxWorker, did, treeDict);
441
- if (byteArr) {
442
- await ctxWorker.pushCmnd(ctx, 'write', [[did,byteArr]]);
443
- setTimeout(function(ctxWorker,did){ctxWorker.cmndsQueue.push({'mode':'read', 'did': did});},2500,ctxWorker,did); // Read value after 2500 ms
444
- } else {
445
- ctx.log.error('User command UDS WriteByDid on '+ctxWorker.config.stateBase+': Encoding of data failed.');
433
+ try {
434
+ byteArr = await ctxWorker.storage.encodeDataCAN(ctx, ctxWorker, did, treeDict);
435
+ if (byteArr) {
436
+ await ctxWorker.pushCmnd(ctx, 'write', [[did,byteArr]]);
437
+ setTimeout(function(ctxWorker,did){ctxWorker.cmndsQueue.push({'mode':'read', 'did': did});},2500,ctxWorker,did); // Read value after 2500 ms
438
+ } else {
439
+ ctx.log.error('User command UDS WriteByDid on '+ctxWorker.config.stateBase+': Encoding of data failed.');
440
+ }
441
+ } catch(e) {
442
+ ctx.log.error('WriteByDid(): Encoding of data failed on '+ctxWorker.config.stateBase+'.'+String(did)+'; err='+JSON.stringify(e));
446
443
  }
447
444
  });
448
445
  break;
package/lib/codecs.js CHANGED
@@ -205,18 +205,28 @@ class O3EUtf8 {
205
205
  this.id = idStr;
206
206
  this.offset = _args.offset;
207
207
  }
208
+
208
209
  encode(data) {
209
- const encoder = new TextEncoder();
210
- const result = Object.values(encoder.encode(data));
211
- if (result.length > this.string_len) throw new Error('O3EUtf8.encode() result too long: '+this.id+' - '+String(result.length)+' > '+String(this.string_len));
210
+ let result;
211
+ try {
212
+ result = Object.values(new TextEncoder().encode(data));
213
+ } catch(e) {
214
+ throw new Error('O3EUtf8.encode(): Conversion from Utf8 failed '+this.id+'; err='+JSON.stringify(e));
215
+ }
216
+ if (result.length > this.string_len) throw new Error('O3EUtf8.encode(): Result too long: '+this.id+' - '+String(result.length)+' > '+String(this.string_len));
212
217
  return result.concat(Array.from(Array(this.string_len-result.length), () => 0));
213
218
 
214
219
  }
215
220
  decode(string_bin) {
216
- let mystr = '';
217
- for (let i=this.offset; i<this.offset+this.string_len;i++) { mystr += String.fromCharCode(string_bin[i]); }
218
- // eslint-disable-next-line no-control-regex
219
- return mystr.replace(/[\u0000-\u0008,\u000A-\u001F,\u007F-\u00A0]+/g, '');
221
+ let i = string_bin.slice(this.offset).indexOf(0);
222
+ if (i == -1) { i = string_bin.length-this.offset; }
223
+ let result;
224
+ try {
225
+ result = new TextDecoder().decode(new Uint8Array(string_bin.slice(this.offset,i)));
226
+ } catch(e) {
227
+ throw new Error('O3EUtf8.decode(): Conversion to Utf8 failed '+this.id+'; err='+JSON.stringify(e));
228
+ }
229
+ return result;
220
230
  }
221
231
 
222
232
  __len__() {
@@ -38,6 +38,62 @@ function codecEncode(descr, expected, codec, len, id, args, val) {
38
38
  });
39
39
  }
40
40
 
41
+ // Utf8:
42
+ describe('codecs.js => O3EUtf8()', () => {
43
+ const raw_hex1 = '4865697a6bc3b6727065720000000000000000000000000000000000000000000000000000000000';
44
+ const raw1 = E3.toByteArray(raw_hex1);
45
+ const txt1 = 'Heizkörper';
46
+ it(`Decode ${JSON.stringify(txt1)}`, () => {
47
+ const f = new E3.O3Ecodecs.O3EUtf8(raw1.length,'test1Utf08', {offset:0});
48
+ const result = f.decode(raw1);
49
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(txt1));
50
+ });
51
+ it(`Encode ${JSON.stringify(raw1)}`, () => {
52
+ const f = new E3.O3Ecodecs.O3EUtf8(raw1.length,'test2Utf08', {offset:0});
53
+ const result = f.encode(txt1);
54
+ expect(JSON.stringify(raw1)).to.equal(JSON.stringify(result));
55
+ });
56
+ const raw_hex2 = '4675c39f626f64656e6865697a756e67000000000000000000000000000000000000000000000000';
57
+ const raw2 = E3.toByteArray(raw_hex2);
58
+ const txt2 = 'Fußbodenheizung';
59
+ it(`Decode ${JSON.stringify(txt2)}`, () => {
60
+ const f = new E3.O3Ecodecs.O3EUtf8(raw2.length,'test3Utf08', {offset:0});
61
+ const result = f.decode(raw2);
62
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(txt2));
63
+ });
64
+ it(`Encode ${JSON.stringify(raw2)}`, () => {
65
+ const f = new E3.O3Ecodecs.O3EUtf8(raw2.length,'test4Utf08', {offset:0});
66
+ const result = f.encode(txt2);
67
+ expect(JSON.stringify(raw2)).to.equal(JSON.stringify(result));
68
+ });
69
+ const raw_hex3 = '00000000000000000000000000000000000000000000000000000000000000000000000000000000';
70
+ const raw3 = E3.toByteArray(raw_hex3);
71
+ const txt3 = '';
72
+ it(`Decode ${JSON.stringify(txt3)}`, () => {
73
+ const f = new E3.O3Ecodecs.O3EUtf8(raw3.length,'test5Utf08', {offset:0});
74
+ const result = f.decode(raw3);
75
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(txt3));
76
+ });
77
+ it(`Encode ${JSON.stringify(raw3)}`, () => {
78
+ const f = new E3.O3Ecodecs.O3EUtf8(raw3.length,'test5Utf08', {offset:0});
79
+ const result = f.encode(txt3);
80
+ expect(JSON.stringify(raw3)).to.equal(JSON.stringify(result));
81
+ });
82
+ const raw_hex4 = '4675c39f';
83
+ const raw4 = E3.toByteArray(raw_hex4);
84
+ const txt4 = 'Fuß';
85
+ it(`Decode ${JSON.stringify(txt4)}`, () => {
86
+ const f = new E3.O3Ecodecs.O3EUtf8(raw4.length,'test6Utf08', {offset:0});
87
+ const result = f.decode(raw4);
88
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(txt4));
89
+ });
90
+ it(`Encode ${JSON.stringify(raw4)}`, () => {
91
+ const f = new E3.O3Ecodecs.O3EUtf8(raw4.length,'test6Utf08', {offset:0});
92
+ const result = f.encode(txt4);
93
+ expect(JSON.stringify(raw4)).to.equal(JSON.stringify(result));
94
+ });
95
+ });
96
+
41
97
  // val2byteArray:
42
98
  describe('codecs.js => val2byteArr()', () => {
43
99
  let val = -1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.e3oncan",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "description": "Collect data on CAN bus for Viessmann E3 devices, e.g. Vitocal, Vitocharge, Energy Meter E380",
5
5
  "author": {
6
6
  "name": "Juergen",