iobroker.e3oncan 0.6.9
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/LICENSE +21 -0
- package/README.md +129 -0
- package/admin/e3oncan.png +0 -0
- package/admin/e3oncan_small.png +0 -0
- package/admin/i18n/de/translations.json +60 -0
- package/admin/i18n/en/translations.json +60 -0
- package/admin/i18n/es/translations.json +62 -0
- package/admin/i18n/fr/translations.json +62 -0
- package/admin/i18n/it/translations.json +62 -0
- package/admin/i18n/nl/translations.json +62 -0
- package/admin/i18n/pl/translations.json +62 -0
- package/admin/i18n/pt/translations.json +62 -0
- package/admin/i18n/ru/translations.json +62 -0
- package/admin/i18n/uk/translations.json +62 -0
- package/admin/i18n/zh-cn/translations.json +62 -0
- package/admin/jsonConfig.json +594 -0
- package/admin/udsDevices.json +37 -0
- package/io-package.json +164 -0
- package/lib/adapter-config.d.ts +19 -0
- package/lib/canCollect.js +199 -0
- package/lib/canUds.js +715 -0
- package/lib/codecs.js +709 -0
- package/lib/didsE3.json +29025 -0
- package/lib/didsE380.json +275 -0
- package/lib/didsE3Writables.json +396 -0
- package/lib/enums.js +2223 -0
- package/lib/storage.js +396 -0
- package/lib/udsScan.js +353 -0
- package/main.js +490 -0
- package/package.json +80 -0
package/io-package.json
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"name": "e3oncan",
|
|
4
|
+
"version": "0.6.9",
|
|
5
|
+
"news": {
|
|
6
|
+
"0.6.9": {
|
|
7
|
+
"en": "Bugfix: Only Linux is supported",
|
|
8
|
+
"de": "Bugfix: Nur Linux wird unterstützt",
|
|
9
|
+
"ru": "Исправление: Только Linux поддерживается",
|
|
10
|
+
"pt": "Bugfix: Apenas o Linux é suportado",
|
|
11
|
+
"nl": "Bugfix: Alleen Linux wordt ondersteund",
|
|
12
|
+
"fr": "Correction : Seul Linux est pris en charge",
|
|
13
|
+
"it": "Bugfix: Solo Linux è supportato",
|
|
14
|
+
"es": "Bugfix: Sólo Linux es compatible",
|
|
15
|
+
"pl": "Bugfix: Tylko Linux jest obsługiwany",
|
|
16
|
+
"uk": "Помилки: Тільки Linux підтримується",
|
|
17
|
+
"zh-cn": "错误修正 : 仅支持 Linux"
|
|
18
|
+
},
|
|
19
|
+
"0.6.8": {
|
|
20
|
+
"en": "Initial npm version",
|
|
21
|
+
"de": "Ursprüngliche npm-Version",
|
|
22
|
+
"ru": "Первоначальная версия npm",
|
|
23
|
+
"pt": "Versão inicial npm",
|
|
24
|
+
"nl": "Eerste npm-versie",
|
|
25
|
+
"fr": "Version initiale npm",
|
|
26
|
+
"it": "Versione npm iniziale",
|
|
27
|
+
"es": "Versión inicial npm",
|
|
28
|
+
"pl": "Początkowa wersja npm",
|
|
29
|
+
"uk": "Початкова версія npm",
|
|
30
|
+
"zh-cn": "初始 npm 版本"
|
|
31
|
+
},
|
|
32
|
+
"0.6.6": {
|
|
33
|
+
"en": "initial npm release",
|
|
34
|
+
"de": "Erstveröffentlichung auf npm",
|
|
35
|
+
"ru": "Начальная версия",
|
|
36
|
+
"pt": "lançamento inicial",
|
|
37
|
+
"nl": "Eerste uitgave",
|
|
38
|
+
"fr": "Première version",
|
|
39
|
+
"it": "Versione iniziale",
|
|
40
|
+
"es": "Versión inicial",
|
|
41
|
+
"pl": "Pierwsze wydanie",
|
|
42
|
+
"uk": "Початкова версія",
|
|
43
|
+
"zh-cn": "首次出版"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"title": "Viessmann E3 CAN do",
|
|
47
|
+
"titleLang": {
|
|
48
|
+
"en": "Viessmann E3 CAN do",
|
|
49
|
+
"de": "Viessmann E3 CAN do",
|
|
50
|
+
"ru": "CAN-прослушиватель серии Viessmann E3",
|
|
51
|
+
"pt": "Ouvinte CAN da série Viessmann E3",
|
|
52
|
+
"nl": "Viessmann E3-serie CAN-luisteraar",
|
|
53
|
+
"fr": "Écouteur CAN Viessmann série E3",
|
|
54
|
+
"it": "Ascoltatore CAN Viessmann serie E3",
|
|
55
|
+
"es": "Oyente CAN serie Viessmann E3",
|
|
56
|
+
"pl": "Odbiornik CAN firmy Viessmann serii E3",
|
|
57
|
+
"uk": "CAN-слухач серії Viessmann E3",
|
|
58
|
+
"zh-cn": "菲斯曼E3系列CAN监听器"
|
|
59
|
+
},
|
|
60
|
+
"desc": {
|
|
61
|
+
"en": "Collect data on CAN bus for Viessmann E3 devices, e.g. Vitocal, Vitocharge, Energy Meter E380",
|
|
62
|
+
"de": "Greifen Sie auf Daten per CAN-Bus zu, für Viessmann E3-Geräte, z.B. Vitocal, Vitocharge, Energiezähler E380",
|
|
63
|
+
"ru": "Сбор данных по шине CAN для устройств Viessmann E3, например. Vitocal, Vitocharge, Счетчик энергии E380",
|
|
64
|
+
"pt": "Colete dados no barramento CAN para dispositivos Viessmann E3, por ex. Vitocal, Vitocharge, medidor de energia E380",
|
|
65
|
+
"nl": "Verzamel gegevens op de CAN-bus voor Viessmann E3-apparaten, b.v. Vitocal, Vitocharge, Energiemeter E380",
|
|
66
|
+
"fr": "Collectez des données sur le bus CAN pour les appareils Viessmann E3, par ex. Vitocal, Vitocharge, Compteur d'énergie E380",
|
|
67
|
+
"it": "Raccogliere dati sul bus CAN per dispositivi Viessmann E3, ad es. Vitocal, Vitocharge, Contatore di energia E380",
|
|
68
|
+
"es": "Recopilar datos en el bus CAN para dispositivos Viessmann E3, p. Vitocal, Vitocharge, Contador de energía E380",
|
|
69
|
+
"pl": "Zbieraj dane na magistrali CAN dla urządzeń Viessmann E3, np.: Vitocal, Vitocharge, licznik energii E380",
|
|
70
|
+
"uk": "Збір даних на шині CAN для пристроїв Viessmann E3, напр. Vitocal, Vitocharge, Енерголічильник E380",
|
|
71
|
+
"zh-cn": "收集 Viessmann E3 设备 CAN 总线上的数据,例如Vitocal、Vitocharge、能量计 E380"
|
|
72
|
+
},
|
|
73
|
+
"authors": [
|
|
74
|
+
"MyHomeMyData <juergen.bonfert@gmail.com>"
|
|
75
|
+
],
|
|
76
|
+
"keywords": [
|
|
77
|
+
"Viessmann",
|
|
78
|
+
"E3",
|
|
79
|
+
"CAN",
|
|
80
|
+
"Heating",
|
|
81
|
+
"PV",
|
|
82
|
+
"Energy Meter"
|
|
83
|
+
],
|
|
84
|
+
"license": "MIT",
|
|
85
|
+
"platform": "Javascript/Node.js",
|
|
86
|
+
"main": "main.js",
|
|
87
|
+
"icon": "e3oncan.png",
|
|
88
|
+
"enabled": true,
|
|
89
|
+
"messagebox": true,
|
|
90
|
+
"extIcon": "https://raw.githubusercontent.com/MyHomeMyData/ioBroker.e3oncan/main/admin/e3oncan.png",
|
|
91
|
+
"readme": "https://github.com/MyHomeMyData/ioBroker.e3oncan/blob/main/README.md",
|
|
92
|
+
"loglevel": "debug",
|
|
93
|
+
"mode": "daemon",
|
|
94
|
+
"type": "iot-systems",
|
|
95
|
+
"compact": true,
|
|
96
|
+
"connectionType": "local",
|
|
97
|
+
"dataSource": "push",
|
|
98
|
+
"adminUI": {
|
|
99
|
+
"config": "json"
|
|
100
|
+
},
|
|
101
|
+
"dependencies": [
|
|
102
|
+
{
|
|
103
|
+
"js-controller": ">=3.3.22"
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"globalDependencies": [
|
|
107
|
+
{
|
|
108
|
+
"admin": ">=5.1.13"
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
"native": {
|
|
113
|
+
"tableUdsDevices": [],
|
|
114
|
+
"tableUdsDids": []
|
|
115
|
+
},
|
|
116
|
+
"objects": [],
|
|
117
|
+
"instanceObjects": [
|
|
118
|
+
{
|
|
119
|
+
"_id": "info",
|
|
120
|
+
"type": "channel",
|
|
121
|
+
"common": {
|
|
122
|
+
"name": {
|
|
123
|
+
"en": "Information",
|
|
124
|
+
"de": "Informationen",
|
|
125
|
+
"ru": "Информация",
|
|
126
|
+
"pt": "Informação",
|
|
127
|
+
"nl": "Informatie",
|
|
128
|
+
"fr": "Informations",
|
|
129
|
+
"it": "Informazioni",
|
|
130
|
+
"es": "Información",
|
|
131
|
+
"pl": "Informacje",
|
|
132
|
+
"uk": "Інформація",
|
|
133
|
+
"zh-cn": "资料"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"native": {}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"_id": "info.connection",
|
|
140
|
+
"type": "state",
|
|
141
|
+
"common": {
|
|
142
|
+
"role": "indicator.connected",
|
|
143
|
+
"name": {
|
|
144
|
+
"en": "Device or service connected",
|
|
145
|
+
"de": "Geräte oder Service vrbunden",
|
|
146
|
+
"ru": "Устройство или служба подключены",
|
|
147
|
+
"pt": "Dispositivo ou serviço conectado",
|
|
148
|
+
"nl": "Apparaat of dienst verbonden",
|
|
149
|
+
"fr": "Appareil ou service connecté",
|
|
150
|
+
"it": "Dispositivo o servizio collegato",
|
|
151
|
+
"es": "Dispositivo o servicio conectado",
|
|
152
|
+
"pl": "Przyłączone urządzenie lub usługa",
|
|
153
|
+
"uk": "Пристрої або послуги підключені",
|
|
154
|
+
"zh-cn": "设备或服务已连接"
|
|
155
|
+
},
|
|
156
|
+
"type": "boolean",
|
|
157
|
+
"read": true,
|
|
158
|
+
"write": false,
|
|
159
|
+
"def": false
|
|
160
|
+
},
|
|
161
|
+
"native": {}
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// This file extends the AdapterConfig type from "@types/iobroker"
|
|
2
|
+
// using the actual properties present in io-package.json
|
|
3
|
+
// in order to provide typings for adapter.config properties
|
|
4
|
+
|
|
5
|
+
import { native } from '../io-package.json';
|
|
6
|
+
|
|
7
|
+
type _AdapterConfig = typeof native;
|
|
8
|
+
|
|
9
|
+
// Augment the globally declared type ioBroker.AdapterConfig
|
|
10
|
+
declare global {
|
|
11
|
+
namespace ioBroker {
|
|
12
|
+
interface AdapterConfig extends _AdapterConfig {
|
|
13
|
+
// Do not enter anything here!
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// this is required so the above AdapterConfig is found by TypeScript / type checking
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const storage = require('./storage');
|
|
2
|
+
|
|
3
|
+
class collect {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
this.config.statId = 'statCollect';
|
|
7
|
+
this.config.worker = 'collect';
|
|
8
|
+
this.storage = new storage.storage(this.config);
|
|
9
|
+
this.ts = {};
|
|
10
|
+
this.msDelay = config.delay*1000;
|
|
11
|
+
this.timeoutHandle = null;
|
|
12
|
+
this.maxDid = 3500;
|
|
13
|
+
this.commBusy = false; // Communication routine running
|
|
14
|
+
this.data = {
|
|
15
|
+
'len' : 0,
|
|
16
|
+
'timestamp' : 0,
|
|
17
|
+
'databytes' : [],
|
|
18
|
+
'did' : 0,
|
|
19
|
+
'collecting': false,
|
|
20
|
+
'D0expected': 0x21
|
|
21
|
+
};
|
|
22
|
+
this.stat = {
|
|
23
|
+
state : 'standby',
|
|
24
|
+
cntCommTotal : 0, // Number collected dids
|
|
25
|
+
cntCommOk : 0, // Number of ok
|
|
26
|
+
cntCommStored : 0, // Number of dids stored
|
|
27
|
+
cntCommTimeout : 0, // Number of timeouts
|
|
28
|
+
cntCommBadProt : 0, // Number of bad communications
|
|
29
|
+
cntTooBusy : 0, // Number of conflicting calls of msgCollect()
|
|
30
|
+
nextTs : 0, // Timestamp for next storage (earliest)
|
|
31
|
+
tsMinStep : 5000 // Minimum time step between storages
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async initStates(ctx, opMode) {
|
|
36
|
+
await this.storage.initStates(ctx, opMode);
|
|
37
|
+
this.stat.state = 'standby';
|
|
38
|
+
await this.storage.storeStatistics(ctx, this, true);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async onTimeout(ctxGlobal, ctxLocal) {
|
|
42
|
+
ctxGlobal.log.error('Collect timeout on 0x'+Number(ctxLocal.config.canID[0]).toString(16)+'.'+String(ctxLocal.data.did));
|
|
43
|
+
ctxLocal.stat.cntCommTimeout += 1;
|
|
44
|
+
ctxLocal.data.collecting = false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async startup(ctx) {
|
|
48
|
+
this.stat.state = 'active';
|
|
49
|
+
await this.storage.storeStatistics(ctx, this, true);
|
|
50
|
+
this.data.collecting = false;
|
|
51
|
+
await this.storage.setOpMode('normal');
|
|
52
|
+
await ctx.log.info('Collect worker started on '+this.config.stateBase);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async stop(ctx) {
|
|
56
|
+
try {
|
|
57
|
+
await this.storage.setOpMode('standby');
|
|
58
|
+
|
|
59
|
+
// Stop Timeout:
|
|
60
|
+
if (this.timeoutHandle) await clearTimeout(this.timeoutHandle);
|
|
61
|
+
this.timeoutHandle = null;
|
|
62
|
+
|
|
63
|
+
this.stat.state = 'stopped';
|
|
64
|
+
await this.storage.storeStatistics(ctx, this, true);
|
|
65
|
+
|
|
66
|
+
// Stop worker:
|
|
67
|
+
this.data.collecting = false;
|
|
68
|
+
await ctx.log.info('Collect worker stopped on '+this.config.stateBase);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
await ctx.log.error('Collect worker on '+this.config.stateBase+' could not be stopped. err='+e.message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
sleep(milliseconds) {
|
|
75
|
+
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async msgCollect(ctx, msg) {
|
|
79
|
+
|
|
80
|
+
if (await this.storage.getOpMode() == 'standby') return; // No communication in mode 'standby'
|
|
81
|
+
|
|
82
|
+
if (this.commBusy) {
|
|
83
|
+
this.stat.cntTooBusy += 1;
|
|
84
|
+
if (this.stat.cntTooBusy == 1) ctx.log.warn('Collect worker error on '+this.config.stateBase+': Evaluation of messages overloaded.');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.commBusy = true;
|
|
89
|
+
const candata = msg.data.toJSON().data;
|
|
90
|
+
|
|
91
|
+
const canid = msg.id;
|
|
92
|
+
const msgDlc = candata.length;
|
|
93
|
+
const tsNow = new Date().getTime();
|
|
94
|
+
if (this.config.device == 'e380') {
|
|
95
|
+
// Energy meter
|
|
96
|
+
this.stat.cntCommTotal += 1;
|
|
97
|
+
this.stat.cntCommOk += 1;
|
|
98
|
+
if (!(canid in this.ts)) { this.ts[canid] = tsNow; }
|
|
99
|
+
if ( (this.config.delay == 0) || ((tsNow >= this.ts[canid])) ) {
|
|
100
|
+
this.stat.cntCommStored += 1;
|
|
101
|
+
this.storage.decodeDataCAN(ctx,this,msg.id,candata);
|
|
102
|
+
this.ts[canid] = tsNow+this.msDelay;
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// E3 device
|
|
106
|
+
if (this.data.collecting) {
|
|
107
|
+
if (candata[0] == this.data.D0expected) {
|
|
108
|
+
// append next part of data
|
|
109
|
+
this.data.databytes = this.data.databytes.concat(candata.slice(1));
|
|
110
|
+
if (this.data.databytes.length >= this.data.len) {
|
|
111
|
+
// All data received
|
|
112
|
+
if (this.timeoutHandle) await clearTimeout(this.timeoutHandle);
|
|
113
|
+
this.stat.cntCommOk += 1;
|
|
114
|
+
if (!(this.data.did in this.ts)) { this.ts[this.data.did] = tsNow; }
|
|
115
|
+
if ( (this.config.delay == 0) || ((tsNow >= this.ts[this.data.did])) ) {
|
|
116
|
+
this.stat.cntCommStored += 1;
|
|
117
|
+
this.storage.decodeDataCAN(ctx, this, this.data.did, this.data.databytes.slice(0,this.data.len));
|
|
118
|
+
this.ts[this.data.did] = tsNow+this.msDelay;
|
|
119
|
+
}
|
|
120
|
+
this.data.collecting = false;
|
|
121
|
+
} else {
|
|
122
|
+
// More data to come
|
|
123
|
+
this.data.D0expected += 1;
|
|
124
|
+
if (this.data.D0expected > 0x2f) {
|
|
125
|
+
this.data.D0expected = 0x20;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const D3 = candata[3];
|
|
132
|
+
if ( (!this.data.collecting) && (msgDlc > 4) && (candata[0] == 0x21) && (D3 >= 0xb0) && (D3 < 0xc0)) {
|
|
133
|
+
this.data.D0expected = candata[0];
|
|
134
|
+
this.data.did = candata[1]+256*candata[2];
|
|
135
|
+
this.data.timestamp = msg.ts_sec*1000+Math.round(msg.ts_usec/1000);
|
|
136
|
+
if ( (this.data.did > 0) && (this.data.did < this.maxDid) ) {
|
|
137
|
+
switch (D3) {
|
|
138
|
+
case 0xb1:
|
|
139
|
+
case 0xb2:
|
|
140
|
+
case 0xb3:
|
|
141
|
+
case 0xb4:
|
|
142
|
+
// Single Frame B1,B2,B3,B4
|
|
143
|
+
this.data.len = D3-0xb0;
|
|
144
|
+
this.data.databytes = candata.slice(4);
|
|
145
|
+
this.stat.cntCommTotal += 1;
|
|
146
|
+
this.stat.cntCommOk += 1;
|
|
147
|
+
if (!(this.data.did in this.ts)) { this.ts[this.data.did] = tsNow; }
|
|
148
|
+
if ( (this.config.delay == 0) || ((tsNow >= this.ts[this.data.did])) ) {
|
|
149
|
+
this.stat.cntCommStored += 1;
|
|
150
|
+
this.storage.decodeDataCAN(ctx, this, this.data.did, this.data.databytes.slice(0,this.data.len));
|
|
151
|
+
this.ts[this.data.did] = tsNow+this.msDelay;
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
case 0xb0:
|
|
155
|
+
// Multi Frame B0
|
|
156
|
+
this.data.D0expected = candata[0]+1;
|
|
157
|
+
this.stat.cntCommTotal += 1;
|
|
158
|
+
if (candata[4]==0xc1) {
|
|
159
|
+
this.data.databytes = candata.slice(6);
|
|
160
|
+
this.data.len = candata[5];
|
|
161
|
+
} else {
|
|
162
|
+
this.data.databytes = candata.slice(5);
|
|
163
|
+
this.data.len = candata[4];
|
|
164
|
+
}
|
|
165
|
+
this.timeoutHandle = setTimeout(this.onTimeout, this.config.timeout, ctx, this);
|
|
166
|
+
this.data.collecting = true;
|
|
167
|
+
break;
|
|
168
|
+
case 0xb5:
|
|
169
|
+
case 0xb6:
|
|
170
|
+
case 0xb7:
|
|
171
|
+
case 0xb8:
|
|
172
|
+
case 0xb9:
|
|
173
|
+
case 0xba:
|
|
174
|
+
case 0xbb:
|
|
175
|
+
case 0xbc:
|
|
176
|
+
case 0xbd:
|
|
177
|
+
case 0xbe:
|
|
178
|
+
case 0xbf:
|
|
179
|
+
// Multi Frame B6 .. BF
|
|
180
|
+
this.stat.cntCommTotal += 1;
|
|
181
|
+
this.data.D0expected = candata[0]+1;
|
|
182
|
+
this.data.databytes = candata.slice(4);
|
|
183
|
+
this.data.len = D3-0xb0;
|
|
184
|
+
this.timeoutHandle = setTimeout(this.onTimeout, this.config.timeout, ctx, this);
|
|
185
|
+
this.data.collecting = true;
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
ctx.log.debug('Collect worker error on '+this.config.stateBase+': Unplausible byte D3: Did='+String(this.data.did)+' D3=0x'+Number(D3).toString(16)+' candata='+this.storage.arr2Hex(candata));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
this.commBusy = false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
collect
|
|
199
|
+
};
|