iobroker.e3oncan 0.6.11 → 0.6.13

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
@@ -51,7 +51,7 @@ This step is not mandatory but strongly recomended. If you would like to write t
51
51
  * Last step is to configure schedules for collecting data on tab "ASSIGNMENTS TO EXTERNAL CAN ADAPTER".
52
52
  * For **energy meter E380** (if available in your setup) you just can activate or not. Please notice the value "Min. update time (s)". Updates to single datapoints are done no faster than the given value (default is 5 seconds). By choosing zero every received data will be stored. Since E380 is sending data very fast (more than 20 values per second), it's recommended not to use zero here. This would put a high load on the ioBroker system.
53
53
  * If you have connected E3 devices via CAN bus, e.g. Vitocal and VX3, you can collect data exchanged between those devices in realtime by listening. Press "+" to add a line, check "active" chackbox, select a device and edit "Min. update time (s)". It's feasable to use 0s here, however, I recommend to keep to the 5s.
54
- * Finally, you may add schedules for requesting data via UDSonCAN protocol. Again press "+" button and edit the setting. You may have several schedules with different (!) timings. By this you can request some datapoints more often than others. Default value of 0 for "Schedule (s)" means, those datapoints will be requested just once during startup of the instance.
54
+ * Finally, you may add schedules for requesting data via UDSonCAN protocol. Again press "+" button and edit the settings. You may have several schedules on each device. By this you can request some datapoints more often than others. Default value of 0 for "Schedule (s)" means, those datapoints will be requested just once during startup of the instance.
55
55
  You may use datapoints informations on tab "LIST OF DATAPOINTS" for reference (opening on 2nd tab could help).
56
56
  * If you have configured a CAN adapter connected to the **internal CAN bus**, a tab "ASSIGNMENTS TO INTERNAL CAN ADAPTER" is visible. Please configure the devices for colletion there. UDSonCAN is not supported on internal CAN bus by E3 devices.
57
57
  * That's it. Press "SAVE & CLOSE" button and check the data collected in object tree.
