iobroker.easee 1.0.2 → 1.0.6
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 +21 -0
- package/io-package.json +50 -2
- package/lib/enum.js +16 -2
- package/main.js +326 -50
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,7 +16,28 @@
|
|
|
16
16
|
|
|
17
17
|
Adapter to connect Easee Wallbox
|
|
18
18
|
|
|
19
|
+
## Help
|
|
20
|
+
|
|
21
|
+
chargerOpMode =
|
|
22
|
+
Offline: 0,
|
|
23
|
+
Disconnected: 1,
|
|
24
|
+
AwaitingStart: 2,
|
|
25
|
+
Charging: 3,
|
|
26
|
+
Completed: 4,
|
|
27
|
+
Error: 5,
|
|
28
|
+
ReadyToCharge: 6
|
|
29
|
+
|
|
30
|
+
dynamicCircuitCurrentPX -> All phases must be set within 500ms (script) otherwise the phase will be set to 0.
|
|
31
|
+
|
|
19
32
|
## Changelog
|
|
33
|
+
### 1.0.5
|
|
34
|
+
* (marwin79) More Features supported and convert values to expected datatypes
|
|
35
|
+
|
|
36
|
+
### 1.0.4
|
|
37
|
+
* (Newan) dynamicCircuitCurrentPX writeable (set all Phases in 500ms) to limit ampere
|
|
38
|
+
|
|
39
|
+
### 1.0.3
|
|
40
|
+
* (Newan) Adapter crash fixed an other bugfixes
|
|
20
41
|
|
|
21
42
|
### 1.0.1
|
|
22
43
|
* (Newan) Add circuitMaxCurrentPX to limit current ampere
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "easee",
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"news": {
|
|
4
|
+
"version": "1.0.6",
|
|
5
|
+
"news": {
|
|
6
|
+
"1.0.6": {
|
|
7
|
+
"en": "Changed that smart charging is editable",
|
|
8
|
+
"de": "Geändert, dass Smart Charging bearbeitet werden kann",
|
|
9
|
+
"ru": "Изменено, что умная зарядка доступна для редактирования",
|
|
10
|
+
"pt": "Alterado que o carregamento inteligente é editável",
|
|
11
|
+
"nl": "Gewijzigd dat slim opladen bewerkbaar is",
|
|
12
|
+
"fr": "Changé que la charge intelligente est modifiable",
|
|
13
|
+
"it": "Modificato che la ricarica intelligente è modificabile",
|
|
14
|
+
"es": "Se modificó que la carga inteligente es editable",
|
|
15
|
+
"pl": "Zmieniono, że inteligentne ładowanie można edytować",
|
|
16
|
+
"zh-cn": "更改智能充电可编辑"
|
|
17
|
+
},
|
|
18
|
+
"1.0.5": {
|
|
19
|
+
"en": "marwin79 - More Features supported and convert values to expected datatypes",
|
|
20
|
+
"de": "marwin79 - Weitere unterstützte Funktionen und Konvertieren von Werten in erwartete Datentypen",
|
|
21
|
+
"ru": "marwin79 - Поддерживаются дополнительные функции и преобразование значений в ожидаемые типы данных",
|
|
22
|
+
"pt": "marwin79 - Mais recursos suportados e convertem valores em tipos de dados esperados",
|
|
23
|
+
"nl": "marwin79 - Meer ondersteunde functies en converteer waarden naar verwachte datatypes",
|
|
24
|
+
"fr": "marwin79 - Plus de fonctionnalités prises en charge et convertir les valeurs en types de données attendus",
|
|
25
|
+
"it": "marwin79 - Altre funzionalità supportate e converte i valori nei tipi di dati previsti",
|
|
26
|
+
"es": "marwin79: se admiten más funciones y se convierten los valores en los tipos de datos esperados",
|
|
27
|
+
"pl": "marwin79 - Więcej funkcji obsługiwanych i konwertowanie wartości na oczekiwane typy danych",
|
|
28
|
+
"zh-cn": "marwin79 - 支持更多功能并将值转换为预期的数据类型"
|
|
29
|
+
},
|
|
30
|
+
"1.0.4": {
|
|
31
|
+
"en": "dynamicCircuitCurrentPX writeable (set all phases in 500ms) to limit ampere ",
|
|
32
|
+
"de": "dynamicCircuitCurrentPX beschreibbar (alle Phasen in 500ms einstellen) um Ampere zu begrenzen",
|
|
33
|
+
"ru": "dynamicCircuitCurrentPX с возможностью записи (установить все фазы в 500 мс) для ограничения силы тока",
|
|
34
|
+
"pt": "dynamicCircuitCurrentPX gravável (definir todas as fases em 500 ms) para limitar ampere",
|
|
35
|
+
"nl": "dynamicCircuitCurrentPX beschrijfbaar (stel alle fasen in op 500 ms) om ampère te beperken",
|
|
36
|
+
"fr": "dynamicCircuitCurrentPX inscriptible (définir toutes les phases en 500ms) pour limiter l'ampère",
|
|
37
|
+
"it": "dynamicCircuitCurrentPX scrivibile (imposta tutte le fasi in 500 ms) per limitare l'ampere",
|
|
38
|
+
"es": "dynamicCircuitCurrentPX escribible (establezca todas las fases en 500ms) para limitar los amperios",
|
|
39
|
+
"pl": "Możliwość zapisu dynamicCircuitCurrentPX (ustaw wszystkie fazy na 500 ms), aby ograniczyć ampery",
|
|
40
|
+
"zh-cn": "dynamicCircuitCurrentPX 可写(在 500 毫秒内设置所有阶段)以限制安培"
|
|
41
|
+
},
|
|
42
|
+
"1.0.3": {
|
|
43
|
+
"en": "Adapter crash fixed an other bugfixes",
|
|
44
|
+
"de": "Adapterabsturz behoben und weitere Bugfixes",
|
|
45
|
+
"ru": "Авария адаптера исправила другие исправления.",
|
|
46
|
+
"pt": "Falha do adaptador corrigida e outras correções de bugs",
|
|
47
|
+
"nl": "Adaptercrash heeft andere bugfixes opgelost",
|
|
48
|
+
"fr": "Le crash de l'adaptateur a corrigé une autre correction de bugs",
|
|
49
|
+
"it": "Il crash dell'adattatore ha risolto un altro bugfix",
|
|
50
|
+
"es": "La falla del adaptador corrigió otras correcciones de errores",
|
|
51
|
+
"pl": "Awaria adaptera naprawiono inne poprawki błędów",
|
|
52
|
+
"zh-cn": "适配器崩溃修复了其他错误修复"
|
|
53
|
+
},
|
|
6
54
|
"1.0.2": {
|
|
7
55
|
"en": "bugfix for the new stable version",
|
|
8
56
|
"de": "Bugfix für die neue stabile Version",
|
package/lib/enum.js
CHANGED
|
@@ -331,11 +331,16 @@ const easeeIds = {
|
|
|
331
331
|
36: '.config.wiFiSSID',
|
|
332
332
|
38: '.config.phaseMode',
|
|
333
333
|
40: '.config.ledStripBrightness',
|
|
334
|
+
44: '.config.smartButtonEnabled',
|
|
335
|
+
|
|
334
336
|
48: '.config.dynamicChargerCurrent',
|
|
335
337
|
|
|
336
338
|
//Status
|
|
339
|
+
46: '.status.ledMode',
|
|
337
340
|
68: '.status.wiFiAPEnabled',
|
|
338
341
|
82: '.status.chargerFirmware',
|
|
342
|
+
96: '.status.reasonForNoCurrent',
|
|
343
|
+
102: '.status.smartCharging',
|
|
339
344
|
103: '.status.cableLocked',
|
|
340
345
|
109: '.status.chargerOpMode',
|
|
341
346
|
111: '.status.dynamicCircuitCurrentP1',
|
|
@@ -353,8 +358,17 @@ const easeeIds = {
|
|
|
353
358
|
182: '.status.inCurrentT2',
|
|
354
359
|
183: '.status.inCurrentT3',
|
|
355
360
|
184: '.status.inCurrentT4',
|
|
356
|
-
|
|
357
|
-
|
|
361
|
+
185: '.status.inCurrentT5',
|
|
362
|
+
190: '.status.inVoltageT1T2',
|
|
363
|
+
191: '.status.inVoltageT1T3',
|
|
364
|
+
192: '.status.inVoltageT1T4',
|
|
365
|
+
193: '.status.inVoltageT1T5',
|
|
366
|
+
194: '.status.inVoltageT2T3',
|
|
367
|
+
195: '.status.inVoltageT2T4',
|
|
368
|
+
196: '.status.inVoltageT2T5',
|
|
369
|
+
197: '.status.inVoltageT3T4',
|
|
370
|
+
198: '.status.inVoltageT3T5',
|
|
371
|
+
199: '.status.inVoltageT4T5',
|
|
358
372
|
};
|
|
359
373
|
Object.freeze(easeeIds);
|
|
360
374
|
|
package/main.js
CHANGED
|
@@ -17,6 +17,12 @@ const minPollTimeEnergy = 120;
|
|
|
17
17
|
let roundCounter = 0;
|
|
18
18
|
const arrCharger = [];
|
|
19
19
|
|
|
20
|
+
//Variable für dynamicCircuitCurrentPX
|
|
21
|
+
let dynamicCircuitCurrentP1 = 0;
|
|
22
|
+
let dynamicCircuitCurrentP2 = 0;
|
|
23
|
+
let dynamicCircuitCurrentP3 = 0;
|
|
24
|
+
|
|
25
|
+
|
|
20
26
|
class Easee extends utils.Adapter {
|
|
21
27
|
|
|
22
28
|
/**
|
|
@@ -42,23 +48,36 @@ class Easee extends utils.Adapter {
|
|
|
42
48
|
.build();
|
|
43
49
|
|
|
44
50
|
connection.on('ProductUpdate', data => {
|
|
45
|
-
//haben einen neuen Wert über
|
|
46
|
-
|
|
51
|
+
//haben einen neuen Wert über SignalR erhalten
|
|
52
|
+
const data_name = objEnum.getNameByEnum(data.id);
|
|
53
|
+
if (data_name == undefined) {
|
|
47
54
|
this.log.debug('New SignalR-ID, possible new Value: ' + data.id);
|
|
48
55
|
this.log.debug(JSON.stringify(data));
|
|
49
56
|
} else {
|
|
50
57
|
//Value is in ioBroker, update it
|
|
51
|
-
const tmpValueId = data.mid +
|
|
58
|
+
const tmpValueId = data.mid + data_name;
|
|
52
59
|
this.log.info('New value over SignalR for: ' + tmpValueId + 'value: ' + data.value);
|
|
60
|
+
switch (data.dataType) {
|
|
61
|
+
case 2:
|
|
62
|
+
data.value = data.value == '1';
|
|
63
|
+
break;
|
|
64
|
+
case 3:
|
|
65
|
+
data.value = parseFloat(data.value);
|
|
66
|
+
break;
|
|
67
|
+
case 4:
|
|
68
|
+
data.value = parseInt(data.value);
|
|
69
|
+
break;
|
|
70
|
+
//case 6: JSON
|
|
71
|
+
}
|
|
53
72
|
this.setStateAsync(tmpValueId, { val: data.value, ack: true });
|
|
54
73
|
}
|
|
55
74
|
});
|
|
56
75
|
|
|
57
76
|
connection.start().then(() => {
|
|
58
77
|
//for each charger subscribe SignalR
|
|
59
|
-
arrCharger.forEach(
|
|
60
|
-
connection.send('SubscribeWithCurrentState',
|
|
61
|
-
this.log.info('Charger registrate in SignalR: ' +
|
|
78
|
+
arrCharger.forEach(charger_id => {
|
|
79
|
+
connection.send('SubscribeWithCurrentState', charger_id, true).then(() => {
|
|
80
|
+
this.log.info('Charger registrate in SignalR: ' + charger_id);
|
|
62
81
|
});
|
|
63
82
|
});
|
|
64
83
|
});
|
|
@@ -89,7 +108,7 @@ class Easee extends utils.Adapter {
|
|
|
89
108
|
} else if (this.config.client_secret == '') {
|
|
90
109
|
this.log.error('No password set');
|
|
91
110
|
} else {
|
|
92
|
-
this.log.
|
|
111
|
+
this.log.debug('Api login started');
|
|
93
112
|
this.log.debug('Password is:' + this.config.client_secret);
|
|
94
113
|
const login = await this.login(this.config.username, this.config.client_secret);
|
|
95
114
|
if (login) {
|
|
@@ -126,8 +145,9 @@ class Easee extends utils.Adapter {
|
|
|
126
145
|
onUnload(callback) {
|
|
127
146
|
try {
|
|
128
147
|
clearTimeout(adapterIntervals.readAllStates);
|
|
148
|
+
clearTimeout(adapterIntervals.updateDynamicCircuitCurrent);
|
|
129
149
|
this.log.info('Adaptor easee cleaned up everything...');
|
|
130
|
-
this.
|
|
150
|
+
this.setStateAsync('info.connection', false, true);
|
|
131
151
|
callback();
|
|
132
152
|
} catch (e) {
|
|
133
153
|
callback();
|
|
@@ -143,7 +163,7 @@ class Easee extends utils.Adapter {
|
|
|
143
163
|
await this.refreshToken();
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
this.log.
|
|
166
|
+
this.log.debug('read new states from the API');
|
|
147
167
|
|
|
148
168
|
//Lesen alle Charger aus
|
|
149
169
|
const tmpAllChargers = await this.getAllCharger();
|
|
@@ -161,25 +181,34 @@ class Easee extends utils.Adapter {
|
|
|
161
181
|
|
|
162
182
|
this.log.debug('Charger gefunden');
|
|
163
183
|
this.log.debug(JSON.stringify(charger));
|
|
184
|
+
try {
|
|
164
185
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
186
|
+
//Lesen den Status aus
|
|
187
|
+
const tmpChargerState = await this.getChargerState(charger.id);
|
|
188
|
+
//Lesen die config
|
|
189
|
+
const tmpChargerConfig = await this.getChargerConfig(charger.id);
|
|
169
190
|
|
|
170
|
-
|
|
171
|
-
|
|
191
|
+
//Setzen die Daten der Charger
|
|
192
|
+
await this.setNewStatusToCharger(charger, tmpChargerState);
|
|
172
193
|
|
|
173
|
-
|
|
174
|
-
|
|
194
|
+
//Setzen die Config zum Charger
|
|
195
|
+
await this.setConfigStatus(charger, tmpChargerConfig);
|
|
175
196
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
197
|
+
//setzen und erechnen der Energiedaten, aber gebremste
|
|
198
|
+
if(roundCounter > (minPollTimeEnergy/polltime)) {
|
|
199
|
+
//lesen der Energiedaten
|
|
200
|
+
const tmpChargerSession = await this.getChargerSession(charger.id);
|
|
201
|
+
//etzen die Objekte
|
|
202
|
+
this.setNewSessionToCharger(charger, tmpChargerSession);
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
if (typeof error === 'string') {
|
|
206
|
+
this.log.error(error);
|
|
207
|
+
} else if (error instanceof Error) {
|
|
208
|
+
this.log.error(error.message);
|
|
209
|
+
}
|
|
182
210
|
}
|
|
211
|
+
|
|
183
212
|
});
|
|
184
213
|
} else {
|
|
185
214
|
this.log.warn('No Chargers found!');
|
|
@@ -187,14 +216,14 @@ class Easee extends utils.Adapter {
|
|
|
187
216
|
|
|
188
217
|
//Energiedaten dürfen nur einmal in der Minute aufgerufen werden, daher müssen wir das bremsen
|
|
189
218
|
if(roundCounter > (minPollTimeEnergy/polltime)) {
|
|
190
|
-
this.log.
|
|
219
|
+
this.log.debug('Hole Energiedaten: ' + roundCounter);
|
|
191
220
|
roundCounter = 0;
|
|
192
221
|
}
|
|
193
222
|
//Zählen die Runde!
|
|
194
223
|
roundCounter = roundCounter + 1;
|
|
195
224
|
|
|
196
225
|
//Melden das Update
|
|
197
|
-
await this.setStateAsync('lastUpdate', new Date().toLocaleTimeString(),
|
|
226
|
+
await this.setStateAsync('lastUpdate', new Date().toLocaleTimeString(), true);
|
|
198
227
|
adapterIntervals.readAllStates = setTimeout(this.readAllStates.bind(this), polltime * 1000);
|
|
199
228
|
}
|
|
200
229
|
|
|
@@ -221,15 +250,45 @@ class Easee extends utils.Adapter {
|
|
|
221
250
|
this.log.debug('Get infos from site:');
|
|
222
251
|
this.log.debug(JSON.stringify(site));
|
|
223
252
|
|
|
224
|
-
this.
|
|
225
|
-
this.log.
|
|
253
|
+
this.changeMaxCircuitConfig(site.id, site.circuits[0].id, state.val);
|
|
254
|
+
this.log.debug('Changes send to API');
|
|
226
255
|
});
|
|
256
|
+
} else if (tmpControl[4] == 'dynamicCircuitCurrentP1' || tmpControl[4] == 'dynamicCircuitCurrentP2' || tmpControl[4] == 'dynamicCircuitCurrentP3') {
|
|
257
|
+
|
|
258
|
+
this.getChargerSite(tmpControl[2]).then( (site) => {
|
|
259
|
+
this.log.debug('Update dynamicCircuitCurrent to: ' + state.val);
|
|
260
|
+
this.log.debug('Get infos from site:');
|
|
261
|
+
this.log.debug(JSON.stringify(site));
|
|
262
|
+
|
|
263
|
+
//setze die WErte für das Update
|
|
264
|
+
switch(tmpControl[4]) {
|
|
265
|
+
case 'dynamicCircuitCurrentP1':
|
|
266
|
+
dynamicCircuitCurrentP1 = Number(state.val);
|
|
267
|
+
break;
|
|
268
|
+
case 'dynamicCircuitCurrentP2':
|
|
269
|
+
dynamicCircuitCurrentP2 = Number(state.val);
|
|
270
|
+
break;
|
|
271
|
+
case 'dynamicCircuitCurrentP3':
|
|
272
|
+
dynamicCircuitCurrentP3 = Number(state.val);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
//Warten mit dem Update 500ms um weitere Phasen zu setzen:
|
|
277
|
+
if (adapterIntervals.updateDynamicCircuitCurrent != null) {
|
|
278
|
+
clearTimeout(adapterIntervals.updateDynamicCircuitCurrent);
|
|
279
|
+
adapterIntervals.updateDynamicCircuitCurrent = null;
|
|
280
|
+
|
|
281
|
+
}
|
|
282
|
+
adapterIntervals.updateDynamicCircuitCurrent = setTimeout( async () => {
|
|
283
|
+
await this.changeCircuitConfig(site.id, site.circuits[0].id);
|
|
284
|
+
}, 500);
|
|
227
285
|
|
|
286
|
+
});
|
|
228
287
|
|
|
229
288
|
} else {
|
|
230
289
|
this.log.debug('update config to API: ' + id);
|
|
231
290
|
this.changeConfig(tmpControl[2], tmpControl[4], state.val);
|
|
232
|
-
this.log.
|
|
291
|
+
this.log.debug('Changes send to API');
|
|
233
292
|
}
|
|
234
293
|
}
|
|
235
294
|
} else {
|
|
@@ -283,31 +342,43 @@ class Easee extends utils.Adapter {
|
|
|
283
342
|
await this.setStateAsync(charger.id + '.status.wiFiRSSI', charger_states.wiFiRSSI, true);
|
|
284
343
|
await this.setStateAsync(charger.id + '.status.chargerFirmware', charger_states.chargerFirmware, true);
|
|
285
344
|
await this.setStateAsync(charger.id + '.status.latestFirmware', charger_states.latestFirmware, true);
|
|
345
|
+
await this.setStateAsync(charger.id + '.status.reasonForNoCurrent', charger_states.reasonForNoCurrent, true);
|
|
286
346
|
await this.setStateAsync(charger.id + '.status.voltage', charger_states.voltage, true);
|
|
287
347
|
await this.setStateAsync(charger.id + '.status.outputCurrent', charger_states.outputCurrent, true);
|
|
288
348
|
await this.setStateAsync(charger.id + '.status.isOnline', charger_states.isOnline, true);
|
|
289
349
|
await this.setStateAsync(charger.id + '.status.wiFiAPEnabled', charger_states.wiFiAPEnabled, true);
|
|
350
|
+
await this.setStateAsync(charger.id + '.status.ledMode', charger_states.ledMode, true);
|
|
290
351
|
await this.setStateAsync(charger.id + '.status.lifetimeEnergy', charger_states.lifetimeEnergy, true);
|
|
291
352
|
await this.setStateAsync(charger.id + '.status.energyPerHour', charger_states.energyPerHour, true);
|
|
292
353
|
await this.setStateAsync(charger.id + '.status.inCurrentT2', charger_states.inCurrentT2, true);
|
|
293
354
|
await this.setStateAsync(charger.id + '.status.inCurrentT3', charger_states.inCurrentT3, true);
|
|
294
355
|
await this.setStateAsync(charger.id + '.status.inCurrentT4', charger_states.inCurrentT4, true);
|
|
295
356
|
await this.setStateAsync(charger.id + '.status.inCurrentT5', charger_states.inCurrentT5, true);
|
|
357
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT1T2', charger_states.inVoltageT1T2, true);
|
|
358
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT1T3', charger_states.inVoltageT1T3, true);
|
|
359
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT1T4', charger_states.inVoltageT1T4, true);
|
|
360
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT1T5', charger_states.inVoltageT1T5, true);
|
|
361
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT2T3', charger_states.inVoltageT2T3, true);
|
|
362
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT2T4', charger_states.inVoltageT2T4, true);
|
|
363
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT2T5', charger_states.inVoltageT2T5, true);
|
|
364
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT3T4', charger_states.inVoltageT3T4, true);
|
|
365
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT3T5', charger_states.inVoltageT3T5, true);
|
|
366
|
+
await this.setStateAsync(charger.id + '.status.inVoltageT4T5', charger_states.inVoltageT4T5, true);
|
|
296
367
|
|
|
297
368
|
//wert der config wird nur hier gesendet
|
|
298
369
|
await this.setStateAsync(charger.id + '.config.dynamicChargerCurrent', { val: charger_states.dynamicChargerCurrent, ack: true });
|
|
299
370
|
await this.setStateAsync(charger.id + '.config.dynamicCircuitCurrentP1', { val: charger_states.dynamicCircuitCurrentP1, ack: true });
|
|
300
371
|
await this.setStateAsync(charger.id + '.config.dynamicCircuitCurrentP2', { val: charger_states.dynamicCircuitCurrentP2, ack: true });
|
|
301
372
|
await this.setStateAsync(charger.id + '.config.dynamicCircuitCurrentP3', { val: charger_states.dynamicCircuitCurrentP3, ack: true });
|
|
302
|
-
|
|
373
|
+
await this.setStateAsync(charger.id + '.config.smartCharging', charger_states.smartCharging, true);
|
|
303
374
|
}
|
|
304
375
|
|
|
305
|
-
|
|
306
376
|
//Setzen alle Status für Config
|
|
307
377
|
async setConfigStatus(charger, charger_config) {
|
|
308
378
|
await this.setStateAsync(charger.id + '.config.isEnabled',{ val: charger_config.isEnabled, ack: true } );
|
|
309
379
|
await this.setStateAsync(charger.id + '.config.phaseMode', { val: charger_config.phaseMode, ack: true });
|
|
310
380
|
await this.setStateAsync(charger.id + '.config.ledStripBrightness', { val: charger_config.ledStripBrightness, ack: true });
|
|
381
|
+
await this.setStateAsync(charger.id + '.config.smartButtonEnabled', { val: charger_config.smartButtonEnabled, ack: true });
|
|
311
382
|
await this.setStateAsync(charger.id + '.config.wiFiSSID', { val: charger_config.wiFiSSID, ack: true });
|
|
312
383
|
await this.setStateAsync(charger.id + '.config.maxChargerCurrent', { val: charger_config.maxChargerCurrent, ack: true });
|
|
313
384
|
|
|
@@ -315,8 +386,6 @@ class Easee extends utils.Adapter {
|
|
|
315
386
|
await this.setStateAsync(charger.id + '.config.circuitMaxCurrentP1', { val: charger_config.circuitMaxCurrentP1, ack: true });
|
|
316
387
|
await this.setStateAsync(charger.id + '.config.circuitMaxCurrentP2', { val: charger_config.circuitMaxCurrentP3, ack: true });
|
|
317
388
|
await this.setStateAsync(charger.id + '.config.circuitMaxCurrentP3', { val: charger_config.circuitMaxCurrentP3, ack: true });
|
|
318
|
-
|
|
319
|
-
|
|
320
389
|
}
|
|
321
390
|
|
|
322
391
|
/*************************************************************************
|
|
@@ -333,7 +402,7 @@ class Easee extends utils.Adapter {
|
|
|
333
402
|
password: password
|
|
334
403
|
});
|
|
335
404
|
|
|
336
|
-
this.log.info('Login successful');
|
|
405
|
+
this.log.info('Easee Api Login successful');
|
|
337
406
|
|
|
338
407
|
accessToken = response.data.accessToken;
|
|
339
408
|
refreshToken = response.data.refreshToken;
|
|
@@ -343,7 +412,11 @@ class Easee extends utils.Adapter {
|
|
|
343
412
|
return true;
|
|
344
413
|
} catch (error) {
|
|
345
414
|
this.log.error('Api login error - check Username and password');
|
|
346
|
-
|
|
415
|
+
if (typeof error === 'string') {
|
|
416
|
+
this.log.error(error);
|
|
417
|
+
} else if (error instanceof Error) {
|
|
418
|
+
this.log.error(error.message);
|
|
419
|
+
}
|
|
347
420
|
await this.setStateAsync('info.connection', false, true);
|
|
348
421
|
return false;
|
|
349
422
|
}
|
|
@@ -390,6 +463,7 @@ class Easee extends utils.Adapter {
|
|
|
390
463
|
return response.data;
|
|
391
464
|
}).catch((error) => {
|
|
392
465
|
this.log.error(error);
|
|
466
|
+
throw new Error('Easee API error on charger state - stop refresh');
|
|
393
467
|
});
|
|
394
468
|
}
|
|
395
469
|
|
|
@@ -402,6 +476,7 @@ class Easee extends utils.Adapter {
|
|
|
402
476
|
return response.data;
|
|
403
477
|
}).catch((error) => {
|
|
404
478
|
this.log.error(error);
|
|
479
|
+
throw new Error('Easee API error on charger config - stop refresh');
|
|
405
480
|
});
|
|
406
481
|
}
|
|
407
482
|
|
|
@@ -414,6 +489,7 @@ class Easee extends utils.Adapter {
|
|
|
414
489
|
return response.data;
|
|
415
490
|
}).catch((error) => {
|
|
416
491
|
this.log.error(error);
|
|
492
|
+
throw new Error('Easee API error on charger site - stop refresh');
|
|
417
493
|
});
|
|
418
494
|
}
|
|
419
495
|
|
|
@@ -426,6 +502,7 @@ class Easee extends utils.Adapter {
|
|
|
426
502
|
return response.data;
|
|
427
503
|
}).catch((error) => {
|
|
428
504
|
this.log.error(error);
|
|
505
|
+
throw new Error('Easee API error on charger session - stop refresh');
|
|
429
506
|
});
|
|
430
507
|
}
|
|
431
508
|
|
|
@@ -508,13 +585,32 @@ class Easee extends utils.Adapter {
|
|
|
508
585
|
}
|
|
509
586
|
|
|
510
587
|
//circuitMaxCurrentPX
|
|
511
|
-
async
|
|
588
|
+
async changeMaxCircuitConfig(site_id, circuit_id, value) {
|
|
512
589
|
return await axios.post(apiUrl + '/api/sites/' + site_id + '/circuits/' + circuit_id + '/settings', {
|
|
513
590
|
'maxCircuitCurrentP1': value,
|
|
514
591
|
'maxCircuitCurrentP2': value,
|
|
515
592
|
'maxCircuitCurrentP3': value,
|
|
516
593
|
},
|
|
517
594
|
{ headers: {'Authorization' : `Bearer ${accessToken}`}}
|
|
595
|
+
).then(response => {
|
|
596
|
+
this.log.info('CircuitMax update successful');
|
|
597
|
+
this.log.debug(JSON.stringify(response.data));
|
|
598
|
+
}).catch((error) => {
|
|
599
|
+
this.log.error('CircuitMax update error');
|
|
600
|
+
this.log.error(error);
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
//dynamicCircuitCurrentPX
|
|
605
|
+
async changeCircuitConfig(site_id, circuit_id) {
|
|
606
|
+
|
|
607
|
+
//Der Wert darf nur für 3 Fach Wwrte aktualisiert werden
|
|
608
|
+
await axios.post(apiUrl + '/api/sites/' + site_id + '/circuits/' + circuit_id + '/settings', {
|
|
609
|
+
'dynamicCircuitCurrentP1': dynamicCircuitCurrentP1,
|
|
610
|
+
'dynamicCircuitCurrentP2': dynamicCircuitCurrentP2,
|
|
611
|
+
'dynamicCircuitCurrentP3': dynamicCircuitCurrentP3
|
|
612
|
+
},
|
|
613
|
+
{ headers: {'Authorization' : `Bearer ${accessToken}`}}
|
|
518
614
|
).then(response => {
|
|
519
615
|
this.log.info('Circuit update successful');
|
|
520
616
|
this.log.debug(JSON.stringify(response.data));
|
|
@@ -522,8 +618,16 @@ class Easee extends utils.Adapter {
|
|
|
522
618
|
this.log.error('Circuit update error');
|
|
523
619
|
this.log.error(error);
|
|
524
620
|
});
|
|
621
|
+
|
|
622
|
+
//setze Werte zurück
|
|
623
|
+
adapterIntervals.updateDynamicCircuitCurrent = null;
|
|
624
|
+
dynamicCircuitCurrentP1 = 0;
|
|
625
|
+
dynamicCircuitCurrentP2 = 0;
|
|
626
|
+
dynamicCircuitCurrentP3 = 0;
|
|
627
|
+
|
|
525
628
|
}
|
|
526
629
|
|
|
630
|
+
|
|
527
631
|
/***********************************************************************
|
|
528
632
|
* Funktionen zum erstellen der Objekte der Reading
|
|
529
633
|
***********************************************************************/
|
|
@@ -607,7 +711,7 @@ class Easee extends utils.Adapter {
|
|
|
607
711
|
},
|
|
608
712
|
native: {},
|
|
609
713
|
});
|
|
610
|
-
await this.setStateAsync(charger.id + '.id', charger.id);
|
|
714
|
+
await this.setStateAsync(charger.id + '.id', charger.id, true);
|
|
611
715
|
|
|
612
716
|
//name
|
|
613
717
|
await this.setObjectNotExistsAsync(charger.id + '.name', {
|
|
@@ -700,6 +804,19 @@ class Easee extends utils.Adapter {
|
|
|
700
804
|
native: {},
|
|
701
805
|
});
|
|
702
806
|
|
|
807
|
+
//"reasonForNoCurrent": 0,
|
|
808
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.reasonForNoCurrent', {
|
|
809
|
+
type: 'state',
|
|
810
|
+
common: {
|
|
811
|
+
name: 'Reason for not offering current to the car',
|
|
812
|
+
type: 'number',
|
|
813
|
+
role: 'value',
|
|
814
|
+
read: true,
|
|
815
|
+
write: false,
|
|
816
|
+
},
|
|
817
|
+
native: {},
|
|
818
|
+
});
|
|
819
|
+
|
|
703
820
|
//"voltage": 0,
|
|
704
821
|
await this.setObjectNotExistsAsync(charger.id + '.status.voltage', {
|
|
705
822
|
type: 'state',
|
|
@@ -778,6 +895,127 @@ class Easee extends utils.Adapter {
|
|
|
778
895
|
native: {},
|
|
779
896
|
});
|
|
780
897
|
|
|
898
|
+
//"inVoltageT1T2": 0,
|
|
899
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT1T2', {
|
|
900
|
+
type: 'state',
|
|
901
|
+
common: {
|
|
902
|
+
name: 'Current Voltage for between inputs T1 and T2 [Volts]',
|
|
903
|
+
type: 'number',
|
|
904
|
+
role: 'value.voltage',
|
|
905
|
+
read: true,
|
|
906
|
+
write: false,
|
|
907
|
+
},
|
|
908
|
+
native: {},
|
|
909
|
+
});
|
|
910
|
+
//"inVoltageT1T3": 0,
|
|
911
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT1T3', {
|
|
912
|
+
type: 'state',
|
|
913
|
+
common: {
|
|
914
|
+
name: 'Current Voltage for between inputs T1 and T3 [Volts]',
|
|
915
|
+
type: 'number',
|
|
916
|
+
role: 'value.voltage',
|
|
917
|
+
read: true,
|
|
918
|
+
write: false,
|
|
919
|
+
},
|
|
920
|
+
native: {},
|
|
921
|
+
});
|
|
922
|
+
//"inVoltageT1T4": 0,
|
|
923
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT1T4', {
|
|
924
|
+
type: 'state',
|
|
925
|
+
common: {
|
|
926
|
+
name: 'Current Voltage for between inputs T1 and T4 [Volts]',
|
|
927
|
+
type: 'number',
|
|
928
|
+
role: 'value.voltage',
|
|
929
|
+
read: true,
|
|
930
|
+
write: false,
|
|
931
|
+
},
|
|
932
|
+
native: {},
|
|
933
|
+
});
|
|
934
|
+
//"inVoltageT1T5": 0,
|
|
935
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT1T5', {
|
|
936
|
+
type: 'state',
|
|
937
|
+
common: {
|
|
938
|
+
name: 'Current Voltage for between inputs T1 and T5 [Volts]',
|
|
939
|
+
type: 'number',
|
|
940
|
+
role: 'value.voltage',
|
|
941
|
+
read: true,
|
|
942
|
+
write: false,
|
|
943
|
+
},
|
|
944
|
+
native: {},
|
|
945
|
+
});
|
|
946
|
+
//"inVoltageT2T3": 0,
|
|
947
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT2T3', {
|
|
948
|
+
type: 'state',
|
|
949
|
+
common: {
|
|
950
|
+
name: 'Current Voltage for between inputs T2 and T3 [Volts]',
|
|
951
|
+
type: 'number',
|
|
952
|
+
role: 'value.voltage',
|
|
953
|
+
read: true,
|
|
954
|
+
write: false,
|
|
955
|
+
},
|
|
956
|
+
native: {},
|
|
957
|
+
});
|
|
958
|
+
//"inVoltageT2T4": 0,
|
|
959
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT2T4', {
|
|
960
|
+
type: 'state',
|
|
961
|
+
common: {
|
|
962
|
+
name: 'Current Voltage for between inputs T2 and T4 [Volts]',
|
|
963
|
+
type: 'number',
|
|
964
|
+
role: 'value.voltage',
|
|
965
|
+
read: true,
|
|
966
|
+
write: false,
|
|
967
|
+
},
|
|
968
|
+
native: {},
|
|
969
|
+
});
|
|
970
|
+
//"inVoltageT2T5": 0,
|
|
971
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT2T5', {
|
|
972
|
+
type: 'state',
|
|
973
|
+
common: {
|
|
974
|
+
name: 'Current Voltage for between inputs T2 and T5 [Volts]',
|
|
975
|
+
type: 'number',
|
|
976
|
+
role: 'value.voltage',
|
|
977
|
+
read: true,
|
|
978
|
+
write: false,
|
|
979
|
+
},
|
|
980
|
+
native: {},
|
|
981
|
+
});
|
|
982
|
+
//"inVoltageT3T4": 0,
|
|
983
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT3T4', {
|
|
984
|
+
type: 'state',
|
|
985
|
+
common: {
|
|
986
|
+
name: 'Current Voltage for between inputs T3 and T4 [Volts]',
|
|
987
|
+
type: 'number',
|
|
988
|
+
role: 'value.voltage',
|
|
989
|
+
read: true,
|
|
990
|
+
write: false,
|
|
991
|
+
},
|
|
992
|
+
native: {},
|
|
993
|
+
});
|
|
994
|
+
//"inVoltageT3T5": 0,
|
|
995
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT3T5', {
|
|
996
|
+
type: 'state',
|
|
997
|
+
common: {
|
|
998
|
+
name: 'Current Voltage for between inputs T3 and T5 [Volts]',
|
|
999
|
+
type: 'number',
|
|
1000
|
+
role: 'value.voltage',
|
|
1001
|
+
read: true,
|
|
1002
|
+
write: false,
|
|
1003
|
+
},
|
|
1004
|
+
native: {},
|
|
1005
|
+
});
|
|
1006
|
+
//"inVoltageT4T5": 0,
|
|
1007
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.inVoltageT4T5', {
|
|
1008
|
+
type: 'state',
|
|
1009
|
+
common: {
|
|
1010
|
+
name: 'Current Voltage for between inputs T4 and T5 [Volts]',
|
|
1011
|
+
type: 'number',
|
|
1012
|
+
role: 'value.voltage',
|
|
1013
|
+
read: true,
|
|
1014
|
+
write: false,
|
|
1015
|
+
},
|
|
1016
|
+
native: {},
|
|
1017
|
+
});
|
|
1018
|
+
|
|
781
1019
|
//"isOnline": true,
|
|
782
1020
|
await this.setObjectNotExistsAsync(charger.id + '.status.isOnline', {
|
|
783
1021
|
type: 'state',
|
|
@@ -804,6 +1042,19 @@ class Easee extends utils.Adapter {
|
|
|
804
1042
|
native: {},
|
|
805
1043
|
});
|
|
806
1044
|
|
|
1045
|
+
//"ledMode": true,
|
|
1046
|
+
await this.setObjectNotExistsAsync(charger.id + '.status.ledMode', {
|
|
1047
|
+
type: 'state',
|
|
1048
|
+
common: {
|
|
1049
|
+
name: 'Charger LED mode',
|
|
1050
|
+
type: 'number',
|
|
1051
|
+
role: 'value',
|
|
1052
|
+
read: true,
|
|
1053
|
+
write: false,
|
|
1054
|
+
},
|
|
1055
|
+
native: {},
|
|
1056
|
+
});
|
|
1057
|
+
|
|
807
1058
|
//"lifetimeEnergy": 0,
|
|
808
1059
|
await this.setObjectNotExistsAsync(charger.id + '.status.lifetimeEnergy', {
|
|
809
1060
|
type: 'state',
|
|
@@ -877,7 +1128,7 @@ class Easee extends utils.Adapter {
|
|
|
877
1128
|
},
|
|
878
1129
|
native: {},
|
|
879
1130
|
});
|
|
880
|
-
await this.setStateAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.totalEnergyUsage', session.totalEnergyUsage);
|
|
1131
|
+
await this.setStateAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.totalEnergyUsage', session.totalEnergyUsage, true);
|
|
881
1132
|
|
|
882
1133
|
await this.setObjectNotExistsAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.totalCost', {
|
|
883
1134
|
type: 'state',
|
|
@@ -890,7 +1141,7 @@ class Easee extends utils.Adapter {
|
|
|
890
1141
|
},
|
|
891
1142
|
native: {},
|
|
892
1143
|
});
|
|
893
|
-
await this.setStateAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.totalCost', session.totalCost);
|
|
1144
|
+
await this.setStateAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.totalCost', session.totalCost, true);
|
|
894
1145
|
|
|
895
1146
|
await this.setObjectNotExistsAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.currencyId', {
|
|
896
1147
|
type: 'state',
|
|
@@ -903,7 +1154,7 @@ class Easee extends utils.Adapter {
|
|
|
903
1154
|
},
|
|
904
1155
|
native: {},
|
|
905
1156
|
});
|
|
906
|
-
await this.setStateAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.currencyId', session.currencyId);
|
|
1157
|
+
await this.setStateAsync(charger.id + '.session.' + session.year + '.' + session.month+ '.currencyId', session.currencyId, true);
|
|
907
1158
|
|
|
908
1159
|
await this.setObjectNotExistsAsync(charger.id + '.session.' + session.year + '.total_year', {
|
|
909
1160
|
type: 'state',
|
|
@@ -924,15 +1175,15 @@ class Easee extends utils.Adapter {
|
|
|
924
1175
|
let tmpYearCount = 0;
|
|
925
1176
|
charger_session.forEach(session => {
|
|
926
1177
|
//Jahreszähler umhängen
|
|
927
|
-
this.log.
|
|
1178
|
+
this.log.debug('set session year data');
|
|
928
1179
|
if (tmpYear != session.year) {
|
|
929
1180
|
//neues Jahr setzen alles zurück
|
|
930
|
-
this.
|
|
1181
|
+
this.setStateAsync(charger.id + '.session.' + session.year + '.total_year', session.totalEnergyUsage, true);
|
|
931
1182
|
tmpYearCount = session.totalEnergyUsage;
|
|
932
1183
|
tmpYear = session.year;
|
|
933
1184
|
} else {
|
|
934
1185
|
tmpYearCount = tmpYearCount + session.totalEnergyUsage;
|
|
935
|
-
this.
|
|
1186
|
+
this.setStateAsync(charger.id + '.session.' + session.year + '.total_year', tmpYearCount, true);
|
|
936
1187
|
}
|
|
937
1188
|
});
|
|
938
1189
|
|
|
@@ -1005,11 +1256,11 @@ class Easee extends utils.Adapter {
|
|
|
1005
1256
|
type: 'number',
|
|
1006
1257
|
role: 'value.current',
|
|
1007
1258
|
read: true,
|
|
1008
|
-
write:
|
|
1259
|
+
write: true,
|
|
1009
1260
|
},
|
|
1010
1261
|
native: {},
|
|
1011
1262
|
});
|
|
1012
|
-
|
|
1263
|
+
this.subscribeStates(charger.id + '.config.dynamicCircuitCurrentP1');
|
|
1013
1264
|
|
|
1014
1265
|
await this.setObjectNotExistsAsync(charger.id + '.config.dynamicCircuitCurrentP2', {
|
|
1015
1266
|
type: 'state',
|
|
@@ -1018,11 +1269,11 @@ class Easee extends utils.Adapter {
|
|
|
1018
1269
|
type: 'number',
|
|
1019
1270
|
role: 'value.current',
|
|
1020
1271
|
read: true,
|
|
1021
|
-
write:
|
|
1272
|
+
write: true,
|
|
1022
1273
|
},
|
|
1023
1274
|
native: {},
|
|
1024
1275
|
});
|
|
1025
|
-
|
|
1276
|
+
this.subscribeStates(charger.id + '.config.dynamicCircuitCurrentP2');
|
|
1026
1277
|
|
|
1027
1278
|
await this.setObjectNotExistsAsync(charger.id + '.config.dynamicCircuitCurrentP3', {
|
|
1028
1279
|
type: 'state',
|
|
@@ -1031,7 +1282,7 @@ class Easee extends utils.Adapter {
|
|
|
1031
1282
|
type: 'number',
|
|
1032
1283
|
role: 'value.current',
|
|
1033
1284
|
read: true,
|
|
1034
|
-
write:
|
|
1285
|
+
write: true,
|
|
1035
1286
|
},
|
|
1036
1287
|
native: {},
|
|
1037
1288
|
});
|
|
@@ -1090,6 +1341,33 @@ class Easee extends utils.Adapter {
|
|
|
1090
1341
|
});
|
|
1091
1342
|
this.subscribeStates(charger.id + '.config.ledStripBrightness');
|
|
1092
1343
|
|
|
1344
|
+
//"smartCharging": true,
|
|
1345
|
+
await this.setObjectNotExistsAsync(charger.id + '.config.smartCharging', {
|
|
1346
|
+
type: 'state',
|
|
1347
|
+
common: {
|
|
1348
|
+
name: 'Smart charging state enabled by capacitive touch button',
|
|
1349
|
+
type: 'boolean',
|
|
1350
|
+
role: 'value',
|
|
1351
|
+
read: true,
|
|
1352
|
+
write: true,
|
|
1353
|
+
},
|
|
1354
|
+
native: {},
|
|
1355
|
+
});
|
|
1356
|
+
this.subscribeStates(charger.id + '.config.smartCharging');
|
|
1357
|
+
|
|
1358
|
+
//smartButtonEnabled
|
|
1359
|
+
await this.setObjectNotExistsAsync(charger.id + '.config.smartButtonEnabled', {
|
|
1360
|
+
type: 'state',
|
|
1361
|
+
common: {
|
|
1362
|
+
name: 'Smart Button Enabled/Disabled',
|
|
1363
|
+
type: 'boolean',
|
|
1364
|
+
role: 'indicator.state',
|
|
1365
|
+
read: true,
|
|
1366
|
+
write: true,
|
|
1367
|
+
},
|
|
1368
|
+
native: {},
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1093
1371
|
//wiFiSSID
|
|
1094
1372
|
await this.setObjectNotExistsAsync(charger.id + '.config.wiFiSSID', {
|
|
1095
1373
|
type: 'state',
|
|
@@ -1104,6 +1382,7 @@ class Easee extends utils.Adapter {
|
|
|
1104
1382
|
});
|
|
1105
1383
|
this.subscribeStates(charger.id + '.config.wiFiSSID');
|
|
1106
1384
|
|
|
1385
|
+
|
|
1107
1386
|
}
|
|
1108
1387
|
}
|
|
1109
1388
|
|
|
@@ -1119,6 +1398,3 @@ if (module.parent) {
|
|
|
1119
1398
|
new Easee();
|
|
1120
1399
|
}
|
|
1121
1400
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.easee",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Adapter to connect Easee Wallbox",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jens Nachtigal",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"axios": "^0.21.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@iobroker/testing": "^2.
|
|
24
|
+
"@iobroker/testing": "^2.5.2",
|
|
25
25
|
"@types/chai": "^4.2.14",
|
|
26
26
|
"@types/chai-as-promised": "^7.1.3",
|
|
27
27
|
"@types/gulp": "^4.0.8",
|