iobroker.smartloadmanager 0.0.6 → 0.0.8
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 +22 -303
- package/admin/i18n/de/translations.json +85 -11
- package/admin/i18n/en/translations.json +75 -3
- package/admin/i18n/es/translations.json +75 -1
- package/admin/i18n/fr/translations.json +75 -1
- package/admin/i18n/it/translations.json +75 -1
- package/admin/i18n/nl/translations.json +75 -1
- package/admin/i18n/pl/translations.json +75 -1
- package/admin/i18n/pt/translations.json +75 -1
- package/admin/i18n/ru/translations.json +75 -1
- package/admin/i18n/uk/translations.json +75 -1
- package/admin/i18n/zh-cn/translations.json +75 -1
- package/admin/jsonConfig.json +46 -370
- package/io-package.json +40 -42
- package/main.js +262 -157
- package/package.json +6 -17
package/main.js
CHANGED
|
@@ -8,6 +8,9 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
8
8
|
...options,
|
|
9
9
|
name: 'smartloadmanager',
|
|
10
10
|
});
|
|
11
|
+
// Timeout-Limits definieren
|
|
12
|
+
this.MAX_TIMEOUT = 86400000; // 24h in ms
|
|
13
|
+
this.DEFAULT_TIMEOUT = 60000; // 60 Sekunden Fallback
|
|
11
14
|
this.consumerList = [];
|
|
12
15
|
this.feedInDatapoint = null;
|
|
13
16
|
this.batteryControlModeDatapoint = null;
|
|
@@ -19,15 +22,40 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
19
22
|
this.on('stateChange', this.onStateChange.bind(this));
|
|
20
23
|
this.on('unload', this.onUnload.bind(this));
|
|
21
24
|
}
|
|
25
|
+
name2id(pName) {
|
|
26
|
+
return (pName || '')
|
|
27
|
+
.replace(this.FORBIDDEN_CHARS, '_') // ioBroker verbotene Zeichen
|
|
28
|
+
.replace(/\s+/g, '_') // Leerzeichen → _
|
|
29
|
+
.replace(/\./g, '_') // Punkte → _
|
|
30
|
+
.replace(/[^a-zA-Z0-9\-_]/g, '_') // nur erlaubte Zeichen
|
|
31
|
+
.replace(/_+/g, '_') // mehrere Unterstriche zusammenfassen
|
|
32
|
+
.replace(/^_+|_+$/g, ''); // führende/trailing _ weg
|
|
33
|
+
}
|
|
34
|
+
// Wrapper für sichere Timer
|
|
35
|
+
safeSetTimeout(fn, ms, ...args) {
|
|
36
|
+
const safeMs = Math.min(Math.max(ms, 0), this.MAX_TIMEOUT);
|
|
37
|
+
if (ms > this.MAX_TIMEOUT) {
|
|
38
|
+
this.log.warn(`Timeout of ${ms} ms has been limited to ${this.MAX_TIMEOUT} ms!`);
|
|
39
|
+
}
|
|
40
|
+
return this.setTimeout(fn, safeMs, ...args);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
safeSetInterval(fn, ms, ...args) {
|
|
44
|
+
const safeMs = Math.min(Math.max(ms, 0), this.MAX_TIMEOUT);
|
|
45
|
+
if (ms > this.MAX_TIMEOUT) {
|
|
46
|
+
this.log.warn(`Interval of ${ms} ms has been limited to ${this.MAX_TIMEOUT} ms!`);
|
|
47
|
+
}
|
|
48
|
+
return this.setInterval(fn, safeMs, ...args);
|
|
49
|
+
}
|
|
22
50
|
|
|
23
51
|
async onReady() {
|
|
24
52
|
try {
|
|
25
|
-
this.log.info(
|
|
53
|
+
this.log.info(`Adapter started, PID: ${process.pid}`);
|
|
26
54
|
|
|
27
55
|
if (!this.config.FeedInDataPoint) {
|
|
28
56
|
this.log.error('No FeedInDataPoint configured!');
|
|
29
|
-
//
|
|
30
|
-
await this.sendNotification('smartloadmanager', 'alarm', '
|
|
57
|
+
// additionally send alert
|
|
58
|
+
await this.sendNotification('smartloadmanager', 'alarm', 'No FeedInDataPoint configured!');
|
|
31
59
|
return;
|
|
32
60
|
}
|
|
33
61
|
this.feedInDatapoint = this.config.FeedInDataPoint;
|
|
@@ -58,20 +86,10 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
58
86
|
}
|
|
59
87
|
|
|
60
88
|
await this.checkConsumers();
|
|
61
|
-
|
|
62
|
-
/* // Info beim erfolgreichen Start
|
|
63
|
-
if (!this.adapterStartedNotified) {
|
|
64
|
-
await this.sendNotification(
|
|
65
|
-
'smartloadmanager',
|
|
66
|
-
'info',
|
|
67
|
-
'ℹ️ SmartLoadManager Adapter erfolgreich gestartet.',
|
|
68
|
-
);
|
|
69
|
-
this.adapterStartedNotified = true;
|
|
70
|
-
} */
|
|
71
89
|
} catch (error) {
|
|
72
90
|
this.log.error(`Error in onReady: ${error.message}`);
|
|
73
|
-
//
|
|
74
|
-
await this.sendNotification('smartloadmanager', 'alert',
|
|
91
|
+
// additionally send alert
|
|
92
|
+
await this.sendNotification('smartloadmanager', 'alert', `Error in onReady: ${error.message}`);
|
|
75
93
|
}
|
|
76
94
|
}
|
|
77
95
|
|
|
@@ -93,20 +111,30 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
async createConsumerStates() {
|
|
114
|
+
// Übergeordnetes Device erstellen - löst "intermediate objects missing" Problem
|
|
115
|
+
await this.setObjectNotExistsAsync('consumer', {
|
|
116
|
+
type: 'device',
|
|
117
|
+
common: {
|
|
118
|
+
name: 'Consumer Management',
|
|
119
|
+
},
|
|
120
|
+
native: {},
|
|
121
|
+
});
|
|
122
|
+
|
|
96
123
|
for (let i = 0; i < this.consumerList.length; i++) {
|
|
97
124
|
const v = this.consumerList[i];
|
|
98
|
-
const
|
|
125
|
+
const safeName = this.name2id(v.name);
|
|
126
|
+
const channelId = `consumer.${i}_${safeName}`;
|
|
99
127
|
|
|
100
128
|
await this.setObjectNotExistsAsync(channelId, {
|
|
101
129
|
type: 'channel',
|
|
102
|
-
common: { name: v.name },
|
|
130
|
+
common: { name: v.name }, // Channel selbst darf Klartext-Nutzername behalten
|
|
103
131
|
native: {},
|
|
104
132
|
});
|
|
105
133
|
|
|
106
134
|
await this.setObjectNotExistsAsync(`${channelId}.controlMode`, {
|
|
107
135
|
type: 'state',
|
|
108
136
|
common: {
|
|
109
|
-
name:
|
|
137
|
+
name: 'controlMode',
|
|
110
138
|
type: 'number',
|
|
111
139
|
role: 'level.mode',
|
|
112
140
|
read: true,
|
|
@@ -117,12 +145,13 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
117
145
|
native: {},
|
|
118
146
|
});
|
|
119
147
|
|
|
148
|
+
// KORRIGIERT: switchOnTime und switchOffTime als text statt value.time
|
|
120
149
|
await this.setObjectNotExistsAsync(`${channelId}.switchOnTime`, {
|
|
121
150
|
type: 'state',
|
|
122
151
|
common: {
|
|
123
|
-
name:
|
|
152
|
+
name: 'switchOnTime',
|
|
124
153
|
type: 'string',
|
|
125
|
-
role: 'value.time'
|
|
154
|
+
role: 'text', // GEÄNDERT von 'value.time' zu 'text'
|
|
126
155
|
read: true,
|
|
127
156
|
write: true,
|
|
128
157
|
def: '',
|
|
@@ -133,9 +162,9 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
133
162
|
await this.setObjectNotExistsAsync(`${channelId}.switchOffTime`, {
|
|
134
163
|
type: 'state',
|
|
135
164
|
common: {
|
|
136
|
-
name:
|
|
165
|
+
name: 'switchOffTime',
|
|
137
166
|
type: 'string',
|
|
138
|
-
role: 'value.time'
|
|
167
|
+
role: 'text', // GEÄNDERT von 'value.time' zu 'text'
|
|
139
168
|
read: true,
|
|
140
169
|
write: true,
|
|
141
170
|
def: '',
|
|
@@ -146,7 +175,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
146
175
|
await this.setObjectNotExistsAsync(`${channelId}.alwaysOffAtTime`, {
|
|
147
176
|
type: 'state',
|
|
148
177
|
common: {
|
|
149
|
-
name:
|
|
178
|
+
name: 'alwaysOffAtTime',
|
|
150
179
|
type: 'boolean',
|
|
151
180
|
role: 'switch',
|
|
152
181
|
read: true,
|
|
@@ -159,9 +188,9 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
159
188
|
await this.setObjectNotExistsAsync(`${channelId}.performance`, {
|
|
160
189
|
type: 'state',
|
|
161
190
|
common: {
|
|
162
|
-
name:
|
|
191
|
+
name: 'performance',
|
|
163
192
|
type: 'number',
|
|
164
|
-
role: 'value.power.consumption'
|
|
193
|
+
role: 'value.power', // GEÄNDERT von 'value.power.consumption' zu 'value.power'
|
|
165
194
|
read: true,
|
|
166
195
|
write: false,
|
|
167
196
|
def: v.performance || 0,
|
|
@@ -173,7 +202,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
173
202
|
await this.setObjectNotExistsAsync(`${channelId}.switchOnPoint`, {
|
|
174
203
|
type: 'state',
|
|
175
204
|
common: {
|
|
176
|
-
name:
|
|
205
|
+
name: 'switchOnPoint',
|
|
177
206
|
type: 'number',
|
|
178
207
|
role: 'value.power',
|
|
179
208
|
read: true,
|
|
@@ -187,7 +216,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
187
216
|
await this.setObjectNotExistsAsync(`${channelId}.switchOffPoint`, {
|
|
188
217
|
type: 'state',
|
|
189
218
|
common: {
|
|
190
|
-
name:
|
|
219
|
+
name: 'switchOffPoint',
|
|
191
220
|
type: 'number',
|
|
192
221
|
role: 'value.power',
|
|
193
222
|
read: true,
|
|
@@ -202,9 +231,9 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
202
231
|
await this.setObjectNotExistsAsync(`${channelId}.batterySetpoint`, {
|
|
203
232
|
type: 'state',
|
|
204
233
|
common: {
|
|
205
|
-
name:
|
|
234
|
+
name: 'batterySetpoint',
|
|
206
235
|
type: 'string',
|
|
207
|
-
role: 'value'
|
|
236
|
+
role: 'text', // GEÄNDERT von 'value' zu 'text' (spezifischer)
|
|
208
237
|
read: true,
|
|
209
238
|
write: true,
|
|
210
239
|
def: v.batterySetpoint || '',
|
|
@@ -213,12 +242,13 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
213
242
|
});
|
|
214
243
|
}
|
|
215
244
|
|
|
245
|
+
// KORRIGIERT: value.duration ist korrekt für Timer
|
|
216
246
|
await this.setObjectNotExistsAsync(`${channelId}.onTimerRemaining`, {
|
|
217
247
|
type: 'state',
|
|
218
248
|
common: {
|
|
219
|
-
name:
|
|
249
|
+
name: 'onTimerRemaining',
|
|
220
250
|
type: 'number',
|
|
221
|
-
role: 'value.duration'
|
|
251
|
+
role: 'value.interval', // GEÄNDERT von 'value.duration' zu 'value.interval'
|
|
222
252
|
read: true,
|
|
223
253
|
write: false,
|
|
224
254
|
def: 0,
|
|
@@ -230,9 +260,9 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
230
260
|
await this.setObjectNotExistsAsync(`${channelId}.offTimerRemaining`, {
|
|
231
261
|
type: 'state',
|
|
232
262
|
common: {
|
|
233
|
-
name:
|
|
263
|
+
name: 'offTimerRemaining',
|
|
234
264
|
type: 'number',
|
|
235
|
-
role: 'value.duration'
|
|
265
|
+
role: 'value.interval', // GEÄNDERT von 'value.duration' zu 'value.interval'
|
|
236
266
|
read: true,
|
|
237
267
|
write: false,
|
|
238
268
|
def: 0,
|
|
@@ -241,11 +271,18 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
241
271
|
native: {},
|
|
242
272
|
});
|
|
243
273
|
|
|
274
|
+
// States initialisieren
|
|
244
275
|
const finalOnTime = v.switchOnTime || '';
|
|
245
276
|
const finalOffTime = v.switchOffTime || '';
|
|
246
277
|
|
|
247
|
-
await this.setStateAsync(`${this.namespace}.${channelId}.switchOnTime`, {
|
|
248
|
-
|
|
278
|
+
await this.setStateAsync(`${this.namespace}.${channelId}.switchOnTime`, {
|
|
279
|
+
val: finalOnTime,
|
|
280
|
+
ack: true,
|
|
281
|
+
});
|
|
282
|
+
await this.setStateAsync(`${this.namespace}.${channelId}.switchOffTime`, {
|
|
283
|
+
val: finalOffTime,
|
|
284
|
+
ack: true,
|
|
285
|
+
});
|
|
249
286
|
await this.setStateAsync(`${this.namespace}.${channelId}.alwaysOffAtTime`, {
|
|
250
287
|
val: v.alwaysOffAtTime || false,
|
|
251
288
|
ack: true,
|
|
@@ -285,7 +322,9 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
285
322
|
|
|
286
323
|
if (name) {
|
|
287
324
|
this.log.debug(
|
|
288
|
-
|
|
325
|
+
`${name}: Time window ${onTime}–${offTime}, current ${now
|
|
326
|
+
.toTimeString()
|
|
327
|
+
.slice(0, 5)} (${currentMinutes} min), within? ${within}`,
|
|
289
328
|
);
|
|
290
329
|
}
|
|
291
330
|
|
|
@@ -297,52 +336,58 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
297
336
|
return;
|
|
298
337
|
}
|
|
299
338
|
|
|
300
|
-
// FeedIn
|
|
339
|
+
// FeedIn Datapoint - fremder State, nur bei ack=true reagieren
|
|
301
340
|
if (id === this.feedInDatapoint) {
|
|
302
|
-
|
|
303
|
-
|
|
341
|
+
// Nur verarbeiten wenn der Wert vom anderen Adapter bestätigt wurde
|
|
342
|
+
if (state.ack === true) {
|
|
343
|
+
this.log.debug(`Feed-in value updated: ${state.val} (ack=${state.ack})`);
|
|
344
|
+
await this.checkConsumers();
|
|
304
345
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
346
|
+
if (this.percentTimer) {
|
|
347
|
+
clearTimeout(this.percentTimer);
|
|
348
|
+
}
|
|
349
|
+
this.percentTimer = this.safeSetTimeout(
|
|
350
|
+
async () => {
|
|
351
|
+
for (const v of this.consumerList.filter(v => v.ruletype === 'percent')) {
|
|
352
|
+
await this.controlPercentConsumer(v);
|
|
353
|
+
}
|
|
354
|
+
this.percentTimer = null;
|
|
355
|
+
},
|
|
356
|
+
(this.config.delaySecondsProzent || this.DEFAULT_TIMEOUT / 1000) * 1000,
|
|
357
|
+
);
|
|
317
358
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
359
|
+
if (this.batteryTimer) {
|
|
360
|
+
clearTimeout(this.batteryTimer);
|
|
361
|
+
}
|
|
362
|
+
this.batteryTimer = this.safeSetTimeout(
|
|
363
|
+
async () => {
|
|
364
|
+
if (!this.feedInDatapoint) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const feedInState = await this.getForeignStateAsync(this.feedInDatapoint);
|
|
368
|
+
let feedIn = Number(feedInState?.val) || 0;
|
|
369
|
+
if (this.config.feedinNegativ) {
|
|
370
|
+
feedIn = -feedIn;
|
|
371
|
+
}
|
|
331
372
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
373
|
+
for (const v of this.consumerList.filter(v => v.ruletype === 'battery')) {
|
|
374
|
+
await this.controlBattery(v, feedIn);
|
|
375
|
+
}
|
|
376
|
+
this.batteryTimer = null;
|
|
377
|
+
},
|
|
378
|
+
(this.config.batteryDelaySeconds || this.DEFAULT_TIMEOUT / 1000) * 1000,
|
|
379
|
+
);
|
|
380
|
+
} else {
|
|
381
|
+
this.log.debug(`Feed-in value change ignored (ack=${state.ack}) - waiting for confirmed value`);
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
340
384
|
}
|
|
341
385
|
|
|
342
|
-
//
|
|
386
|
+
// Eigene States - nur bei ack=false verarbeiten (Commands/Befehle)
|
|
343
387
|
if (state && !state.ack) {
|
|
344
388
|
this.log.debug(`State changed: ${id} => ${state.val}`);
|
|
345
389
|
|
|
390
|
+
// ControlMode Änderungen
|
|
346
391
|
const match = id.match(/consumer\.(\d+)_.*?\.controlMode/);
|
|
347
392
|
if (match) {
|
|
348
393
|
const index = parseInt(match[1]);
|
|
@@ -356,6 +401,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
356
401
|
}
|
|
357
402
|
}
|
|
358
403
|
|
|
404
|
+
// Time Änderungen
|
|
359
405
|
const timeMatch = id.match(/consumer\.(\d+)_.*?\.(switchOnTime|switchOffTime)/);
|
|
360
406
|
if (timeMatch) {
|
|
361
407
|
const index = parseInt(timeMatch[1]);
|
|
@@ -376,7 +422,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
376
422
|
|
|
377
423
|
try {
|
|
378
424
|
if (!this.feedInDatapoint) {
|
|
379
|
-
this.log.warn('
|
|
425
|
+
this.log.warn('No FeedIn datapoint set – skipping checkConsumers()');
|
|
380
426
|
await this.sendNotification(
|
|
381
427
|
'smartloadmanager',
|
|
382
428
|
'alert',
|
|
@@ -398,7 +444,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
398
444
|
const baseload = this.config.baseload || 0;
|
|
399
445
|
const gridUsage = feedIn <= baseload;
|
|
400
446
|
|
|
401
|
-
this.log.debug(
|
|
447
|
+
this.log.debug(`FeedIn: ${feedIn} W, Baseload: ${baseload} W, GridUsage: ${gridUsage}`);
|
|
402
448
|
|
|
403
449
|
// ----------------------------------
|
|
404
450
|
// Einschalten binary + heating
|
|
@@ -407,17 +453,18 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
407
453
|
.filter(v => v.ruletype === 'binary' || v.ruletype === 'heating')
|
|
408
454
|
.sort((a, b) => (a.priority || 1) - (b.priority || 1));
|
|
409
455
|
|
|
410
|
-
this.log.debug(
|
|
456
|
+
this.log.debug(`Switch-on check for ${sortedOn.length} binary/heating-consumers.`);
|
|
411
457
|
|
|
412
458
|
for (const v of sortedOn) {
|
|
413
459
|
const idx = this.consumerList.indexOf(v);
|
|
414
|
-
const
|
|
460
|
+
const safeName = this.name2id(v.name);
|
|
461
|
+
const id = `${this.namespace}.consumer.${idx}_${safeName}`;
|
|
415
462
|
const mode = await this.getStateAsync(`${id}.controlMode`);
|
|
416
463
|
|
|
417
|
-
this.log.debug(
|
|
464
|
+
this.log.debug(`Checking "${v.name}" (priority ${v.priority}, ${v.performance} W, Type=${v.ruletype})`);
|
|
418
465
|
|
|
419
466
|
if (!mode || mode.val !== 2) {
|
|
420
|
-
this.log.debug(
|
|
467
|
+
this.log.debug(`Manual mode active, skipping.`);
|
|
421
468
|
continue;
|
|
422
469
|
}
|
|
423
470
|
|
|
@@ -435,10 +482,10 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
435
482
|
if (numVal === 1) {
|
|
436
483
|
isOn = true;
|
|
437
484
|
}
|
|
438
|
-
this.log.debug(
|
|
485
|
+
this.log.debug(`NumericDatapoint für "${v.name}": ${v.numericDatapoint} = ${numVal}`);
|
|
439
486
|
}
|
|
440
487
|
|
|
441
|
-
this.log.debug(
|
|
488
|
+
this.log.debug(`"${v.name}" Status: isOn=${isOn}`);
|
|
442
489
|
|
|
443
490
|
// Zusatz-Checks heating
|
|
444
491
|
if (v.ruletype === 'heating' && withinWindow && !isOn) {
|
|
@@ -449,13 +496,13 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
449
496
|
|
|
450
497
|
if (currentTemp !== null && v.maxTemperature !== undefined && currentTemp >= v.maxTemperature) {
|
|
451
498
|
this.log.warn(
|
|
452
|
-
|
|
499
|
+
`"${v.name}" not switched on, temperature ${currentTemp}°C >= MaxTemp ${v.maxTemperature}°C`,
|
|
453
500
|
);
|
|
454
501
|
continue;
|
|
455
502
|
}
|
|
456
503
|
if (currentTemp !== null && v.minTemperature !== undefined && currentTemp < v.minTemperature) {
|
|
457
504
|
this.log.debug(
|
|
458
|
-
|
|
505
|
+
`"${v.name}" not switched on yet, temperature ${currentTemp}°C < MinTemp ${v.minTemperature}°C`,
|
|
459
506
|
);
|
|
460
507
|
continue;
|
|
461
508
|
}
|
|
@@ -463,7 +510,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
463
510
|
if (v.enableDatapoint) {
|
|
464
511
|
const enableState = await this.getForeignStateAsync(v.enableDatapoint);
|
|
465
512
|
if (!enableState?.val) {
|
|
466
|
-
this.log.debug(
|
|
513
|
+
this.log.debug(`"${v.name}" NICHT eingeschaltet – Hardware release missing.`);
|
|
467
514
|
continue;
|
|
468
515
|
}
|
|
469
516
|
}
|
|
@@ -471,7 +518,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
471
518
|
if (v.heartbeatDatapoint) {
|
|
472
519
|
const hbState = await this.getForeignStateAsync(v.heartbeatDatapoint);
|
|
473
520
|
if (!hbState?.val) {
|
|
474
|
-
this.log.warn(
|
|
521
|
+
this.log.warn(`"${v.name}" not switched on – hardware approval missing.`);
|
|
475
522
|
continue;
|
|
476
523
|
}
|
|
477
524
|
}
|
|
@@ -488,28 +535,30 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
488
535
|
v.timer = null;
|
|
489
536
|
v.timerEnd = null;
|
|
490
537
|
v.pendingAction = null;
|
|
491
|
-
const channelId = `consumer.${idx}_${
|
|
538
|
+
const channelId = `consumer.${idx}_${safeName}`;
|
|
492
539
|
await this.setStateAsync(`${this.namespace}.${channelId}.onTimerRemaining`, { val: 0, ack: true });
|
|
493
540
|
await this.setStateAsync(`${this.namespace}.${channelId}.offTimerRemaining`, { val: 0, ack: true });
|
|
494
|
-
this.log.debug(
|
|
541
|
+
this.log.debug(`Switch-on timer for "${v.name}" canceled (condition no longer valid).`);
|
|
495
542
|
}
|
|
496
543
|
|
|
497
544
|
if (withinWindow) {
|
|
498
545
|
if (!gridUsage && v.performance <= feedIn - baseload && !isOn) {
|
|
499
546
|
this.log.debug(
|
|
500
|
-
|
|
547
|
+
`Switch-on possible: surplus ${
|
|
548
|
+
feedIn - baseload
|
|
549
|
+
} W, within time window (onDelay ${v.switchOnDelay || 0}s).`,
|
|
501
550
|
);
|
|
502
551
|
await this.switchConsumerWithDelay(v, true, v.switchOnDelay || 0);
|
|
503
552
|
feedIn -= v.performance;
|
|
504
|
-
this.log.debug(
|
|
553
|
+
this.log.debug(`Remaining surplus after switch-on: ${feedIn - baseload} W`);
|
|
505
554
|
} else if (gridUsage && !isOn) {
|
|
506
|
-
this.log.debug(
|
|
555
|
+
this.log.debug(`Not switched on: grid usage active.`);
|
|
507
556
|
} else if (!isOn) {
|
|
508
557
|
this.log.debug(
|
|
509
|
-
|
|
558
|
+
`Not switched on: surplus too low (${feedIn - baseload} W < ${v.performance} W).`,
|
|
510
559
|
);
|
|
511
560
|
} else {
|
|
512
|
-
this.log.debug(
|
|
561
|
+
this.log.debug(`"${v.name}" is already switched on – no action needed.`);
|
|
513
562
|
}
|
|
514
563
|
}
|
|
515
564
|
}
|
|
@@ -521,11 +570,12 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
521
570
|
.filter(v => v.ruletype === 'binary' || v.ruletype === 'heating')
|
|
522
571
|
.sort((a, b) => (b.priority || 1) - (a.priority || 1));
|
|
523
572
|
|
|
524
|
-
this.log.debug(
|
|
573
|
+
this.log.debug(`Switch-off check for ${sortedOff.length} binary/heating-consumers.`);
|
|
525
574
|
|
|
526
575
|
for (const v of sortedOff) {
|
|
527
576
|
const idx = this.consumerList.indexOf(v);
|
|
528
|
-
const
|
|
577
|
+
const safeName = this.name2id(v.name);
|
|
578
|
+
const id = `${this.namespace}.consumer.${idx}_${safeName}`;
|
|
529
579
|
const mode = await this.getStateAsync(`${id}.controlMode`);
|
|
530
580
|
if (!mode || mode.val !== 2) {
|
|
531
581
|
continue;
|
|
@@ -552,13 +602,13 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
552
602
|
if (v.alwaysOffAtTime && !withinWindow && isOn) {
|
|
553
603
|
if (v.timer && v.timerEnd && v.pendingAction === false) {
|
|
554
604
|
const remaining = Math.max(0, Math.round((v.timerEnd - Date.now()) / 1000));
|
|
555
|
-
this.log.debug(
|
|
605
|
+
this.log.debug(`Active timer for ${v.name}: ${remaining}s remaining`);
|
|
556
606
|
} else if (v.timer && v.timerEnd && v.pendingAction === true) {
|
|
557
607
|
clearTimeout(v.timer);
|
|
558
608
|
v.timer = null;
|
|
559
609
|
v.timerEnd = null;
|
|
560
610
|
v.pendingAction = null;
|
|
561
|
-
const channelId = `consumer.${idx}_${
|
|
611
|
+
const channelId = `consumer.${idx}_${safeName}`;
|
|
562
612
|
await this.setStateAsync(`${this.namespace}.${channelId}.onTimerRemaining`, {
|
|
563
613
|
val: 0,
|
|
564
614
|
ack: true,
|
|
@@ -567,14 +617,14 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
567
617
|
val: 0,
|
|
568
618
|
ack: true,
|
|
569
619
|
});
|
|
570
|
-
this.log.debug(
|
|
620
|
+
this.log.debug(`Switch-on timer for "${v.name}" canceled (AlwaysOff active).`);
|
|
571
621
|
this.log.debug(
|
|
572
|
-
|
|
622
|
+
`AlwaysOff active: "${v.name}" - time window expired, offDelay ${v.switchOffDelay || 0}s.`,
|
|
573
623
|
);
|
|
574
624
|
await this.switchConsumerWithDelay(v, false, v.switchOffDelay || 0);
|
|
575
625
|
} else {
|
|
576
626
|
this.log.debug(
|
|
577
|
-
|
|
627
|
+
`AlwaysOff activ: "${v.name}" - time window expired, offDelay ${v.switchOffDelay || 0}s.`,
|
|
578
628
|
);
|
|
579
629
|
await this.switchConsumerWithDelay(v, false, v.switchOffDelay || 0);
|
|
580
630
|
}
|
|
@@ -585,7 +635,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
585
635
|
if (v.enableDatapoint) {
|
|
586
636
|
const enableState = await this.getForeignStateAsync(v.enableDatapoint);
|
|
587
637
|
if (!enableState?.val) {
|
|
588
|
-
this.log.warn(
|
|
638
|
+
this.log.warn(`"${v.name}" is being switched off - hardware approval missing.`);
|
|
589
639
|
await this.switchConsumerWithDelay(v, false, 0);
|
|
590
640
|
continue;
|
|
591
641
|
}
|
|
@@ -593,7 +643,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
593
643
|
if (v.heartbeatDatapoint) {
|
|
594
644
|
const hbState = await this.getForeignStateAsync(v.heartbeatDatapoint);
|
|
595
645
|
if (!hbState?.val) {
|
|
596
|
-
this.log.warn(
|
|
646
|
+
this.log.warn(`"${v.name}" is being switched off - heartbeat missing.`);
|
|
597
647
|
await this.switchConsumerWithDelay(v, false, 0);
|
|
598
648
|
continue;
|
|
599
649
|
}
|
|
@@ -604,30 +654,54 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
604
654
|
continue;
|
|
605
655
|
}
|
|
606
656
|
|
|
657
|
+
// Temperatur-basierte Ausschaltung für heating
|
|
658
|
+
if (v.ruletype === 'heating' && v.maxTemperature !== undefined && isOn) {
|
|
659
|
+
const tempState = v.temperatureDatapoint
|
|
660
|
+
? await this.getForeignStateAsync(v.temperatureDatapoint)
|
|
661
|
+
: null;
|
|
662
|
+
const currentTemp = tempState?.val !== undefined ? Number(tempState.val) : null;
|
|
663
|
+
if (currentTemp !== null && currentTemp >= v.maxTemperature) {
|
|
664
|
+
this.log.warn(
|
|
665
|
+
`"${v.name}" switched off due to maxTemperature reached: ${currentTemp}°C >= ${v.maxTemperature}°C`,
|
|
666
|
+
);
|
|
667
|
+
await this.switchConsumerWithDelay(v, false, 0);
|
|
668
|
+
continue; // weiter zum nächsten Verbraucher
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
607
672
|
// Timer-Abbruch bei wegfallender Bedingung (Ausschalten)
|
|
608
|
-
|
|
673
|
+
const surplus = feedIn - baseload;
|
|
674
|
+
if (v.timer && v.timerEnd && v.pendingAction === false && withinWindow && surplus >= v.switchOffPoint) {
|
|
609
675
|
clearTimeout(v.timer);
|
|
610
676
|
v.timer = null;
|
|
611
677
|
v.timerEnd = null;
|
|
612
678
|
v.pendingAction = null;
|
|
613
|
-
const channelId = `consumer.${idx}_${
|
|
679
|
+
const channelId = `consumer.${idx}_${safeName}`;
|
|
614
680
|
await this.setStateAsync(`${this.namespace}.${channelId}.onTimerRemaining`, { val: 0, ack: true });
|
|
615
681
|
await this.setStateAsync(`${this.namespace}.${channelId}.offTimerRemaining`, { val: 0, ack: true });
|
|
616
|
-
this.log.debug(
|
|
682
|
+
this.log.debug(`Switch-off timer for "${v.name}" canceled (condition no longer valid).`);
|
|
617
683
|
}
|
|
618
684
|
|
|
619
|
-
|
|
685
|
+
// Ausschalten, wenn:
|
|
686
|
+
// - Zeitfenster verlassen ODER
|
|
687
|
+
// - Netzbezug aktiv ODER
|
|
688
|
+
// - Überschuss < switchOffPoint
|
|
689
|
+
const shouldSwitchOff = !withinWindow || gridUsage || surplus < v.switchOffPoint;
|
|
690
|
+
|
|
691
|
+
if (!v.alwaysOffAtTime && shouldSwitchOff) {
|
|
620
692
|
if (v.timer && v.timerEnd && v.pendingAction === false) {
|
|
621
693
|
const remaining = Math.max(0, Math.round((v.timerEnd - Date.now()) / 1000));
|
|
622
|
-
this.log.debug(
|
|
694
|
+
this.log.debug(`Active timer for ${v.name}: ${remaining}s remaining`);
|
|
623
695
|
} else {
|
|
624
|
-
this.log.debug(
|
|
696
|
+
this.log.debug(
|
|
697
|
+
`Switching off: "${v.name}" - surplus ${surplus}W < switchOffPoint ${v.switchOffPoint}W, offDelay ${v.switchOffDelay || 0}s.`,
|
|
698
|
+
);
|
|
625
699
|
await this.switchConsumerWithDelay(v, false, v.switchOffDelay || 0);
|
|
626
700
|
}
|
|
627
701
|
}
|
|
628
702
|
}
|
|
629
703
|
|
|
630
|
-
// --- Batterie
|
|
704
|
+
// --- Batterie unverändert ---
|
|
631
705
|
const batteryConsumers = this.consumerList.filter(c => c.ruletype === 'battery');
|
|
632
706
|
for (const v of batteryConsumers) {
|
|
633
707
|
await this.controlBattery(v, feedIn);
|
|
@@ -641,9 +715,9 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
641
715
|
});
|
|
642
716
|
|
|
643
717
|
if (runningTimers.length > 0) {
|
|
644
|
-
this.log.debug(
|
|
718
|
+
this.log.debug(`Active timers: ${runningTimers.join(' | ')}`);
|
|
645
719
|
} else {
|
|
646
|
-
this.log.debug('
|
|
720
|
+
this.log.debug('No active timers.');
|
|
647
721
|
}
|
|
648
722
|
} catch (err) {
|
|
649
723
|
this.log.error(`Error in checkConsumers: ${err.message}`);
|
|
@@ -660,13 +734,13 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
660
734
|
try {
|
|
661
735
|
const withinWindow = this.timeWithinWindow(v.switchOnTime, v.switchOffTime);
|
|
662
736
|
if (!withinWindow) {
|
|
663
|
-
this.log.debug(`[Percent] ${v.name}
|
|
737
|
+
this.log.debug(`[Percent] ${v.name} outside time window, set to 0%`);
|
|
664
738
|
await this.setForeignStateAsync(v.datapoint, 0);
|
|
665
739
|
return;
|
|
666
740
|
}
|
|
667
741
|
|
|
668
742
|
if (!this.feedInDatapoint) {
|
|
669
|
-
this.log.error('
|
|
743
|
+
this.log.error('feedInDatapoint not set!');
|
|
670
744
|
await this.sendNotification('smartloadmanager', 'alert', '❌ feedInDatapoint ist nicht gesetzt!');
|
|
671
745
|
return;
|
|
672
746
|
}
|
|
@@ -696,7 +770,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
696
770
|
|
|
697
771
|
if (newPercent !== currentPercent) {
|
|
698
772
|
await this.setForeignStateAsync(v.datapoint, newPercent);
|
|
699
|
-
this.log.info(
|
|
773
|
+
this.log.info(`${v.name} set to ${newPercent}%`);
|
|
700
774
|
|
|
701
775
|
if (this.config.notifyPercent) {
|
|
702
776
|
await this.sendNotification(
|
|
@@ -706,10 +780,10 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
706
780
|
);
|
|
707
781
|
}
|
|
708
782
|
} else {
|
|
709
|
-
this.log.debug(`[Percent] ${v.name}
|
|
783
|
+
this.log.debug(`[Percent] ${v.name} remains at ${currentPercent}%`);
|
|
710
784
|
}
|
|
711
785
|
} catch (error) {
|
|
712
|
-
this.log.error(
|
|
786
|
+
this.log.error(`Error in controlPercentConsumer: ${error.message}`);
|
|
713
787
|
await this.sendNotification(
|
|
714
788
|
'smartloadmanager',
|
|
715
789
|
'alert',
|
|
@@ -720,24 +794,33 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
720
794
|
|
|
721
795
|
async controlBattery(v, feedIn) {
|
|
722
796
|
try {
|
|
723
|
-
this.log.debug(`[Battery]
|
|
797
|
+
this.log.debug(`[Battery] Checking for ${v.name}`);
|
|
724
798
|
|
|
725
799
|
const withinWindow = this.timeWithinWindow(v.switchOnTime, v.switchOffTime);
|
|
726
800
|
if (!withinWindow) {
|
|
727
|
-
this.log.debug(`[Battery] ${v.name}
|
|
801
|
+
this.log.debug(`[Battery] ${v.name} outside time window, set to 0`);
|
|
728
802
|
if (v.batterySetpoint) {
|
|
729
|
-
await this.setForeignStateAsync(v.batterySetpoint, {
|
|
803
|
+
await this.setForeignStateAsync(v.batterySetpoint, {
|
|
804
|
+
val: 0,
|
|
805
|
+
ack: true,
|
|
806
|
+
});
|
|
730
807
|
}
|
|
731
|
-
if (
|
|
732
|
-
await this.setForeignStateAsync(
|
|
808
|
+
if (this.batteryControlModeDatapoint) {
|
|
809
|
+
await this.setForeignStateAsync(this.batteryControlModeDatapoint, {
|
|
810
|
+
val: 0,
|
|
811
|
+
ack: true,
|
|
812
|
+
});
|
|
733
813
|
}
|
|
734
814
|
return;
|
|
735
815
|
}
|
|
736
816
|
|
|
737
817
|
if (!v.batterySetpoint) {
|
|
738
|
-
this.log.warn(`[Battery] ${v.name}
|
|
739
|
-
if (
|
|
740
|
-
await this.setForeignStateAsync(
|
|
818
|
+
this.log.warn(`[Battery] ${v.name} has no batterySetpoint`);
|
|
819
|
+
if (this.batteryControlModeDatapoint) {
|
|
820
|
+
await this.setForeignStateAsync(this.batteryControlModeDatapoint, {
|
|
821
|
+
val: 0,
|
|
822
|
+
ack: true,
|
|
823
|
+
});
|
|
741
824
|
}
|
|
742
825
|
return;
|
|
743
826
|
}
|
|
@@ -750,7 +833,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
750
833
|
soc = Number(socState?.val);
|
|
751
834
|
targetSoc = Number(targetSocState?.val);
|
|
752
835
|
if (isNaN(soc) || isNaN(targetSoc)) {
|
|
753
|
-
this.log.warn(`[Battery] ${v.name}:
|
|
836
|
+
this.log.warn(`[Battery] ${v.name}: invalid SOC or targetSOC`);
|
|
754
837
|
soc = null;
|
|
755
838
|
targetSoc = null;
|
|
756
839
|
}
|
|
@@ -777,8 +860,11 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
777
860
|
: -1;
|
|
778
861
|
|
|
779
862
|
if (powerToSet !== oldPower) {
|
|
780
|
-
await this.setForeignStateAsync(v.batterySetpoint, {
|
|
781
|
-
|
|
863
|
+
await this.setForeignStateAsync(v.batterySetpoint, {
|
|
864
|
+
val: powerToSet,
|
|
865
|
+
ack: true,
|
|
866
|
+
});
|
|
867
|
+
this.log.info(`Battery ${v.name}: charging power set to ${powerToSet}W`);
|
|
782
868
|
if (this.config.notifyBattery) {
|
|
783
869
|
await this.sendNotification(
|
|
784
870
|
'smartloadmanager',
|
|
@@ -787,12 +873,15 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
787
873
|
);
|
|
788
874
|
}
|
|
789
875
|
} else {
|
|
790
|
-
this.log.debug(`[Battery] ${v.name}
|
|
876
|
+
this.log.debug(`[Battery] ${v.name} charging power remains at ${oldPower}W`);
|
|
791
877
|
}
|
|
792
878
|
|
|
793
879
|
if (this.batteryControlModeDatapoint && modeToSet !== oldMode) {
|
|
794
|
-
await this.setForeignStateAsync(this.batteryControlModeDatapoint, {
|
|
795
|
-
|
|
880
|
+
await this.setForeignStateAsync(this.batteryControlModeDatapoint, {
|
|
881
|
+
val: modeToSet,
|
|
882
|
+
ack: true,
|
|
883
|
+
});
|
|
884
|
+
this.log.debug(`[Battery] ${v.name} mode changed from ${oldMode} to ${modeToSet}`);
|
|
796
885
|
if (this.config.notifyBattery) {
|
|
797
886
|
await this.sendNotification(
|
|
798
887
|
'smartloadmanager',
|
|
@@ -802,7 +891,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
802
891
|
}
|
|
803
892
|
}
|
|
804
893
|
} catch (error) {
|
|
805
|
-
this.log.error(`[Battery]
|
|
894
|
+
this.log.error(`[Battery] Error for ${v.name}: ${error.message}`);
|
|
806
895
|
await this.sendNotification(
|
|
807
896
|
'smartloadmanager',
|
|
808
897
|
'alert',
|
|
@@ -822,16 +911,28 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
822
911
|
|
|
823
912
|
if (consumer.datapoint) {
|
|
824
913
|
// Erst setzen
|
|
825
|
-
await this.setForeignStateAsync(consumer.datapoint, {
|
|
914
|
+
await this.setForeignStateAsync(consumer.datapoint, {
|
|
915
|
+
val: targetState,
|
|
916
|
+
ack: false,
|
|
917
|
+
});
|
|
826
918
|
// Danach sofort bestätigen (falls keine Rückmeldung vom Gerät kommt)
|
|
827
|
-
await this.setForeignStateAsync(consumer.datapoint, {
|
|
919
|
+
await this.setForeignStateAsync(consumer.datapoint, {
|
|
920
|
+
val: targetState,
|
|
921
|
+
ack: true,
|
|
922
|
+
});
|
|
828
923
|
}
|
|
829
924
|
|
|
830
925
|
if (consumer.numericDatapoint) {
|
|
831
926
|
const numVal = targetState ? 1 : 0;
|
|
832
|
-
await this.setForeignStateAsync(consumer.numericDatapoint, {
|
|
833
|
-
|
|
834
|
-
|
|
927
|
+
await this.setForeignStateAsync(consumer.numericDatapoint, {
|
|
928
|
+
val: numVal,
|
|
929
|
+
ack: false,
|
|
930
|
+
});
|
|
931
|
+
await this.setForeignStateAsync(consumer.numericDatapoint, {
|
|
932
|
+
val: numVal,
|
|
933
|
+
ack: true,
|
|
934
|
+
});
|
|
935
|
+
this.log.debug(`Numeric datapoint for "${consumer.name}" set to ${numVal}`);
|
|
835
936
|
}
|
|
836
937
|
|
|
837
938
|
const msg = `${consumer.name} wurde ${targetState ? 'eingeschaltet' : 'ausgeschaltet'}`;
|
|
@@ -856,7 +957,8 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
856
957
|
: consumer.switchOffDelay || 0;
|
|
857
958
|
|
|
858
959
|
const idx = this.consumerList.indexOf(consumer);
|
|
859
|
-
const
|
|
960
|
+
const safeName = this.name2id(consumer.name);
|
|
961
|
+
const channelId = `consumer.${idx}_${safeName}`;
|
|
860
962
|
const onTimerState = `${this.namespace}.${channelId}.onTimerRemaining`;
|
|
861
963
|
const offTimerState = `${this.namespace}.${channelId}.offTimerRemaining`;
|
|
862
964
|
|
|
@@ -885,7 +987,10 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
885
987
|
await this.setStateAsync(onTimerState, { val: remaining, ack: true });
|
|
886
988
|
await this.setStateAsync(offTimerState, { val: 0, ack: true });
|
|
887
989
|
} else {
|
|
888
|
-
await this.setStateAsync(offTimerState, {
|
|
990
|
+
await this.setStateAsync(offTimerState, {
|
|
991
|
+
val: remaining,
|
|
992
|
+
ack: true,
|
|
993
|
+
});
|
|
889
994
|
await this.setStateAsync(onTimerState, { val: 0, ack: true });
|
|
890
995
|
}
|
|
891
996
|
};
|
|
@@ -894,7 +999,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
894
999
|
clearInterval(consumer.timerTick);
|
|
895
1000
|
consumer.timerTick = null;
|
|
896
1001
|
}
|
|
897
|
-
consumer.timerTick =
|
|
1002
|
+
consumer.timerTick = this.safeSetInterval(tick, 1000);
|
|
898
1003
|
};
|
|
899
1004
|
|
|
900
1005
|
const doSwitch = async () => {
|
|
@@ -962,8 +1067,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
962
1067
|
await stopTimerTicker();
|
|
963
1068
|
};
|
|
964
1069
|
|
|
965
|
-
//
|
|
966
|
-
// Falls Timer läuft, aber Bedingung sich geändert hat → Timer abbrechen
|
|
1070
|
+
// Falls Timer läuft, aber Bedingung sich geändert hat → Timer abbrechen / anpassen
|
|
967
1071
|
if (consumer.timer) {
|
|
968
1072
|
// wenn Einschalt-Timer läuft, aber turnOn nicht mehr gilt → abbrechen
|
|
969
1073
|
if (consumer.pendingAction === true && !turnOn) {
|
|
@@ -990,7 +1094,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
990
1094
|
return;
|
|
991
1095
|
}
|
|
992
1096
|
|
|
993
|
-
// ansonsten Timer neu setzen
|
|
1097
|
+
// ansonsten Timer neu setzen (älteren abbrechen)
|
|
994
1098
|
clearTimeout(consumer.timer);
|
|
995
1099
|
consumer.timer = null;
|
|
996
1100
|
consumer.timerEnd = null;
|
|
@@ -1001,7 +1105,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1001
1105
|
if (delay > 0) {
|
|
1002
1106
|
consumer.timerEnd = Date.now() + delay * 1000;
|
|
1003
1107
|
consumer.pendingAction = turnOn;
|
|
1004
|
-
consumer.timer =
|
|
1108
|
+
consumer.timer = this.safeSetTimeout(doSwitch, delay * 1000);
|
|
1005
1109
|
await startTimerTicker();
|
|
1006
1110
|
} else {
|
|
1007
1111
|
await doSwitch();
|
|
@@ -1017,7 +1121,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1017
1121
|
// 1) Controller-Notification (nur wenn Option aktiv ist)
|
|
1018
1122
|
if (cfg.enableNotificationManager) {
|
|
1019
1123
|
await this.registerNotification(scope, category, message);
|
|
1020
|
-
this.log.debug(
|
|
1124
|
+
this.log.debug(`Controller-Notification [${scope}/${category}]: ${message}`);
|
|
1021
1125
|
}
|
|
1022
1126
|
|
|
1023
1127
|
// 2) Messenger-Nachrichten
|
|
@@ -1031,11 +1135,11 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1031
1135
|
if (recipients.length > 0) {
|
|
1032
1136
|
recipients.forEach(user => {
|
|
1033
1137
|
this.sendTo(cfg.telegramInstance, { text: message, user });
|
|
1034
|
-
this.log.debug(
|
|
1138
|
+
this.log.debug(`Telegram → ${cfg.telegramInstance}/${user}: ${message}`);
|
|
1035
1139
|
});
|
|
1036
1140
|
} else {
|
|
1037
1141
|
this.sendTo(cfg.telegramInstance, message);
|
|
1038
|
-
this.log.debug(
|
|
1142
|
+
this.log.debug(`Telegram → ${cfg.telegramInstance}: ${message}`);
|
|
1039
1143
|
}
|
|
1040
1144
|
}
|
|
1041
1145
|
|
|
@@ -1045,7 +1149,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1045
1149
|
title: 'SmartLoadManager',
|
|
1046
1150
|
message: message,
|
|
1047
1151
|
});
|
|
1048
|
-
this.log.debug(
|
|
1152
|
+
this.log.debug(`Gotify → ${cfg.gotifyInstance}: ${message}`);
|
|
1049
1153
|
}
|
|
1050
1154
|
|
|
1051
1155
|
// E-Mail
|
|
@@ -1061,14 +1165,14 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1061
1165
|
text: message,
|
|
1062
1166
|
subject: 'SmartLoadManager',
|
|
1063
1167
|
});
|
|
1064
|
-
this.log.debug(
|
|
1168
|
+
this.log.debug(`Email → ${cfg.emailInstance}/${to}: ${message}`);
|
|
1065
1169
|
});
|
|
1066
1170
|
} else {
|
|
1067
1171
|
this.sendTo(cfg.emailInstance, {
|
|
1068
1172
|
text: message,
|
|
1069
1173
|
subject: 'SmartLoadManager',
|
|
1070
1174
|
});
|
|
1071
|
-
this.log.debug(
|
|
1175
|
+
this.log.debug(`Email → ${cfg.emailInstance}: ${message}`);
|
|
1072
1176
|
}
|
|
1073
1177
|
}
|
|
1074
1178
|
|
|
@@ -1081,11 +1185,11 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1081
1185
|
if (recipients.length > 0) {
|
|
1082
1186
|
recipients.forEach(user => {
|
|
1083
1187
|
this.sendTo(cfg.whatsappInstance, { text: message, user });
|
|
1084
|
-
this.log.debug(
|
|
1188
|
+
this.log.debug(`WhatsApp → ${cfg.whatsappInstance}/${user}: ${message}`);
|
|
1085
1189
|
});
|
|
1086
1190
|
} else {
|
|
1087
1191
|
this.sendTo(cfg.whatsappInstance, { text: message });
|
|
1088
|
-
this.log.debug(
|
|
1192
|
+
this.log.debug(`WhatsApp → ${cfg.whatsappInstance}: ${message}`);
|
|
1089
1193
|
}
|
|
1090
1194
|
}
|
|
1091
1195
|
|
|
@@ -1098,11 +1202,11 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1098
1202
|
if (recipients.length > 0) {
|
|
1099
1203
|
recipients.forEach(user => {
|
|
1100
1204
|
this.sendTo(cfg.signalInstance, { text: message, user });
|
|
1101
|
-
this.log.debug(
|
|
1205
|
+
this.log.debug(`Signal → ${cfg.signalInstance}/${user}: ${message}`);
|
|
1102
1206
|
});
|
|
1103
1207
|
} else {
|
|
1104
1208
|
this.sendTo(cfg.signalInstance, { text: message });
|
|
1105
|
-
this.log.debug(
|
|
1209
|
+
this.log.debug(`Signal → ${cfg.signalInstance}: ${message}`);
|
|
1106
1210
|
}
|
|
1107
1211
|
}
|
|
1108
1212
|
|
|
@@ -1119,25 +1223,26 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1119
1223
|
title: 'SmartLoadManager',
|
|
1120
1224
|
user,
|
|
1121
1225
|
});
|
|
1122
|
-
this.log.debug(
|
|
1226
|
+
this.log.debug(`Pushover → ${cfg.pushoverInstance}/${user}: ${message}`);
|
|
1123
1227
|
});
|
|
1124
1228
|
} else {
|
|
1125
1229
|
this.sendTo(cfg.pushoverInstance, {
|
|
1126
1230
|
message: message,
|
|
1127
1231
|
title: 'SmartLoadManager',
|
|
1128
1232
|
});
|
|
1129
|
-
this.log.debug(
|
|
1233
|
+
this.log.debug(`Pushover → ${cfg.pushoverInstance}: ${message}`);
|
|
1130
1234
|
}
|
|
1131
1235
|
}
|
|
1132
1236
|
} catch (e) {
|
|
1133
|
-
this.log.warn(
|
|
1237
|
+
this.log.warn(`Error sending notification: ${e.message}`);
|
|
1134
1238
|
}
|
|
1135
1239
|
}
|
|
1136
1240
|
|
|
1137
1241
|
async initializeConsumerStatus() {
|
|
1138
1242
|
// Initiale Status-Updates
|
|
1139
1243
|
for (const v of this.consumerList) {
|
|
1140
|
-
const
|
|
1244
|
+
const safeName = this.name2id(v.name);
|
|
1245
|
+
const channelId = `consumer.${this.consumerList.indexOf(v)}_${safeName}`;
|
|
1141
1246
|
const modeState = await this.getStateAsync(`${this.namespace}.${channelId}.controlMode`);
|
|
1142
1247
|
if (!modeState) {
|
|
1143
1248
|
await this.setStateAsync(`${this.namespace}.${channelId}.controlMode`, {
|
|
@@ -1149,7 +1254,7 @@ class ZeroFeedIn extends utils.Adapter {
|
|
|
1149
1254
|
}
|
|
1150
1255
|
|
|
1151
1256
|
sleep(ms) {
|
|
1152
|
-
return new Promise(resolve =>
|
|
1257
|
+
return new Promise(resolve => this.safeSetTimeout(resolve, ms, 'sleep'));
|
|
1153
1258
|
}
|
|
1154
1259
|
|
|
1155
1260
|
parseTimeToMinutes(timeStr) {
|