@@ -81,7 +81,7 @@ You may use datapoints informations on tab "LIST OF DATAPOINTS" for reference (o
81
81
  * Therfore from my point of view, combination of both methods is best approach.
82
82
 
83
83
  ## Limitation of collecting data
84
- * At present, the communication protocol is known only for Vitocal (listener on CAN id 0x693) and Vitocharge VX3 (listener on CAN id 0x451).
84
+ * At present, the communication protocol is known only for Vitocal (listener on CAN id 0x693), Vitocharge VX3 and Vitoair (both listener on CAN id 0x451).
85
85
 
86
86
  ## What is different to open3e project?
87
87
  * Obviously, the main differece is the direct integration to ioBroker. Configuration can be done via dialogs, data get's directly listed in object trees.
@@ -98,6 +98,16 @@ Yes, that is possible under certain conditions:
98
98
  Placeholder for the next version (at the beginning of the line):
99
99
  ### **WORK IN PROGRESS**
100
100
  -->
101
+ ### 0.6.13 (2024-01-20)
102
+ * (MyHomeMyData) Now supports multiple definitions of same schedule on a device
103
+ * (MyHomeMyData) Added unit test cases for codecs
104
+
105
+ ### 0.6.12 (2024-01-19)
106
+ * (MyHomeMyData) Added datapoints to list writable dids
107
+ * (MyHomeMyData) Added unit test cases for codecs
108
+ * (MyHomeMyData) Improved speed of codes for numerical values
109
+ * (MyHomeMyData) Improved error messages on UDS negative response
110
+
101
111
  ### 0.6.11 (2024-01-17)
102
112
  * (MyHomeMyData) Improved layout of configuration dialog for device scan
103
113
 
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "e3oncan",
4
- "version": "0.6.11",
4
+ "version": "0.6.13",
5
5
  "news": {
6
+ "0.6.13": {
7
+ "en": "Now supports multiple definitions of same schedule on a device \nAdded unit test cases for codecs",
8
+ "de": "Jetzt unterstützt mehrere Definitionen desselben Zeitplans auf einem Gerät\nHinzugefügte Einheit Testfälle für Codecs",
9
+ "ru": "Теперь поддерживает несколько определений одного и того же расписания на устройстве\n4.1.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.1.2.1.2.1.1.2.1.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.2.2.1.1.1.1.1.2.1.1.1.1.1.1.1.2.2.2.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.2.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1",
10
+ "pt": "Agora suporta várias definições da mesma programação em um dispositivo\nAdicionados casos de teste de unidade para codecs",
11
+ "nl": "Nu ondersteunt meerdere definities van hetzelfde schema op een apparaat\nToegevoegde testcases per eenheid voor codecs",
12
+ "fr": "Maintenant prend en charge plusieurs définitions de même horaire sur un appareil\nAjout de cas de test unitaire pour codecs",
13
+ "it": "Ora supporta più definizioni dello stesso programma su un dispositivo\nAggiunti casi di prova unità per codecs",
14
+ "es": "Ahora soporta múltiples definiciones del mismo horario en un dispositivo\nCasos de prueba de unidad adicionales para codecs",
15
+ "pl": "Teraz obsługuje wiele definicji tego samego harmonogramu na urządzeniu\nDodano jednostkowe przypadki badań kodeków",
16
+ "uk": "Тепер підтримує декілька визначень одного графіка на пристрої\nДодані випадки тестування блоку для кодеків",
17
+ "zh-cn": "现在支持设备上同一时刻表的多个定义\n为解码器添加单位测试例"
18
+ },
19
+ "0.6.12": {
20
+ "en": "Added datapoints to list writable dids\nAdded unit test cases for codecs\nImproved speed of codes for numerical values\nImproved error messages on UDS negative response",
21
+ "de": "Datenpunkte zur Liste der schreibbaren Dossiers hinzugefügt\nHinzugefügte Einheit Testfälle für Codecs\nVerbesserte Geschwindigkeit von Codes für Zahlenwerte\nVerbesserte Fehlermeldungen auf UDS negative Antwort",
22
+ "ru": "Добавлены точки данных в список значимых дел\n4.1.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.2.1.1.2.1.2.1.1.2.1.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.2.2.1.1.1.1.1.2.1.1.1.1.1.1.1.2.2.2.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.2.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1\nПовышение скорости кодов для численных значений\nУлучшенные сообщения об ошибках в негативном ответе на СОД",
23
+ "pt": "Adicionado datapoints para lista writable dids\nAdicionados casos de teste de unidade para codecs\nVelocidade melhorada de códigos para valores numéricos\nMensagens de erro melhoradas na resposta negativa do UDS",
24
+ "nl": "Toegevoegd datapunten aan lijst schrijfbare does\nToegevoegde testcases per eenheid voor codecs\nVerbeterde snelheid van codes voor numerieke waarden\nVerbeterde foutmeldingen op UDS-negatieve reactie",
25
+ "fr": "Ajout de points de données pour lister les dids en écriture\nAjout de cas de test unitaire pour codecs\nAmélioration de la vitesse des codes pour les valeurs numériques\nMessages d'erreur améliorés sur la réponse négative UDS",
26
+ "it": "Aggiunti i datapoints per elencare i fatti scrivibili\nAggiunti casi di prova unità per codecs\nVelocità migliorata dei codici per i valori numerici\nMessaggi di errore migliorati sulla risposta negativa UDS",
27
+ "es": "Puntos de datos añadidos a la lista de hechos\nCasos de prueba de unidad adicionales para codecs\nMejora de la velocidad de los códigos para los valores numéricos\nMensajes de error mejorados en respuesta negativa UDS",
28
+ "pl": "Dodano punkty danych do listy zapisanych didów\nDodano jednostkowe przypadki badań kodeków\nZwiększona prędkość kodów dla wartości numerycznych\nUlepszone komunikaty błędów w odpowiedzi negatywnej UDS",
29
+ "uk": "Додано точки даних для списання збитих даних\nДодані випадки тестування блоку для кодеків\nПокращена швидкість кодів для чисельних значень\nПокращуйте повідомлення про помилки у UDS",
30
+ "zh-cn": "添加数据点到列表可写作的\n为解码器添加单位测试例\n数字值代码速度提高\nUDS 负面响应上改进错误信息"
31
+ },
6
32
  "0.6.11": {
7
33
  "en": "Improved layout of configuration dialog for device scan",
8
34
  "de": "Verbessertes Layout des Konfigurationsdialogs für den Gerätescan",
package/lib/canUds.js CHANGED
@@ -1,11 +1,11 @@
1
1
  const storage = require('./storage');
2
2
 
3
3
  class scheduleLoop {
4
- constructor(ctxGlobal, ctxLocal, schedule, dids) {
4
+ constructor(ctxGlobal, ctxLocal, schedule) {
5
5
  this.ctxGlobal = ctxGlobal;
6
6
  this.ctxLocal = ctxLocal;
7
7
  this.schedule = schedule;
8
- this.dids = dids;
8
+ this.dids = [];
9
9
  this.schedHandle = null;
10
10
  }
11
11
 
@@ -30,6 +30,11 @@ class scheduleLoop {
30
30
  }
31
31
  }
32
32
 
33
+ async addDids(dids) {
34
+ const didsArr = dids.replace(' ','').split(',');
35
+ this.dids = this.dids.concat(didsArr.map(function(str) { return parseInt(str); }));
36
+ }
37
+
33
38
  async loop() {
34
39
  await this.ctxGlobal.log.silly('UDS schedule: '+String(this.schedule)+' '+this.ctxLocal.canIDhex+'.'+JSON.stringify(this.dids));
35
40
  await this.ctxLocal.pushCmnd(this.ctxGlobal, 'read', this.dids);
@@ -244,13 +249,13 @@ class uds {
244
249
  }
245
250
 
246
251
  async addSchedule(ctx, schedule, dids) {
247
- if (!Object.keys(this.schedules).includes(schedule)) {
248
- const didsArr = dids.replace(' ','').split(',');
249
- this.schedules[schedule] = new scheduleLoop(ctx, this, schedule, didsArr.map(function(str) { return parseInt(str); }));
250
- ctx.log.silly('UDS worker on '+this.config.stateBase+': Added schedule '+String(schedule)+'s '+JSON.stringify(didsArr));
251
- } else {
252
- ctx.log.warn('UDS worker warning on '+this.config.stateBase+': Multiple definiton of schedule:'+String(schedule)+'s');
252
+ if (!Object.keys(this.schedules).includes(String(schedule))) {
253
+ // New schedule
254
+ this.schedules[schedule] = new scheduleLoop(ctx, this, schedule);
255
+ await ctx.log.silly('UDS worker on '+this.config.stateBase+': Added schedule '+String(schedule)+'s.');
253
256
  }
257
+ await this.schedules[schedule].addDids(dids);
258
+ await ctx.log.silly('UDS worker on '+this.config.stateBase+': Added dids to schedule '+String(schedule)+'s '+JSON.stringify(this.schedules[schedule].dids));
254
259
  }
255
260
 
256
261
  async pushCmnd(ctx, mode, dids) {
@@ -536,7 +541,7 @@ class uds {
536
541
  if (this.callback) {
537
542
  this.callback(ctx, this, ['negative response', {'did':this.data.did,'didInfo':{'id':'','len':0},'val':''}]);
538
543
  } else {
539
- ctx.log.error('UDS worker error on '+this.config.stateBase+': Negative response on device '+this.canIDhex+'. Code=0x'+Number(candata[3]).toString(16));
544
+ ctx.log.error('UDS worker error on '+this.config.stateBase+': Negative response reading did '+String(this.data.did)+'. Code=0x'+Number(candata[3]).toString(16));
540
545
  }
541
546
  await this.setDidDone(0);
542
547
  break;
@@ -634,7 +639,7 @@ class uds {
634
639
  if ( (candata[0] == 0x03) && (candata[1] == 0x7F) && (candata[2] == this.writeByDidProt.SIDtx) ) {
635
640
  // Negative response
636
641
  this.stat.cntCommNR += 1;
637
- ctx.log.error('UDS worker error on '+this.config.stateBase+': Negative response on device '+this.canIDhex+'. Code=0x'+Number(candata[3]).toString(16));
642
+ ctx.log.error('UDS worker error on '+this.config.stateBase+': Negative response writing did '+String(this.data.did)+'. Code=0x'+Number(candata[3]).toString(16));
638
643
  await this.setDidDone(0);
639
644
  break;
640
645
  }
@@ -669,7 +674,7 @@ class uds {
669
674
  if ( (candata[0] == 0x03) && (candata[1] == 0x7F) && (candata[2] == this.writeByDidProt.SIDtx) ) {
670
675
  // Negative response
671
676
  this.stat.cntCommNR += 1;
672
- ctx.log.error('UDS worker error on '+this.config.stateBase+': Negative response on device '+this.canIDhex+'. Code=0x'+Number(candata[3]).toString(16));
677
+ ctx.log.error('UDS worker error on '+this.config.stateBase+': Negative response writing did '+String(this.data.did)+'. Code=0x'+Number(candata[3]).toString(16));
673
678
  await this.setDidDone(0);
674
679
  break;
675
680
  }
package/lib/codecs.js CHANGED
@@ -1,6 +1,13 @@
1
1
 
2
2
  const enums = require('./enums');
3
3
 
4
+ const C2POW08 = 0x100;
5
+ const C2POW15 = 0x08000;
6
+ const C2POW16 = 0x10000;
7
+ const C2POW24 = 0x1000000;
8
+ const C2POW31 = 0x080000000;
9
+ const C2POW32 = 0x100000000;
10
+
4
11
  function arr2Hex(arr) {
5
12
  // Convert byte array to hex string
6
13
  let hs = '';
@@ -31,22 +38,22 @@ function sint08toVal(j, ofs=0) {
31
38
  }
32
39
 
33
40
  function uint16toVal(j, ofs=0) {
34
- return Math.pow(2,8)*j[ofs+1]+j[ofs];
41
+ return C2POW08*j[ofs+1]+j[ofs];
35
42
  }
36
43
 
37
44
  function sint16toVal(j, ofs=0) {
38
- let v = Math.pow(2,8)*j[ofs+1]+j[ofs];
39
- if (v >= Math.pow(2,15)) { v -= Math.pow(2,16); }
45
+ let v = C2POW08*j[ofs+1]+j[ofs];
46
+ if (v >= C2POW15) { v -= C2POW16; }
40
47
  return v;
41
48
  }
42
49
 
43
50
  function uint32toVal(j, ofs=0) {
44
- return Math.pow(2,24)*j[ofs+3]+Math.pow(2,16)*j[ofs+2]+Math.pow(2,8)*j[ofs+1]+j[ofs];
51
+ return C2POW24*j[ofs+3]+C2POW16*j[ofs+2]+C2POW08*j[ofs+1]+j[ofs];
45
52
  }
46
53
 
47
54
  function sint32toVal(j, ofs=0) {
48
- let v = Math.pow(2,24)*j[ofs+3]+Math.pow(2,16)*j[ofs+2]+Math.pow(2,8)*j[ofs+1]+j[ofs];
49
- if (v >= Math.pow(2,31)) { v -= Math.pow(2,32); }
55
+ let v = C2POW24*j[ofs+3]+C2POW16*j[ofs+2]+C2POW08*j[ofs+1]+j[ofs];
56
+ if (v >= C2POW31) { v -= C2POW32; }
50
57
  return v;
51
58
  }
52
59
 
@@ -67,7 +74,7 @@ function int2val(j, ofs = 0, signed = false) {
67
74
  function val2byteArr(v, byte_width, scale = 1, signed = false) {
68
75
  // Convert int08, int16, int32 to byte array. Signed or unsigned.
69
76
  let val = Math.round(eval(v) * scale);
70
- if ( (signed) && (val < 0) ) { val += Math.pow(2,8*byte_width); }
77
+ if ( (signed) && (val < 0) ) { val += (2**(8*byte_width)); }
71
78
  const string_bin = toByteArray(toHex(val, byte_width*2));
72
79
  return string_bin.reverse();
73
80
  }
@@ -586,5 +593,6 @@ const O3Ecodecs = {
586
593
  module.exports = {
587
594
  O3Ecodecs,
588
595
  arr2Hex,
589
- toByteArray
596
+ toByteArray,
597
+ val2byteArr
590
598
  };
@@ -0,0 +1,222 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Tests for codecs
5
+ *
6
+ * It's automatically excluded from npm and its build output is excluded from both git and npm.
7
+ * It is advised to test all your modules with accompanying *.test.js-files
8
+ */
9
+
10
+ // tslint:disable:no-unused-expression
11
+
12
+ const { expect } = require('chai');
13
+ const E3 = require('./codecs');
14
+ const E3DidsDict = require('./didsE3.json');
15
+
16
+ function toByteArray(hs) {
17
+ // Convert hex string, e.g. '21A8' to byte array: [33,168]
18
+ const ba = [];
19
+ for (let i=0; i<hs.length/2; i++) {
20
+ ba.push(parseInt(hs.slice(2*i,2*i+2), 16));
21
+ }
22
+ return ba;
23
+ }
24
+
25
+ function codecDecode(descr, expected, codec, len, id, args, val) {
26
+ it(`${descr} ${JSON.stringify(expected)}`, () => {
27
+ const f = new codec(len,id,args);
28
+ const result = f.decode(val);
29
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
30
+ });
31
+ }
32
+
33
+ function codecEncode(descr, expected, codec, len, id, args, val) {
34
+ it(`${descr} ${JSON.stringify(expected)}`, () => {
35
+ const f = new codec(len,id,args);
36
+ const result = f.encode(val);
37
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
38
+ });
39
+ }
40
+
41
+ // val2byteArray:
42
+ describe('codecs.js => val2byteArr()', () => {
43
+ let val = -1;
44
+ let expected = [0xff];
45
+ it(`sint08 ${JSON.stringify(expected)}`, () => {
46
+ const result = E3.val2byteArr(val, 4, 1, true);
47
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
48
+ });
49
+ val = 2**7-1;
50
+ expected = [0x7f];
51
+ it(`sint08 ${JSON.stringify(expected)}`, () => {
52
+ const result = E3.val2byteArr(val, 4, 1, true);
53
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
54
+ });
55
+ val = -(2**7);
56
+ expected = [0x80];
57
+ it(`sint08 ${JSON.stringify(expected)}`, () => {
58
+ const result = E3.val2byteArr(val, 4, 1, true);
59
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
60
+ });
61
+ val = 2**8-1;
62
+ expected = [0xff];
63
+ it(`uint08 ${JSON.stringify(expected)}`, () => {
64
+ const result = E3.val2byteArr(val, 4, 1, false);
65
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
66
+ });
67
+
68
+ val = -1;
69
+ expected = [0xff,0xff];
70
+ it(`sint16 ${JSON.stringify(expected)}`, () => {
71
+ const result = E3.val2byteArr(val, 4, 1, true);
72
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
73
+ });
74
+ val = 2**15-1;
75
+ expected = [0xff,0x7f];
76
+ it(`sint16 ${JSON.stringify(expected)}`, () => {
77
+ const result = E3.val2byteArr(val, 4, 1, true);
78
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
79
+ });
80
+ val = -(2**15);
81
+ expected = [0x00,0x80];
82
+ it(`sint16 ${JSON.stringify(expected)}`, () => {
83
+ const result = E3.val2byteArr(val, 4, 1, true);
84
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
85
+ });
86
+ val = 2**16-1;
87
+ expected = [0xff,0xff];
88
+ it(`uint16 ${JSON.stringify(expected)}`, () => {
89
+ const result = E3.val2byteArr(val, 4, 1, false);
90
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
91
+ });
92
+
93
+ val = -1;
94
+ expected = [0xff,0xff,0xff,0xff];
95
+ it(`sint32 ${JSON.stringify(expected)}`, () => {
96
+ const result = E3.val2byteArr(val, 4, 1, true);
97
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
98
+ });
99
+ val = 2**31-1;
100
+ expected = [0xff,0xff,0xff,0x7f];
101
+ it(`sint32 ${JSON.stringify(expected)}`, () => {
102
+ const result = E3.val2byteArr(val, 4, 1, true);
103
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
104
+ });
105
+ val = -(2**31);
106
+ expected = [0x00,0x00,0x00,0x80];
107
+ it(`sint32 ${JSON.stringify(expected)}`, () => {
108
+ const result = E3.val2byteArr(val, 4, 1, true);
109
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
110
+ });
111
+ val = 2**32-1;
112
+ expected = [0xff,0xff,0xff,0xff];
113
+ it(`uint32 ${JSON.stringify(expected)}`, () => {
114
+ const result = E3.val2byteArr(val, 4, 1, false);
115
+ expect(JSON.stringify(result)).to.equal(JSON.stringify(expected));
116
+ });
117
+ });
118
+
119
+ // Integer DECODER:
120
+ describe('codecs.js => O3EInt8().decode()', () => {
121
+ codecDecode('Positive Number, scale=10, signed=true ', 5, E3.O3Ecodecs.O3EInt8, 1, 'test1', {scale:10.0,signed:true, offset: 0}, [0x32]);
122
+ codecDecode('Positive Number, scale=10, signed=true ', (2**7-1)/10, E3.O3Ecodecs.O3EInt8, 1, 'test2', {scale:10.0,signed:true, offset: 0}, [0x7f]);
123
+ codecDecode('Negative Number, scale=10, signed=true ', -(2**7)/10, E3.O3Ecodecs.O3EInt8, 1, 'test3', {scale:10.0,signed:true, offset: 0}, [0x80]);
124
+ codecDecode('Negative Number, scale= 1, signed=true ', -5, E3.O3Ecodecs.O3EInt8, 1, 'test4', {scale:1.0, signed:true, offset: 0}, [0xfb]);
125
+ codecDecode('Positive Number, scale=10, signed=false', 25.1, E3.O3Ecodecs.O3EInt8, 1, 'test5', {scale:10.0,signed:false,offset: 0}, [0xfb]);
126
+ });
127
+
128
+ describe('codecs.js => O3EInt16().decode()', () => {
129
+ codecDecode('Positive Number, scale=10, signed=true ', 5, E3.O3Ecodecs.O3EInt16, 2, 'test1', {scale:10.0,signed:true, offset: 0}, [0x32,0x00]);
130
+ codecDecode('Positive Number, scale=10, signed=true ', (2**15-1)/10, E3.O3Ecodecs.O3EInt16, 2, 'test2', {scale:10.0,signed:true, offset: 0}, [0xff,0x7f]);
131
+ codecDecode('Negative Number, scale=10, signed=true ', -(2**15)/10, E3.O3Ecodecs.O3EInt16, 2, 'test3', {scale:10.0,signed:true, offset: 0}, [0x00,0x80]);
132
+ codecDecode('Negative Number, scale= 1, signed=true ', -5, E3.O3Ecodecs.O3EInt16, 2, 'test4', {scale:1.0, signed:true, offset: 0}, [0xfb,0xff]);
133
+ codecDecode('Positive Number, scale=10, signed=false', (2**16-1)/10, E3.O3Ecodecs.O3EInt16, 2, 'test5', {scale:10.0,signed:false,offset: 0}, [0xff,0xff]);
134
+ codecDecode('Positive Number, scale=10, signed=false', 25.1, E3.O3Ecodecs.O3EInt16, 2, 'test6', {scale:10.0,signed:false,offset: 0}, [0xfb,0x00]);
135
+ });
136
+
137
+ describe('codecs.js => O3EInt32().decode()', () => {
138
+ codecDecode('Positive Number, scale=10, signed=true ', 5, E3.O3Ecodecs.O3EInt32, 4, 'test1', {scale:10.0,signed:true, offset: 0}, [0x32,0x00,0x00,0x00]);
139
+ codecDecode('Positive Number, scale=10, signed=true ', (2**31-1)/10, E3.O3Ecodecs.O3EInt32, 4, 'test2', {scale:10.0,signed:true, offset: 0}, [0xff,0xff,0xff,0x7f]);
140
+ codecDecode('Negative Number, scale=10, signed=true ', -(2**31)/10, E3.O3Ecodecs.O3EInt32, 4, 'test3', {scale:10.0,signed:true, offset: 0}, [0x00,0x00,0x00,0x80]);
141
+ codecDecode('Negative Number, scale= 1, signed=true ', -5, E3.O3Ecodecs.O3EInt32, 4, 'test4', {scale:1.0, signed:true, offset: 0}, [0xfb,0xff,0xff,0xff]);
142
+ codecDecode('Positive Number, scale=10, signed=false', (2**32-1)/10, E3.O3Ecodecs.O3EInt32, 4, 'test5', {scale:10.0,signed:false,offset: 0}, [0xff,0xff,0xff,0xff]);
143
+ codecDecode('Positive Number, scale=10, signed=false', 25.1, E3.O3Ecodecs.O3EInt32, 4, 'test6', {scale:10.0,signed:false,offset: 0}, [0xfb,0x00,0x00,0x00]);
144
+ });
145
+
146
+ // Integer ENCODER:
147
+ describe('codecs.js => O3EInt8().encode()', () => {
148
+ codecEncode('Positive Number, scale=10, signed=true ', [0x32], E3.O3Ecodecs.O3EInt8, 1, 'test1', {scale:10.0,signed:true, offset: 0}, 5);
149
+ codecEncode('Positive Number, scale=10, signed=true ', [0x7f], E3.O3Ecodecs.O3EInt8, 1, 'test2', {scale:10.0,signed:true, offset: 0}, (2**7-1)/10);
150
+ codecEncode('Negative Number, scale=10, signed=true ', [0x80], E3.O3Ecodecs.O3EInt8, 1, 'test3', {scale:10.0,signed:true, offset: 0}, -(2**7)/10);
151
+ codecEncode('Negative Number, scale= 1, signed=true ', [0xfb], E3.O3Ecodecs.O3EInt8, 1, 'test4', {scale:1.0, signed:true, offset: 0}, -5);
152
+ codecEncode('Positive Number, scale=10, signed=false', [0xfb], E3.O3Ecodecs.O3EInt8, 1, 'test5', {scale:10.0,signed:false,offset: 0}, 25.1);
153
+ });
154
+
155
+ describe('codecs.js => O3EInt16().decode()', () => {
156
+ codecEncode('Positive Number, scale=10, signed=true ', [0x32,0x00], E3.O3Ecodecs.O3EInt16, 2, 'test1', {scale:10.0,signed:true, offset: 0}, 5);
157
+ codecEncode('Positive Number, scale=10, signed=true ', [0xff,0x7f], E3.O3Ecodecs.O3EInt16, 2, 'test2', {scale:10.0,signed:true, offset: 0}, (2**15-1)/10);
158
+ codecEncode('Negative Number, scale=10, signed=true ', [0x00,0x80], E3.O3Ecodecs.O3EInt16, 2, 'test3', {scale:10.0,signed:true, offset: 0}, -(2**15)/10);
159
+ codecEncode('Negative Number, scale= 1, signed=true ', [0xfb,0xff], E3.O3Ecodecs.O3EInt16, 2, 'test4', {scale:1.0, signed:true, offset: 0}, -5);
160
+ codecEncode('Positive Number, scale=10, signed=false', [0xff,0xff], E3.O3Ecodecs.O3EInt16, 2, 'test5', {scale:10.0,signed:false,offset: 0}, (2**16-1)/10);
161
+ codecEncode('Positive Number, scale=10, signed=false', [0xfb,0x00], E3.O3Ecodecs.O3EInt16, 2, 'test6', {scale:10.0,signed:false,offset: 0}, 25.1);
162
+ });
163
+
164
+ describe('codecs.js => O3EInt32().encode()', () => {
165
+ codecEncode('Positive Number, scale=10, signed=true ', [0x32,0x00,0x00,0x00], E3.O3Ecodecs.O3EInt32, 4, 'test1', {scale:10.0,signed:true, offset: 0}, 5);
166
+ codecEncode('Positive Number, scale=10, signed=true ', [0xff,0xff,0xff,0x7f], E3.O3Ecodecs.O3EInt32, 4, 'test2', {scale:10.0,signed:true, offset: 0}, (2**31-1)/10);
167
+ codecEncode('Negative Number, scale=10, signed=true ', [0x00,0x00,0x00,0x80], E3.O3Ecodecs.O3EInt32, 4, 'test3', {scale:10.0,signed:true, offset: 0}, -(2**31)/10);
168
+ codecEncode('Negative Number, scale= 1, signed=true ', [0xfb,0xff,0xff,0xff], E3.O3Ecodecs.O3EInt32, 4, 'test4', {scale:1.0, signed:true, offset: 0}, -5);
169
+ codecEncode('Positive Number, scale=10, signed=false', [0xff,0xff,0xff,0xff], E3.O3Ecodecs.O3EInt32, 4, 'test5', {scale:10.0,signed:false,offset: 0}, (2**32-1)/10);
170
+ codecEncode('Positive Number, scale=10, signed=false', [0xfb,0x00,0x00,0x00], E3.O3Ecodecs.O3EInt32, 4, 'test6', {scale:10.0,signed:false,offset: 0}, 25.1);
171
+ });
172
+
173
+ // Complex data structures
174
+ // Do testing for specific dids to cover all available codecs
175
+ describe('codecs.js => O3EComplexType()', () => {
176
+ const raw_hex = '3b0206004700fd01c30801000300f9013001020030303030303030303030303030383135';
177
+ const raw_arr = toByteArray(raw_hex);
178
+ const js = {'BusAddress':59,'BusType':{'ID':2,'Text':'CanInternal'},'DeviceProperty':{'ID':6,'Text':'HMI'},'DeviceFunction':{'ID':0,'Text':'NOTHING'},'SW-Version':'71.509.2243.1','HW-Version':'3.505.304.2','VIN':'0000000000000815'};
179
+ const cdi = E3DidsDict[256];
180
+ codecDecode('Decoding did 256 ', js, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, raw_arr);
181
+ // Encoding makes no sense for this did in real life, but testing helps to improve codec robustness
182
+ codecEncode('Encoding did 256 ', raw_arr, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, js);
183
+ });
184
+
185
+ describe('codecs.js => O3EList()', () => {
186
+ const raw_hex = '0a00b001f30314170b12050e3a313602f30314170b12050e390c3602f30314170b12050e2f273602f30314170b12050d2b063602f30314170b12050d2a233602f30314170b12050d27383602f30314170b12050d26033602f30314170b12050d242d3602f30314170b12050d1f043602f30314170b12050d1e133602';
187
+ const raw_arr = toByteArray(raw_hex);
188
+ const js = {'Count':10,'GrandTotal':432,'ListEntries':[{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 14:58:49','Timestamp':1700315929000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 14:57:12','Timestamp':1700315832000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 14:47:39','Timestamp':1700315259000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 13:43:06','Timestamp':1700311386000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 13:42:35','Timestamp':1700311355000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 13:39:56','Timestamp':1700311196000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 13:38:03','Timestamp':1700311083000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 13:36:45','Timestamp':1700311005000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 13:31:04','Timestamp':1700310664000},'Unknown':566},{'Error':{'ID':1011,'Text':'FailureCompressorOutletPressureSensor'},'DateTime':{'DateTime':'18/11/2023, 13:30:19','Timestamp':1700310619000},'Unknown':566}]};
189
+ const cdi = E3DidsDict[266];
190
+ codecDecode('Decoding did 266 ', js, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, raw_arr);
191
+ // Encoding makes no sense for this did in real life, but testing helps to improve codec robustness
192
+ codecEncode('Encoding did 266 ', raw_arr, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, js);
193
+ });
194
+
195
+ describe('codecs.js => O3EComplexType()', () => {
196
+ const raw_hex = '8c01c1007a027e0100';
197
+ const raw_arr = toByteArray(raw_hex);
198
+ const js = {'Actual':39.6,'Minimum':19.3,'Maximum':63.4,'Average':38.2,'Unknown':0};
199
+ const cdi = E3DidsDict[268];
200
+ codecDecode('Decoding did 268 ', js, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, raw_arr);
201
+ // Encoding makes no sense for this did in real life, but testing helps to improve codec robustness
202
+ codecEncode('Encoding did 268 ', raw_arr, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, js);
203
+ });
204
+
205
+ describe('codecs.js => O3EList()', () => {
206
+ const raw_hex = '020800091e0000030f001100000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
207
+ const raw_arr = toByteArray(raw_hex);
208
+ const js = {'Count':2,'Schedules':[{'Start':'08:00:00','Stop':'09:30:00','Unknown':'0000','Mode':3},{'Start':'15:00:00','Stop':'17:00:00','Unknown':'0000','Mode':3}]};
209
+ const cdi = E3DidsDict[726];
210
+ codecDecode('Decoding did 726 ', js, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, raw_arr);
211
+ codecEncode('Encoding did 726 ', raw_arr, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, js);
212
+ });
213
+
214
+ describe('codecs.js => O3EComplexType()', () => {
215
+ const raw_hex = '38004f0068005c004b0045005400650059000602780090003c004c004b007400840058000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000028002f004200';
216
+ const raw_arr = toByteArray(raw_hex);
217
+ const js = {'CurrentMonth':{'10':51.8,'11':12,'12':14.4,'13':6,'14':7.6,'15':7.5,'16':11.6,'17':13.2,'18':8.8,'19':0,'20':0,'21':0,'22':0,'23':0,'24':0,'25':0,'26':0,'27':0,'28':0,'29':0,'30':0,'31':0,'01':5.6,'02':7.9,'03':10.4,'04':9.2,'05':7.5,'06':6.9,'07':8.4,'08':10.1,'09':8.9},'LastMonth':{'10':0,'11':0,'12':0,'13':0,'14':0,'15':0,'16':0.4,'17':0,'18':0,'19':0,'20':0,'21':0,'22':0,'23':0,'24':0,'25':0,'26':0,'27':0,'28':0,'29':4,'30':4.7,'31':6.6,'01':0,'02':0,'03':0,'04':0,'05':0,'06':0,'07':0,'08':0,'09':0}};
218
+ const cdi = E3DidsDict[1294];
219
+ codecDecode('Decoding did 1294 ', js, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, raw_arr);
220
+ // Encoding makes no sense for this did in real life, but testing helps to improve codec robustness
221
+ codecEncode('Encoding did 1294 ', raw_arr, E3.O3Ecodecs[cdi.codec], cdi.len, cdi.id, cdi.args, js);
222
+ });
@@ -15,6 +15,8 @@
15
15
  "436": "MixerSevenCircuitRoomTemperatureSetpoint",
16
16
  "438": "MixerEightCircuitRoomTemperatureSetpoint",
17
17
  "451": "ExternalAlternatingCurrentPowerSetpoint",
18
+ "491": "DomesticHotWaterCirculationPump",
19
+ "497": "DomesticHotWaterCirculationPumpMode",
18
20
  "504": "DomesticHotWaterSetpointMetaData",
19
21
  "508": "UniversalTimeCoordinatedOffset",
20
22
  "524": "ModulationTargetSetpoint",
@@ -79,7 +81,23 @@
79
81
  "1009": "MixerTwoCircuitTargetQuickMode",
80
82
  "1010": "MixerThreeCircuitTargetQuickMode",
81
83
  "1011": "MixerFourCircuitTargetQuickMode",
84
+ "1084": "FlowTemperatureMinimumMaximumLimit",
85
+ "1100": "CentralHeatingPumpMinimumMaximumLimit",
86
+ "1101": "DomesticHotWaterPumpMinimumMaximumLimit",
87
+ "1102": "MixerOneCircuitPumpMinimumMaximumLimit",
88
+ "1103": "MixerTwoCircuitPumpMinimumMaximumLimit",
89
+ "1104": "MixerThreeCircuitPumpMinimumMaximumLimit",
90
+ "1105": "MixerFourCircuitPumpMinimumMaximumLimit",
91
+ "1118": "SolarCircuitPumpMinimumMaximumLimit",
82
92
  "1167": "ExternalDomesticHotWaterTemperatureSetpoint",
93
+ "1192": "MixerOneCircuitFlowTemperatureMinimumMaximumLimit",
94
+ "1193": "MixerTwoCircuitFlowTemperatureMinimumMaximumLimit",
95
+ "1194": "MixerThreeCircuitFlowTemperatureMinimumMaximumLimit",
96
+ "1195": "MixerFourCircuitFlowTemperatureMinimumMaximumLimit",
97
+ "1196": "MixerFiveCircuitFlowTemperatureMinimumMaximumLimit",
98
+ "1197": "MixerSixCircuitFlowTemperatureMinimumMaximumLimit",
99
+ "1198": "MixerSevenCircuitFlowTemperatureMinimumMaximumLimit",
100
+ "1199": "MixerEightCircuitFlowTemperatureMinimumMaximumLimit",
83
101
  "1265": "FanTargetSpeedMeta",
84
102
  "1363": "FuelCellTargetOperationMode",
85
103
  "1394": "SolarChargingDomesticHotWaterSetpoint",
package/main.js CHANGED
@@ -1,5 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ /*
4
+ * Unveil life data of Viessmann E3 series devices via CAN bus
5
+ *
6
+ * Based on project open3e: https://github.com/open3e/open3e
7
+ *
8
+ */
9
+
3
10
  /*
4
11
  * Created with @iobroker/create-adapter v2.5.0
5
12
  */
@@ -8,9 +15,7 @@
8
15
  // you need to create an adapter
9
16
  const utils = require('@iobroker/adapter-core');
10
17
 
11
- // Load your modules here, e.g.:
12
- // const fs = require("fs");
13
-
18
+ // Loading modules:
14
19
  const can = require('socketcan');
15
20
  const storage = require('./lib/storage');
16
21
  const collect = require('./lib/canCollect');
@@ -28,7 +33,7 @@ class E3oncan extends utils.Adapter {
28
33
  name: 'e3oncan',
29
34
  });
30
35
 
31
- this.e380Collect = null; // E380 alway is assigned to external bus
36
+ this.e380Collect = null; // E380 always is assigned to external bus
32
37
  this.E3CollectInt = {}; // Dict of collect devices on internal bus
33
38
  this.E3CollectExt = {}; // Dict of collect devices on external bus
34
39
  this.collectTimeout = 1500; // Timeout (ms) for collecting data
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.e3oncan",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
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",
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "repository": {
20
20
  "type": "git",
21
- "url": "git@github.com:MyHomeMyData/ioBroker.e3oncan.git"
21
+ "url": "https://github.com/MyHomeMyData/ioBroker.e3oncan.git"
22
22
  },
23
23
  "engines": {
24
24
  "node": ">= 16"