iobroker.utility-monitor 1.4.2

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.
@@ -0,0 +1,1556 @@
1
+ /**
2
+ * State Manager module for nebenkosten-monitor
3
+ * Manages the creation and structure of all adapter states
4
+ */
5
+
6
+ /**
7
+ * Safe wrapper for setObjectNotExistsAsync with error handling
8
+ *
9
+ * @param {object} adapter - Adapter instance
10
+ * @param {string} id - State ID
11
+ * @param {object} obj - State object
12
+ * @returns {Promise<void>}
13
+ */
14
+ async function safeSetObjectNotExists(adapter, id, obj) {
15
+ try {
16
+ await adapter.setObjectNotExistsAsync(id, obj);
17
+ } catch (error) {
18
+ adapter.log.warn(`Failed to create state ${id}: ${error.message}`);
19
+ // Re-throw critical errors
20
+ if (error.message && error.message.includes('EACCES')) {
21
+ throw error;
22
+ }
23
+ }
24
+ }
25
+
26
+ /**
27
+ * State role definitions for different state types
28
+ */
29
+ const STATE_ROLES = {
30
+ consumption: 'value.power.consumption',
31
+ cost: 'value.money',
32
+ meterReading: 'value',
33
+ price: 'value.price',
34
+ timestamp: 'value.time',
35
+ indicator: 'indicator',
36
+ value: 'value',
37
+ };
38
+
39
+ /**
40
+ * Creates the complete state structure for a utility type (gas, water, electricity)
41
+ *
42
+ * @param {object} adapter - The adapter instance
43
+ * @param {string} type - Utility type: 'gas', 'water', or 'electricity'
44
+ * @param {object} _config - Configuration for this utility
45
+ * @returns {Promise<void>}
46
+ */
47
+ async function createUtilityStateStructure(adapter, type, _config = {}) {
48
+ const isGas = type === 'gas';
49
+
50
+ const labels = {
51
+ gas: { name: 'Gas', unit: 'kWh', volumeUnit: 'm³' },
52
+ water: { name: 'Wasser', unit: 'm³', volumeUnit: 'm³' },
53
+ electricity: { name: 'Strom', unit: 'kWh', volumeUnit: 'kWh' },
54
+ pv: { name: 'PV', unit: 'kWh', volumeUnit: 'kWh', consumption: 'Einspeisung', cost: 'Vergütung' },
55
+ };
56
+
57
+ const label = labels[type];
58
+
59
+ // Create main channel
60
+ await adapter.setObjectNotExistsAsync(type, {
61
+ type: 'channel',
62
+ common: {
63
+ name: `${label.name}-Überwachung`,
64
+ },
65
+ native: {},
66
+ });
67
+
68
+ // CONSUMPTION STATES
69
+ await adapter.setObjectNotExistsAsync(`${type}.consumption`, {
70
+ type: 'channel',
71
+ common: {
72
+ name: label.consumption || 'Verbrauch',
73
+ },
74
+ native: {},
75
+ });
76
+
77
+ // For gas: add volume states (m³) in addition to energy states (kWh)
78
+ // Water doesn't need volume states because it's already measured in m³
79
+ if (type === 'gas') {
80
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.dailyVolume`, {
81
+ type: 'state',
82
+ common: {
83
+ name: 'Täglicher Verbrauch (m³)',
84
+ type: 'number',
85
+ role: STATE_ROLES.consumption,
86
+ read: true,
87
+ write: false,
88
+ unit: 'm³',
89
+ def: 0,
90
+ },
91
+ native: {},
92
+ });
93
+
94
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.monthlyVolume`, {
95
+ type: 'state',
96
+ common: {
97
+ name: 'Monatlicher Verbrauch (m³)',
98
+ type: 'number',
99
+ role: STATE_ROLES.consumption,
100
+ read: true,
101
+ write: false,
102
+ unit: 'm³',
103
+ def: 0,
104
+ },
105
+ native: {},
106
+ });
107
+
108
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.yearlyVolume`, {
109
+ type: 'state',
110
+ common: {
111
+ name: 'Jährlicher Verbrauch (m³)',
112
+ type: 'number',
113
+ role: STATE_ROLES.consumption,
114
+ read: true,
115
+ write: false,
116
+ unit: 'm³',
117
+ def: 0,
118
+ },
119
+ native: {},
120
+ });
121
+ }
122
+
123
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.daily`, {
124
+ type: 'state',
125
+ common: {
126
+ name: `Tages-${(label.consumption || 'Verbrauch').toLowerCase()} (${label.unit})`,
127
+ type: 'number',
128
+ role: STATE_ROLES.consumption,
129
+ read: true,
130
+ write: false,
131
+ unit: label.unit,
132
+ def: 0,
133
+ },
134
+ native: {},
135
+ });
136
+
137
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.monthly`, {
138
+ type: 'state',
139
+ common: {
140
+ name: `Monats-${(label.consumption || 'Verbrauch').toLowerCase()} (${label.unit})`,
141
+ type: 'number',
142
+ role: STATE_ROLES.consumption,
143
+ read: true,
144
+ write: false,
145
+ unit: label.unit,
146
+ def: 0,
147
+ },
148
+ native: {},
149
+ });
150
+
151
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.yearly`, {
152
+ type: 'state',
153
+ common: {
154
+ name: `Jahres-${(label.consumption || 'Verbrauch').toLowerCase()} (${label.unit})`,
155
+ type: 'number',
156
+ role: STATE_ROLES.consumption,
157
+ read: true,
158
+ write: false,
159
+ unit: label.unit,
160
+ def: 0,
161
+ },
162
+ native: {},
163
+ });
164
+
165
+ // Map internal type to config type (electricity -> strom, water -> wasser, gas -> gas)
166
+ const configTypeMap = {
167
+ electricity: 'strom',
168
+ water: 'wasser',
169
+ gas: 'gas',
170
+ pv: 'pv',
171
+ };
172
+ const configType = configTypeMap[type] || type;
173
+
174
+ // HT/NT consumption states - only create if HT/NT tariff is enabled
175
+ // Note: Water typically doesn't have HT/NT, but logic remains generic if config exists
176
+ const htNtEnabledKey = `${configType}HtNtEnabled`;
177
+ if (_config[htNtEnabledKey]) {
178
+ const htNtStates = ['dailyHT', 'dailyNT', 'monthlyHT', 'monthlyNT', 'yearlyHT', 'yearlyNT'];
179
+ const htNtLabels = {
180
+ dailyHT: 'Tagesverbrauch Haupttarif (HT)',
181
+ dailyNT: 'Tagesverbrauch Nebentarif (NT)',
182
+ monthlyHT: 'Monatsverbrauch Haupttarif (HT)',
183
+ monthlyNT: 'Monatsverbrauch Nebentarif (NT)',
184
+ yearlyHT: 'Jahresverbrauch Haupttarif (HT)',
185
+ yearlyNT: 'Jahresverbrauch Nebentarif (NT)',
186
+ };
187
+
188
+ for (const state of htNtStates) {
189
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.${state}`, {
190
+ type: 'state',
191
+ common: {
192
+ name: `${htNtLabels[state]} (${label.unit})`,
193
+ type: 'number',
194
+ role: STATE_ROLES.consumption,
195
+ read: true,
196
+ write: false,
197
+ unit: label.unit,
198
+ def: 0,
199
+ },
200
+ native: {},
201
+ });
202
+ }
203
+ }
204
+
205
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.lastUpdate`, {
206
+ type: 'state',
207
+ common: {
208
+ name: 'Letzte Aktualisierung',
209
+ type: 'number',
210
+ role: STATE_ROLES.timestamp,
211
+ read: true,
212
+ write: false,
213
+ },
214
+ native: {},
215
+ });
216
+
217
+ // COST STATES
218
+ await adapter.setObjectNotExistsAsync(`${type}.costs`, {
219
+ type: 'channel',
220
+ common: {
221
+ name: label.cost || 'Kosten',
222
+ },
223
+ native: {},
224
+ });
225
+
226
+ await adapter.setObjectNotExistsAsync(`${type}.costs.daily`, {
227
+ type: 'state',
228
+ common: {
229
+ name: `Tages-${(label.cost || 'Kosten').toLowerCase()} (€)`,
230
+ type: 'number',
231
+ role: STATE_ROLES.cost,
232
+ read: true,
233
+ write: false,
234
+ unit: '€',
235
+ def: 0,
236
+ },
237
+ native: {},
238
+ });
239
+
240
+ await adapter.setObjectNotExistsAsync(`${type}.costs.monthly`, {
241
+ type: 'state',
242
+ common: {
243
+ name: `Monats-${(label.cost || 'Kosten').toLowerCase()} (€)`,
244
+ type: 'number',
245
+ role: STATE_ROLES.cost,
246
+ read: true,
247
+ write: false,
248
+ unit: '€',
249
+ def: 0,
250
+ },
251
+ native: {},
252
+ });
253
+
254
+ await adapter.setObjectNotExistsAsync(`${type}.costs.yearly`, {
255
+ type: 'state',
256
+ common: {
257
+ name: `Jahres-${(label.cost || 'Kosten').toLowerCase()} (€)`,
258
+ type: 'number',
259
+ role: STATE_ROLES.cost,
260
+ read: true,
261
+ write: false,
262
+ unit: '€',
263
+ def: 0,
264
+ },
265
+ native: {},
266
+ });
267
+
268
+ // HT/NT states - only create if HT/NT tariff is enabled
269
+ // Note: htNtEnabledKey already calculated above for consumption section
270
+ if (_config[htNtEnabledKey]) {
271
+ await adapter.setObjectNotExistsAsync(`${type}.costs.yearlyHT`, {
272
+ type: 'state',
273
+ common: {
274
+ name: 'Jahreskosten Haupttarif (HT) (€)',
275
+ type: 'number',
276
+ role: STATE_ROLES.cost,
277
+ read: true,
278
+ write: false,
279
+ unit: '€',
280
+ def: 0,
281
+ },
282
+ native: {},
283
+ });
284
+
285
+ await adapter.setObjectNotExistsAsync(`${type}.costs.yearlyNT`, {
286
+ type: 'state',
287
+ common: {
288
+ name: 'Jahreskosten Nebentarif (NT) (€)',
289
+ type: 'number',
290
+ role: STATE_ROLES.cost,
291
+ read: true,
292
+ write: false,
293
+ unit: '€',
294
+ def: 0,
295
+ },
296
+ native: {},
297
+ });
298
+
299
+ await adapter.setObjectNotExistsAsync(`${type}.costs.monthlyHT`, {
300
+ type: 'state',
301
+ common: {
302
+ name: 'Monatskosten Haupttarif (HT) (€)',
303
+ type: 'number',
304
+ role: STATE_ROLES.cost,
305
+ read: true,
306
+ write: false,
307
+ unit: '€',
308
+ def: 0,
309
+ },
310
+ native: {},
311
+ });
312
+
313
+ await adapter.setObjectNotExistsAsync(`${type}.costs.monthlyNT`, {
314
+ type: 'state',
315
+ common: {
316
+ name: 'Monatskosten Nebentarif (NT) (€)',
317
+ type: 'number',
318
+ role: STATE_ROLES.cost,
319
+ read: true,
320
+ write: false,
321
+ unit: '€',
322
+ def: 0,
323
+ },
324
+ native: {},
325
+ });
326
+
327
+ await adapter.setObjectNotExistsAsync(`${type}.costs.dailyHT`, {
328
+ type: 'state',
329
+ common: {
330
+ name: 'Tageskosten Haupttarif (HT) (€)',
331
+ type: 'number',
332
+ role: STATE_ROLES.cost,
333
+ read: true,
334
+ write: false,
335
+ unit: '€',
336
+ def: 0,
337
+ },
338
+ native: {},
339
+ });
340
+
341
+ await adapter.setObjectNotExistsAsync(`${type}.costs.dailyNT`, {
342
+ type: 'state',
343
+ common: {
344
+ name: 'Tageskosten Nebentarif (NT) (€)',
345
+ type: 'number',
346
+ role: STATE_ROLES.cost,
347
+ read: true,
348
+ write: false,
349
+ unit: '€',
350
+ def: 0,
351
+ },
352
+ native: {},
353
+ });
354
+ }
355
+
356
+ await adapter.setObjectNotExistsAsync(`${type}.costs.totalYearly`, {
357
+ type: 'state',
358
+ common: {
359
+ name: `Gesamt-${(label.cost || 'Kosten').toLowerCase()} Jahr (Verbrauch + Grundgebühr) (€)`,
360
+ type: 'number',
361
+ role: STATE_ROLES.cost,
362
+ read: true,
363
+ write: false,
364
+ unit: '€',
365
+ def: 0,
366
+ },
367
+ native: {},
368
+ });
369
+
370
+ await adapter.setObjectNotExistsAsync(`${type}.costs.annualFee`, {
371
+ type: 'state',
372
+ common: {
373
+ name: 'Jahresgebühr akkumuliert (€)',
374
+ type: 'number',
375
+ role: STATE_ROLES.cost,
376
+ read: true,
377
+ write: false,
378
+ unit: '€',
379
+ def: 0,
380
+ },
381
+ native: {},
382
+ });
383
+
384
+ await adapter.setObjectNotExistsAsync(`${type}.costs.basicCharge`, {
385
+ type: 'state',
386
+ common: {
387
+ name: 'Grundgebühr (€/Monat)',
388
+ type: 'number',
389
+ role: STATE_ROLES.cost,
390
+ read: true,
391
+ write: false,
392
+ unit: '€',
393
+ def: 0,
394
+ },
395
+ native: {},
396
+ });
397
+
398
+ await adapter.setObjectNotExistsAsync(`${type}.costs.paidTotal`, {
399
+ type: 'state',
400
+ common: {
401
+ name: 'Bezahlt gesamt (Abschlag × Monate)',
402
+ type: 'number',
403
+ role: STATE_ROLES.cost,
404
+ read: true,
405
+ write: false,
406
+ unit: '€',
407
+ def: 0,
408
+ },
409
+ native: {},
410
+ });
411
+
412
+ await adapter.setObjectNotExistsAsync(`${type}.costs.balance`, {
413
+ type: 'state',
414
+ common: {
415
+ name: 'Saldo (Bezahlt - Verbraucht)',
416
+ type: 'number',
417
+ role: STATE_ROLES.cost,
418
+ read: true,
419
+ write: false,
420
+ unit: '€',
421
+ def: 0,
422
+ },
423
+ native: {},
424
+ });
425
+
426
+ // BILLING STATES (Abrechnungszeitraum-Management)
427
+ await adapter.setObjectNotExistsAsync(`${type}.billing`, {
428
+ type: 'channel',
429
+ common: {
430
+ name: 'Abrechnungszeitraum',
431
+ },
432
+ native: {},
433
+ });
434
+
435
+ await adapter.setObjectNotExistsAsync(`${type}.billing.endReading`, {
436
+ type: 'state',
437
+ common: {
438
+ name: 'Endzählerstand (manuell eintragen)',
439
+ type: 'number',
440
+ role: STATE_ROLES.meterReading,
441
+ read: true,
442
+ write: true,
443
+ unit: label.volumeUnit,
444
+ def: 0,
445
+ },
446
+ native: {},
447
+ });
448
+
449
+ await adapter.setObjectNotExistsAsync(`${type}.billing.closePeriod`, {
450
+ type: 'state',
451
+ common: {
452
+ name: 'Zeitraum jetzt abschließen (Button)',
453
+ type: 'boolean',
454
+ role: 'button',
455
+ read: true,
456
+ write: true,
457
+ def: false,
458
+ },
459
+ native: {},
460
+ });
461
+
462
+ await adapter.setObjectNotExistsAsync(`${type}.billing.periodEnd`, {
463
+ type: 'state',
464
+ common: {
465
+ name: 'Abrechnungszeitraum endet am',
466
+ type: 'string',
467
+ role: 'text',
468
+ read: true,
469
+ write: false,
470
+ def: '',
471
+ },
472
+ native: {},
473
+ });
474
+
475
+ await adapter.setObjectNotExistsAsync(`${type}.billing.daysRemaining`, {
476
+ type: 'state',
477
+ common: {
478
+ name: 'Tage bis Abrechnungsende',
479
+ type: 'number',
480
+ role: 'value',
481
+ read: true,
482
+ write: false,
483
+ unit: 'Tage',
484
+ def: 0,
485
+ },
486
+ native: {},
487
+ });
488
+
489
+ await adapter.setObjectNotExistsAsync(`${type}.billing.newInitialReading`, {
490
+ type: 'state',
491
+ common: {
492
+ name: 'Neuer Startwert (für Config übernehmen!)',
493
+ type: 'number',
494
+ role: STATE_ROLES.meterReading,
495
+ read: true,
496
+ write: false,
497
+ unit: label.volumeUnit,
498
+ def: 0,
499
+ },
500
+ native: {},
501
+ });
502
+
503
+ await adapter.setObjectNotExistsAsync(`${type}.billing.notificationSent`, {
504
+ type: 'state',
505
+ common: {
506
+ name: 'Benachrichtigung Zählerstand versendet',
507
+ type: 'boolean',
508
+ role: 'indicator',
509
+ read: true,
510
+ write: false,
511
+ def: false,
512
+ },
513
+ native: {},
514
+ });
515
+
516
+ await adapter.setObjectNotExistsAsync(`${type}.billing.notificationChangeSent`, {
517
+ type: 'state',
518
+ common: {
519
+ name: 'Benachrichtigung Vertragswechsel versendet',
520
+ type: 'boolean',
521
+ role: 'indicator',
522
+ read: true,
523
+ write: false,
524
+ def: false,
525
+ },
526
+ native: {},
527
+ });
528
+
529
+ // ADJUSTMENT STATES (Manuelle Anpassung)
530
+ await adapter.setObjectNotExistsAsync(`${type}.adjustment`, {
531
+ type: 'channel',
532
+ common: {
533
+ name: 'Manuelle Anpassung',
534
+ },
535
+ native: {},
536
+ });
537
+
538
+ await adapter.setObjectNotExistsAsync(`${type}.adjustment.value`, {
539
+ type: 'state',
540
+ common: {
541
+ name: 'Korrekturwert (Differenz zum echten Zähler)',
542
+ type: 'number',
543
+ role: STATE_ROLES.value,
544
+ read: true,
545
+ write: true,
546
+ unit: label.volumeUnit,
547
+ def: 0,
548
+ },
549
+ native: {},
550
+ });
551
+
552
+ await adapter.setObjectNotExistsAsync(`${type}.adjustment.note`, {
553
+ type: 'state',
554
+ common: {
555
+ name: 'Notiz/Grund für Anpassung',
556
+ type: 'string',
557
+ role: 'text',
558
+ read: true,
559
+ write: true,
560
+ def: '',
561
+ },
562
+ native: {},
563
+ });
564
+
565
+ await adapter.setObjectNotExistsAsync(`${type}.adjustment.applied`, {
566
+ type: 'state',
567
+ common: {
568
+ name: 'Zeitstempel der letzten Anwendung',
569
+ type: 'number',
570
+ role: 'value.time',
571
+ read: true,
572
+ write: false,
573
+ def: 0,
574
+ },
575
+ native: {},
576
+ });
577
+
578
+ // INFO STATES
579
+ await adapter.setObjectNotExistsAsync(`${type}.info`, {
580
+ type: 'channel',
581
+ common: {
582
+ name: 'Informationen',
583
+ },
584
+ native: {},
585
+ });
586
+
587
+ // For gas, store volume in m³ separate from energy in kWh
588
+ if (isGas) {
589
+ await adapter.setObjectNotExistsAsync(`${type}.info.meterReadingVolume`, {
590
+ type: 'state',
591
+ common: {
592
+ name: `Zählerstand Volumen (${label.volumeUnit})`,
593
+ type: 'number',
594
+ role: STATE_ROLES.meterReading,
595
+ read: true,
596
+ write: false,
597
+ unit: label.volumeUnit,
598
+ def: 0,
599
+ },
600
+ native: {},
601
+ });
602
+ }
603
+
604
+ await adapter.setObjectNotExistsAsync(`${type}.info.meterReading`, {
605
+ type: 'state',
606
+ common: {
607
+ name: `Zählerstand (${label.unit})`,
608
+ type: 'number',
609
+ role: STATE_ROLES.meterReading,
610
+ read: true,
611
+ write: false,
612
+ unit: label.unit,
613
+ def: 0,
614
+ },
615
+ native: {},
616
+ });
617
+
618
+ await adapter.setObjectNotExistsAsync(`${type}.info.currentPrice`, {
619
+ type: 'state',
620
+ common: {
621
+ name: `Aktueller Preis (€/${label.unit})`,
622
+ type: 'number',
623
+ role: STATE_ROLES.price,
624
+ read: true,
625
+ write: false,
626
+ unit: `€/${label.unit}`,
627
+ def: 0,
628
+ },
629
+ native: {},
630
+ });
631
+
632
+ await adapter.setObjectNotExistsAsync(`${type}.info.lastSync`, {
633
+ type: 'state',
634
+ common: {
635
+ name: 'Letzte Synchronisation',
636
+ type: 'number',
637
+ role: STATE_ROLES.timestamp,
638
+ read: true,
639
+ write: false,
640
+ },
641
+ native: {},
642
+ });
643
+
644
+ await adapter.setObjectNotExistsAsync(`${type}.info.sensorActive`, {
645
+ type: 'state',
646
+ common: {
647
+ name: 'Sensor aktiv',
648
+ type: 'boolean',
649
+ role: STATE_ROLES.indicator,
650
+ read: true,
651
+ write: false,
652
+ def: false,
653
+ },
654
+ native: {},
655
+ });
656
+
657
+ await adapter.setObjectNotExistsAsync(`${type}.info.currentTariff`, {
658
+ type: 'state',
659
+ common: {
660
+ name: 'Aktueller Tarif (HT/NT)',
661
+ type: 'string',
662
+ role: 'text',
663
+ read: true,
664
+ write: false,
665
+ def: 'Standard',
666
+ },
667
+ native: {},
668
+ });
669
+
670
+ // STATISTICS STATES
671
+ await adapter.setObjectNotExistsAsync(`${type}.statistics`, {
672
+ type: 'channel',
673
+ common: {
674
+ name: 'Statistiken',
675
+ },
676
+ native: {},
677
+ });
678
+
679
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.averageDaily`, {
680
+ type: 'state',
681
+ common: {
682
+ name: `Durchschnitt pro Tag (${label.unit})`,
683
+ type: 'number',
684
+ role: STATE_ROLES.consumption,
685
+ read: true,
686
+ write: false,
687
+ unit: label.unit,
688
+ def: 0,
689
+ },
690
+ native: {},
691
+ });
692
+
693
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.averageMonthly`, {
694
+ type: 'state',
695
+ common: {
696
+ name: `Durchschnitt pro Monat (${label.unit})`,
697
+ type: 'number',
698
+ role: STATE_ROLES.consumption,
699
+ read: true,
700
+ write: false,
701
+ unit: label.unit,
702
+ def: 0,
703
+ },
704
+ native: {},
705
+ });
706
+
707
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastDay`, {
708
+ type: 'state',
709
+ common: {
710
+ name: `Verbrauch gestern (${label.unit})`,
711
+ type: 'number',
712
+ role: STATE_ROLES.consumption,
713
+ read: true,
714
+ write: false,
715
+ unit: label.unit,
716
+ def: 0,
717
+ },
718
+ native: {},
719
+ });
720
+
721
+ if (type === 'gas') {
722
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastDayVolume`, {
723
+ type: 'state',
724
+ common: {
725
+ name: `Verbrauch gestern (${label.volumeUnit})`,
726
+ type: 'number',
727
+ role: STATE_ROLES.consumption,
728
+ read: true,
729
+ write: false,
730
+ unit: label.volumeUnit,
731
+ def: 0,
732
+ },
733
+ native: {},
734
+ });
735
+ }
736
+
737
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastDayStart`, {
738
+ type: 'state',
739
+ common: {
740
+ name: 'Tageszähler zurückgesetzt am',
741
+ type: 'number',
742
+ role: STATE_ROLES.timestamp,
743
+ read: true,
744
+ write: false,
745
+ },
746
+ native: {},
747
+ });
748
+
749
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastMonthStart`, {
750
+ type: 'state',
751
+ common: {
752
+ name: 'Monatszähler zurückgesetzt am',
753
+ type: 'number',
754
+ role: STATE_ROLES.timestamp,
755
+ read: true,
756
+ write: false,
757
+ },
758
+ native: {},
759
+ });
760
+
761
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastYearStart`, {
762
+ type: 'state',
763
+ common: {
764
+ name: 'Jahreszähler zurückgesetzt am',
765
+ type: 'number',
766
+ role: STATE_ROLES.timestamp,
767
+ read: true,
768
+ write: false,
769
+ },
770
+ native: {},
771
+ });
772
+
773
+ adapter.log.debug(`State structure created for ${type}`);
774
+ }
775
+
776
+ /**
777
+ * Creates the state structure for a meter (main or additional)
778
+ * Can be used for main meter or any custom-named additional meter
779
+ *
780
+ * @param {object} adapter - The adapter instance
781
+ * @param {string} type - Utility type: 'gas', 'water', or 'electricity'
782
+ * @param {string} meterName - Meter name: 'main' or custom name (e.g., 'erdgeschoss')
783
+ * @param {object} _config - Configuration for this meter
784
+ * @returns {Promise<void>}
785
+ */
786
+ async function createMeterStructure(adapter, type, meterName, _config = {}) {
787
+ const isGas = type === 'gas';
788
+
789
+ const labels = {
790
+ gas: { name: meterName === 'main' ? 'Gas' : `Gas (${meterName})`, unit: 'kWh', volumeUnit: 'm³' },
791
+ water: { name: meterName === 'main' ? 'Wasser' : `Wasser (${meterName})`, unit: 'm³', volumeUnit: 'm³' },
792
+ electricity: { name: meterName === 'main' ? 'Strom' : `Strom (${meterName})`, unit: 'kWh', volumeUnit: 'kWh' },
793
+ pv: { name: meterName === 'main' ? 'PV' : `PV (${meterName})`, unit: 'kWh', volumeUnit: 'kWh' },
794
+ };
795
+
796
+ const label = labels[type];
797
+ if (!label) {
798
+ adapter.log.error(
799
+ `MISSING LABEL for type "${type}" in createMeterStructure! Meter: ${meterName}. Valid types: ${Object.keys(labels).join(', ')}`,
800
+ );
801
+ // Fallback to prevent crash
802
+ return;
803
+ }
804
+ const basePath = meterName === 'main' ? type : `${type}.${meterName}`;
805
+
806
+ // Create main channel
807
+ await adapter.setObjectNotExistsAsync(basePath, {
808
+ type: 'channel',
809
+ common: {
810
+ name: `${label.name}-Überwachung`,
811
+ },
812
+ native: {},
813
+ });
814
+
815
+ // CONSUMPTION STATES
816
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption`, {
817
+ type: 'channel',
818
+ common: {
819
+ name: 'Verbrauch',
820
+ },
821
+ native: {},
822
+ });
823
+
824
+ // For gas: add volume states (m³)
825
+ if (type === 'gas') {
826
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.dailyVolume`, {
827
+ type: 'state',
828
+ common: {
829
+ name: 'Täglicher Verbrauch (m³)',
830
+ type: 'number',
831
+ role: STATE_ROLES.consumption,
832
+ read: true,
833
+ write: false,
834
+ unit: 'm³',
835
+ def: 0,
836
+ },
837
+ native: {},
838
+ });
839
+
840
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.monthlyVolume`, {
841
+ type: 'state',
842
+ common: {
843
+ name: 'Monatlicher Verbrauch (m³)',
844
+ type: 'number',
845
+ role: STATE_ROLES.consumption,
846
+ read: true,
847
+ write: false,
848
+ unit: 'm³',
849
+ def: 0,
850
+ },
851
+ native: {},
852
+ });
853
+
854
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.yearlyVolume`, {
855
+ type: 'state',
856
+ common: {
857
+ name: 'Jährlicher Verbrauch (m³)',
858
+ type: 'number',
859
+ role: STATE_ROLES.consumption,
860
+ read: true,
861
+ write: false,
862
+ unit: 'm³',
863
+ def: 0,
864
+ },
865
+ native: {},
866
+ });
867
+ }
868
+
869
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.daily`, {
870
+ type: 'state',
871
+ common: {
872
+ name: `Tagesverbrauch (${label.unit})`,
873
+ type: 'number',
874
+ role: STATE_ROLES.consumption,
875
+ read: true,
876
+ write: false,
877
+ unit: label.unit,
878
+ def: 0,
879
+ },
880
+ native: {},
881
+ });
882
+
883
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.monthly`, {
884
+ type: 'state',
885
+ common: {
886
+ name: `Monatsverbrauch (${label.unit})`,
887
+ type: 'number',
888
+ role: STATE_ROLES.consumption,
889
+ read: true,
890
+ write: false,
891
+ unit: label.unit,
892
+ def: 0,
893
+ },
894
+ native: {},
895
+ });
896
+
897
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.yearly`, {
898
+ type: 'state',
899
+ common: {
900
+ name: `Jahresverbrauch (${label.unit})`,
901
+ type: 'number',
902
+ role: STATE_ROLES.consumption,
903
+ read: true,
904
+ write: false,
905
+ unit: label.unit,
906
+ def: 0,
907
+ },
908
+ native: {},
909
+ });
910
+
911
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.lastUpdate`, {
912
+ type: 'state',
913
+ common: {
914
+ name: 'Letzte Aktualisierung',
915
+ type: 'number',
916
+ role: STATE_ROLES.timestamp,
917
+ read: true,
918
+ write: false,
919
+ },
920
+ native: {},
921
+ });
922
+
923
+ // COST STATES
924
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs`, {
925
+ type: 'channel',
926
+ common: {
927
+ name: 'Kosten',
928
+ },
929
+ native: {},
930
+ });
931
+
932
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.daily`, {
933
+ type: 'state',
934
+ common: {
935
+ name: 'Tageskosten (€)',
936
+ type: 'number',
937
+ role: STATE_ROLES.cost,
938
+ read: true,
939
+ write: false,
940
+ unit: '€',
941
+ def: 0,
942
+ },
943
+ native: {},
944
+ });
945
+
946
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.monthly`, {
947
+ type: 'state',
948
+ common: {
949
+ name: 'Monatskosten (€)',
950
+ type: 'number',
951
+ role: STATE_ROLES.cost,
952
+ read: true,
953
+ write: false,
954
+ unit: '€',
955
+ def: 0,
956
+ },
957
+ native: {},
958
+ });
959
+
960
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.yearly`, {
961
+ type: 'state',
962
+ common: {
963
+ name: 'Jahreskosten (€)',
964
+ type: 'number',
965
+ role: STATE_ROLES.cost,
966
+ read: true,
967
+ write: false,
968
+ unit: '€',
969
+ def: 0,
970
+ },
971
+ native: {},
972
+ });
973
+
974
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.totalYearly`, {
975
+ type: 'state',
976
+ common: {
977
+ name: 'Gesamtkosten Jahr (Verbrauch + Grundgebühr) (€)',
978
+ type: 'number',
979
+ role: STATE_ROLES.cost,
980
+ read: true,
981
+ write: false,
982
+ unit: '€',
983
+ def: 0,
984
+ },
985
+ native: {},
986
+ });
987
+
988
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.annualFee`, {
989
+ type: 'state',
990
+ common: {
991
+ name: 'Jahresgebühr akkumuliert (€)',
992
+ type: 'number',
993
+ role: STATE_ROLES.cost,
994
+ read: true,
995
+ write: false,
996
+ unit: '€',
997
+ def: 0,
998
+ },
999
+ native: {},
1000
+ });
1001
+
1002
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.basicCharge`, {
1003
+ type: 'state',
1004
+ common: {
1005
+ name: 'Grundgebühr (€/Monat)',
1006
+ type: 'number',
1007
+ role: STATE_ROLES.cost,
1008
+ read: true,
1009
+ write: false,
1010
+ unit: '€',
1011
+ def: 0,
1012
+ },
1013
+ native: {},
1014
+ });
1015
+
1016
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.paidTotal`, {
1017
+ type: 'state',
1018
+ common: {
1019
+ name: 'Bezahlt gesamt (Abschlag × Monate)',
1020
+ type: 'number',
1021
+ role: STATE_ROLES.cost,
1022
+ read: true,
1023
+ write: false,
1024
+ unit: '€',
1025
+ def: 0,
1026
+ },
1027
+ native: {},
1028
+ });
1029
+
1030
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.balance`, {
1031
+ type: 'state',
1032
+ common: {
1033
+ name: 'Saldo (Bezahlt - Verbraucht)',
1034
+ type: 'number',
1035
+ role: STATE_ROLES.cost,
1036
+ read: true,
1037
+ write: false,
1038
+ unit: '€',
1039
+ def: 0,
1040
+ },
1041
+ native: {},
1042
+ });
1043
+
1044
+ // BILLING STATES
1045
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing`, {
1046
+ type: 'channel',
1047
+ common: {
1048
+ name: 'Abrechnungszeitraum',
1049
+ },
1050
+ native: {},
1051
+ });
1052
+
1053
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing.endReading`, {
1054
+ type: 'state',
1055
+ common: {
1056
+ name: 'Endzählerstand (manuell eintragen)',
1057
+ type: 'number',
1058
+ role: STATE_ROLES.meterReading,
1059
+ read: true,
1060
+ write: true,
1061
+ unit: label.volumeUnit,
1062
+ def: 0,
1063
+ },
1064
+ native: {},
1065
+ });
1066
+
1067
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing.closePeriod`, {
1068
+ type: 'state',
1069
+ common: {
1070
+ name: 'Zeitraum jetzt abschließen (Button)',
1071
+ type: 'boolean',
1072
+ role: 'button',
1073
+ read: true,
1074
+ write: true,
1075
+ def: false,
1076
+ },
1077
+ native: {},
1078
+ });
1079
+
1080
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing.periodEnd`, {
1081
+ type: 'state',
1082
+ common: {
1083
+ name: 'Abrechnungszeitraum endet am',
1084
+ type: 'string',
1085
+ role: 'text',
1086
+ read: true,
1087
+ write: false,
1088
+ def: '',
1089
+ },
1090
+ native: {},
1091
+ });
1092
+
1093
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing.daysRemaining`, {
1094
+ type: 'state',
1095
+ common: {
1096
+ name: 'Tage bis Abrechnungsende',
1097
+ type: 'number',
1098
+ role: 'value',
1099
+ read: true,
1100
+ write: false,
1101
+ unit: 'Tage',
1102
+ def: 0,
1103
+ },
1104
+ native: {},
1105
+ });
1106
+
1107
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing.newInitialReading`, {
1108
+ type: 'state',
1109
+ common: {
1110
+ name: 'Neuer Startwert (für Config übernehmen!)',
1111
+ type: 'number',
1112
+ role: STATE_ROLES.meterReading,
1113
+ read: true,
1114
+ write: false,
1115
+ unit: label.volumeUnit,
1116
+ def: 0,
1117
+ },
1118
+ native: {},
1119
+ });
1120
+
1121
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing.notificationSent`, {
1122
+ type: 'state',
1123
+ common: {
1124
+ name: 'Benachrichtigung Zählerstand versendet',
1125
+ type: 'boolean',
1126
+ role: 'indicator',
1127
+ read: true,
1128
+ write: false,
1129
+ def: false,
1130
+ },
1131
+ native: {},
1132
+ });
1133
+
1134
+ await adapter.setObjectNotExistsAsync(`${basePath}.billing.notificationChangeSent`, {
1135
+ type: 'state',
1136
+ common: {
1137
+ name: 'Benachrichtigung Vertragswechsel versendet',
1138
+ type: 'boolean',
1139
+ role: 'indicator',
1140
+ read: true,
1141
+ write: false,
1142
+ def: false,
1143
+ },
1144
+ native: {},
1145
+ });
1146
+
1147
+ // ADJUSTMENT STATES
1148
+ await adapter.setObjectNotExistsAsync(`${basePath}.adjustment`, {
1149
+ type: 'channel',
1150
+ common: {
1151
+ name: 'Manuelle Anpassung',
1152
+ },
1153
+ native: {},
1154
+ });
1155
+
1156
+ await adapter.setObjectNotExistsAsync(`${basePath}.adjustment.value`, {
1157
+ type: 'state',
1158
+ common: {
1159
+ name: 'Korrekturwert (Differenz zum echten Zähler)',
1160
+ type: 'number',
1161
+ role: STATE_ROLES.value,
1162
+ read: true,
1163
+ write: true,
1164
+ unit: label.volumeUnit,
1165
+ def: 0,
1166
+ },
1167
+ native: {},
1168
+ });
1169
+
1170
+ await adapter.setObjectNotExistsAsync(`${basePath}.adjustment.note`, {
1171
+ type: 'state',
1172
+ common: {
1173
+ name: 'Notiz/Grund für Anpassung',
1174
+ type: 'string',
1175
+ role: 'text',
1176
+ read: true,
1177
+ write: true,
1178
+ def: '',
1179
+ },
1180
+ native: {},
1181
+ });
1182
+
1183
+ await adapter.setObjectNotExistsAsync(`${basePath}.adjustment.applied`, {
1184
+ type: 'state',
1185
+ common: {
1186
+ name: 'Zeitstempel der letzten Anwendung',
1187
+ type: 'number',
1188
+ role: 'value.time',
1189
+ read: true,
1190
+ write: false,
1191
+ def: 0,
1192
+ },
1193
+ native: {},
1194
+ });
1195
+
1196
+ // INFO STATES
1197
+ await adapter.setObjectNotExistsAsync(`${basePath}.info`, {
1198
+ type: 'channel',
1199
+ common: {
1200
+ name: 'Informationen',
1201
+ },
1202
+ native: {},
1203
+ });
1204
+
1205
+ // For gas, store volume in m³ separate from energy in kWh
1206
+ if (isGas) {
1207
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.meterReadingVolume`, {
1208
+ type: 'state',
1209
+ common: {
1210
+ name: `Zählerstand Volumen (${label.volumeUnit})`,
1211
+ type: 'number',
1212
+ role: STATE_ROLES.meterReading,
1213
+ read: true,
1214
+ write: false,
1215
+ unit: label.volumeUnit,
1216
+ def: 0,
1217
+ },
1218
+ native: {},
1219
+ });
1220
+ }
1221
+
1222
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.meterReading`, {
1223
+ type: 'state',
1224
+ common: {
1225
+ name: `Zählerstand (${label.unit})`,
1226
+ type: 'number',
1227
+ role: STATE_ROLES.meterReading,
1228
+ read: true,
1229
+ write: false,
1230
+ unit: label.unit,
1231
+ def: 0,
1232
+ },
1233
+ native: {},
1234
+ });
1235
+
1236
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.currentPrice`, {
1237
+ type: 'state',
1238
+ common: {
1239
+ name: `Aktueller Preis (€/${label.unit})`,
1240
+ type: 'number',
1241
+ role: STATE_ROLES.price,
1242
+ read: true,
1243
+ write: false,
1244
+ unit: `€/${label.unit}`,
1245
+ def: 0,
1246
+ },
1247
+ native: {},
1248
+ });
1249
+
1250
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.lastSync`, {
1251
+ type: 'state',
1252
+ common: {
1253
+ name: 'Letzte Synchronisation',
1254
+ type: 'number',
1255
+ role: STATE_ROLES.timestamp,
1256
+ read: true,
1257
+ write: false,
1258
+ },
1259
+ native: {},
1260
+ });
1261
+
1262
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.sensorActive`, {
1263
+ type: 'state',
1264
+ common: {
1265
+ name: 'Sensor aktiv',
1266
+ type: 'boolean',
1267
+ role: STATE_ROLES.indicator,
1268
+ read: true,
1269
+ write: false,
1270
+ def: false,
1271
+ },
1272
+ native: {},
1273
+ });
1274
+
1275
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.currentTariff`, {
1276
+ type: 'state',
1277
+ common: {
1278
+ name: 'Aktueller Tarif',
1279
+ type: 'string',
1280
+ role: 'text',
1281
+ read: true,
1282
+ write: false,
1283
+ def: 'Standard',
1284
+ },
1285
+ native: {},
1286
+ });
1287
+
1288
+ // STATISTICS STATES
1289
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics`, {
1290
+ type: 'channel',
1291
+ common: {
1292
+ name: 'Statistiken',
1293
+ },
1294
+ native: {},
1295
+ });
1296
+
1297
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.averageDaily`, {
1298
+ type: 'state',
1299
+ common: {
1300
+ name: `Durchschnitt pro Tag (${label.unit})`,
1301
+ type: 'number',
1302
+ role: STATE_ROLES.consumption,
1303
+ read: true,
1304
+ write: false,
1305
+ unit: label.unit,
1306
+ def: 0,
1307
+ },
1308
+ native: {},
1309
+ });
1310
+
1311
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.averageMonthly`, {
1312
+ type: 'state',
1313
+ common: {
1314
+ name: `Durchschnitt pro Monat (${label.unit})`,
1315
+ type: 'number',
1316
+ role: STATE_ROLES.consumption,
1317
+ read: true,
1318
+ write: false,
1319
+ unit: label.unit,
1320
+ def: 0,
1321
+ },
1322
+ native: {},
1323
+ });
1324
+
1325
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastDay`, {
1326
+ type: 'state',
1327
+ common: {
1328
+ name: `Verbrauch gestern (${label.unit})`,
1329
+ type: 'number',
1330
+ role: STATE_ROLES.consumption,
1331
+ read: true,
1332
+ write: false,
1333
+ unit: label.unit,
1334
+ def: 0,
1335
+ },
1336
+ native: {},
1337
+ });
1338
+
1339
+ if (type === 'gas') {
1340
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastDayVolume`, {
1341
+ type: 'state',
1342
+ common: {
1343
+ name: `Verbrauch gestern (${label.volumeUnit})`,
1344
+ type: 'number',
1345
+ role: STATE_ROLES.consumption,
1346
+ read: true,
1347
+ write: false,
1348
+ unit: label.volumeUnit,
1349
+ def: 0,
1350
+ },
1351
+ native: {},
1352
+ });
1353
+ }
1354
+
1355
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastDayStart`, {
1356
+ type: 'state',
1357
+ common: {
1358
+ name: 'Tageszähler zurückgesetzt am',
1359
+ type: 'number',
1360
+ role: STATE_ROLES.timestamp,
1361
+ read: true,
1362
+ write: false,
1363
+ },
1364
+ native: {},
1365
+ });
1366
+
1367
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastMonthStart`, {
1368
+ type: 'state',
1369
+ common: {
1370
+ name: 'Monatszähler zurückgesetzt am',
1371
+ type: 'number',
1372
+ role: STATE_ROLES.timestamp,
1373
+ read: true,
1374
+ write: false,
1375
+ },
1376
+ native: {},
1377
+ });
1378
+
1379
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastYearStart`, {
1380
+ type: 'state',
1381
+ common: {
1382
+ name: 'Jahreszähler zurückgesetzt am',
1383
+ type: 'number',
1384
+ role: STATE_ROLES.timestamp,
1385
+ read: true,
1386
+ write: false,
1387
+ },
1388
+ native: {},
1389
+ });
1390
+
1391
+ adapter.log.debug(`Meter state structure created for ${type}.${meterName}`);
1392
+ }
1393
+
1394
+ /**
1395
+ * Deletes all states for a utility type
1396
+ *
1397
+ * @param {object} adapter - The adapter instance
1398
+ * @param {string} type - Utility type: 'gas', 'water', or 'electricity'
1399
+ * @returns {Promise<void>}
1400
+ */
1401
+ async function deleteUtilityStateStructure(adapter, type) {
1402
+ try {
1403
+ await adapter.delObjectAsync(type, { recursive: true });
1404
+ adapter.log.debug(`State structure deleted for ${type}`);
1405
+ } catch (error) {
1406
+ adapter.log.warn(`Could not delete state structure for ${type}: ${error.message}`);
1407
+ }
1408
+ }
1409
+
1410
+ /**
1411
+ * Creates the totals state structure for a utility type
1412
+ * Totals show the sum of all meters (main + additional)
1413
+ *
1414
+ * @param {object} adapter - The adapter instance
1415
+ * @param {string} type - Utility type: 'gas', 'water', or 'electricity'
1416
+ * @returns {Promise<void>}
1417
+ */
1418
+ async function createTotalsStructure(adapter, type) {
1419
+ const labels = {
1420
+ gas: { name: 'Gas (Gesamt)', unit: 'kWh' },
1421
+ water: { name: 'Wasser (Gesamt)', unit: 'm³' },
1422
+ electricity: { name: 'Strom (Gesamt)', unit: 'kWh' },
1423
+ pv: { name: 'PV (Gesamt)', unit: 'kWh' },
1424
+ };
1425
+
1426
+ const label = labels[type];
1427
+ if (!label) {
1428
+ adapter.log.error(
1429
+ `MISSING LABEL for type "${type}" in createTotalsStructure! Valid types: ${Object.keys(labels).join(', ')}`,
1430
+ );
1431
+ return;
1432
+ }
1433
+ const basePath = `${type}.totals`;
1434
+
1435
+ // Create main channel
1436
+ await adapter.setObjectNotExistsAsync(basePath, {
1437
+ type: 'channel',
1438
+ common: {
1439
+ name: `${label.name} - Summe aller Zähler`,
1440
+ },
1441
+ native: {},
1442
+ });
1443
+
1444
+ // CONSUMPTION STATES (totals)
1445
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption`, {
1446
+ type: 'channel',
1447
+ common: {
1448
+ name: 'Gesamtverbrauch',
1449
+ },
1450
+ native: {},
1451
+ });
1452
+
1453
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.daily`, {
1454
+ type: 'state',
1455
+ common: {
1456
+ name: `Tagesverbrauch Gesamt (${label.unit})`,
1457
+ type: 'number',
1458
+ role: STATE_ROLES.consumption,
1459
+ read: true,
1460
+ write: false,
1461
+ unit: label.unit,
1462
+ def: 0,
1463
+ },
1464
+ native: {},
1465
+ });
1466
+
1467
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.monthly`, {
1468
+ type: 'state',
1469
+ common: {
1470
+ name: `Monatsverbrauch Gesamt (${label.unit})`,
1471
+ type: 'number',
1472
+ role: STATE_ROLES.consumption,
1473
+ read: true,
1474
+ write: false,
1475
+ unit: label.unit,
1476
+ def: 0,
1477
+ },
1478
+ native: {},
1479
+ });
1480
+
1481
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.yearly`, {
1482
+ type: 'state',
1483
+ common: {
1484
+ name: `Jahresverbrauch Gesamt (${label.unit})`,
1485
+ type: 'number',
1486
+ role: STATE_ROLES.consumption,
1487
+ read: true,
1488
+ write: false,
1489
+ unit: label.unit,
1490
+ def: 0,
1491
+ },
1492
+ native: {},
1493
+ });
1494
+
1495
+ // COST STATES (totals)
1496
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs`, {
1497
+ type: 'channel',
1498
+ common: {
1499
+ name: 'Gesamtkosten',
1500
+ },
1501
+ native: {},
1502
+ });
1503
+
1504
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.daily`, {
1505
+ type: 'state',
1506
+ common: {
1507
+ name: 'Tageskosten Gesamt (€)',
1508
+ type: 'number',
1509
+ role: STATE_ROLES.cost,
1510
+ read: true,
1511
+ write: false,
1512
+ unit: '€',
1513
+ def: 0,
1514
+ },
1515
+ native: {},
1516
+ });
1517
+
1518
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.monthly`, {
1519
+ type: 'state',
1520
+ common: {
1521
+ name: 'Monatskosten Gesamt (€)',
1522
+ type: 'number',
1523
+ role: STATE_ROLES.cost,
1524
+ read: true,
1525
+ write: false,
1526
+ unit: '€',
1527
+ def: 0,
1528
+ },
1529
+ native: {},
1530
+ });
1531
+
1532
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.totalYearly`, {
1533
+ type: 'state',
1534
+ common: {
1535
+ name: 'Jahreskosten Gesamt (€)',
1536
+ type: 'number',
1537
+ role: STATE_ROLES.cost,
1538
+ read: true,
1539
+ write: false,
1540
+ unit: '€',
1541
+ def: 0,
1542
+ },
1543
+ native: {},
1544
+ });
1545
+
1546
+ adapter.log.debug(`Totals state structure created for ${type}`);
1547
+ }
1548
+
1549
+ module.exports = {
1550
+ createUtilityStateStructure,
1551
+ createMeterStructure,
1552
+ createTotalsStructure,
1553
+ deleteUtilityStateStructure,
1554
+ safeSetObjectNotExists,
1555
+ STATE_ROLES,
1556
+ };