iobroker.utility-monitor 1.4.5 → 1.5.0

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.
Files changed (35) hide show
  1. package/README.md +159 -44
  2. package/admin/custom/.vite/manifest.json +90 -0
  3. package/admin/custom/@mf-types/Components.d.ts +2 -0
  4. package/admin/custom/@mf-types/compiled-types/Components/CSVImporter.d.ts +11 -0
  5. package/admin/custom/@mf-types/compiled-types/Components.d.ts +2 -0
  6. package/admin/custom/@mf-types.d.ts +3 -0
  7. package/admin/custom/@mf-types.zip +0 -0
  8. package/admin/custom/CSVImporter_v15_11.js +4415 -0
  9. package/admin/custom/assets/Components-i0AZ59nl.js +18887 -0
  10. package/admin/custom/assets/UtilityMonitor__loadShare__react__loadShare__-Da99Mak4.js +42 -0
  11. package/admin/custom/assets/UtilityMonitor__mf_v__runtimeInit__mf_v__-BmC4OGk6.js +16 -0
  12. package/admin/custom/assets/_commonjsHelpers-Dj2_voLF.js +30 -0
  13. package/admin/custom/assets/hostInit-DEXfeB0W.js +10 -0
  14. package/admin/custom/assets/index-B3WVNJTz.js +401 -0
  15. package/admin/custom/assets/index-VBwl8x_k.js +64 -0
  16. package/admin/custom/assets/preload-helper-BelkbqnE.js +61 -0
  17. package/admin/custom/assets/virtualExposes-CqCLUNLT.js +19 -0
  18. package/admin/custom/index.html +12 -0
  19. package/admin/custom/mf-manifest.json +1 -0
  20. package/admin/jsonConfig.json +219 -35
  21. package/io-package.json +51 -2
  22. package/lib/billingManager.js +276 -170
  23. package/lib/calculator.js +19 -138
  24. package/lib/consumptionManager.js +48 -331
  25. package/lib/importManager.js +300 -0
  26. package/lib/messagingHandler.js +112 -49
  27. package/lib/meter/MeterRegistry.js +110 -0
  28. package/lib/multiMeterManager.js +410 -181
  29. package/lib/stateManager.js +508 -36
  30. package/lib/utils/billingHelper.js +69 -0
  31. package/lib/utils/consumptionHelper.js +47 -0
  32. package/lib/utils/helpers.js +178 -0
  33. package/lib/utils/typeMapper.js +19 -0
  34. package/main.js +99 -36
  35. package/package.json +10 -4
@@ -1,28 +1,7 @@
1
1
  /**
2
- * State Manager module for nebenkosten-monitor
3
2
  * Manages the creation and structure of all adapter states
4
3
  */
5
4
 
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
5
  /**
27
6
  * State role definitions for different state types
28
7
  */
@@ -162,6 +141,20 @@ async function createUtilityStateStructure(adapter, type, _config = {}) {
162
141
  native: {},
163
142
  });
164
143
 
144
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.weekly`, {
145
+ type: 'state',
146
+ common: {
147
+ name: `Wochen-${(label.consumption || 'Verbrauch').toLowerCase()} (${label.unit})`,
148
+ type: 'number',
149
+ role: STATE_ROLES.consumption,
150
+ read: true,
151
+ write: false,
152
+ unit: label.unit,
153
+ def: 0,
154
+ },
155
+ native: {},
156
+ });
157
+
165
158
  // Map internal type to config type (electricity -> strom, water -> wasser, gas -> gas)
166
159
  const configTypeMap = {
167
160
  electricity: 'strom',
@@ -200,6 +193,28 @@ async function createUtilityStateStructure(adapter, type, _config = {}) {
200
193
  native: {},
201
194
  });
202
195
  }
196
+
197
+ const htNtWeeklyStates = ['weeklyHT', 'weeklyNT'];
198
+ const htNtWeeklyLabels = {
199
+ weeklyHT: 'Wochenverbrauch Haupttarif (HT)',
200
+ weeklyNT: 'Wochenverbrauch Nebentarif (NT)',
201
+ };
202
+
203
+ for (const state of htNtWeeklyStates) {
204
+ await adapter.setObjectNotExistsAsync(`${type}.consumption.${state}`, {
205
+ type: 'state',
206
+ common: {
207
+ name: `${htNtWeeklyLabels[state]} (${label.unit})`,
208
+ type: 'number',
209
+ role: STATE_ROLES.consumption,
210
+ read: true,
211
+ write: false,
212
+ unit: label.unit,
213
+ def: 0,
214
+ },
215
+ native: {},
216
+ });
217
+ }
203
218
  }
204
219
 
205
220
  await adapter.setObjectNotExistsAsync(`${type}.consumption.lastUpdate`, {
@@ -265,6 +280,20 @@ async function createUtilityStateStructure(adapter, type, _config = {}) {
265
280
  native: {},
266
281
  });
267
282
 
283
+ await adapter.setObjectNotExistsAsync(`${type}.costs.weekly`, {
284
+ type: 'state',
285
+ common: {
286
+ name: `Wochen-${(label.cost || 'Kosten').toLowerCase()} (€)`,
287
+ type: 'number',
288
+ role: STATE_ROLES.cost,
289
+ read: true,
290
+ write: false,
291
+ unit: '€',
292
+ def: 0,
293
+ },
294
+ native: {},
295
+ });
296
+
268
297
  // HT/NT states - only create if HT/NT tariff is enabled
269
298
  // Note: htNtEnabledKey already calculated above for consumption section
270
299
  if (_config[htNtEnabledKey]) {
@@ -351,6 +380,34 @@ async function createUtilityStateStructure(adapter, type, _config = {}) {
351
380
  },
352
381
  native: {},
353
382
  });
383
+
384
+ await adapter.setObjectNotExistsAsync(`${type}.costs.weeklyHT`, {
385
+ type: 'state',
386
+ common: {
387
+ name: 'Wochenkosten Haupttarif (HT) (€)',
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.weeklyNT`, {
399
+ type: 'state',
400
+ common: {
401
+ name: 'Wochenkosten Nebentarif (NT) (€)',
402
+ type: 'number',
403
+ role: STATE_ROLES.cost,
404
+ read: true,
405
+ write: false,
406
+ unit: '€',
407
+ def: 0,
408
+ },
409
+ native: {},
410
+ });
354
411
  }
355
412
 
356
413
  await adapter.setObjectNotExistsAsync(`${type}.costs.totalYearly`, {
@@ -765,6 +822,68 @@ async function createUtilityStateStructure(adapter, type, _config = {}) {
765
822
  });
766
823
  }
767
824
 
825
+ // Last week consumption
826
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastWeek`, {
827
+ type: 'state',
828
+ common: {
829
+ name: `Verbrauch letzte Woche (${label.unit})`,
830
+ type: 'number',
831
+ role: STATE_ROLES.consumption,
832
+ read: true,
833
+ write: false,
834
+ unit: label.unit,
835
+ def: 0,
836
+ },
837
+ native: {},
838
+ });
839
+
840
+ if (type === 'gas') {
841
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastWeekVolume`, {
842
+ type: 'state',
843
+ common: {
844
+ name: `Verbrauch letzte Woche (${label.volumeUnit})`,
845
+ type: 'number',
846
+ role: STATE_ROLES.consumption,
847
+ read: true,
848
+ write: false,
849
+ unit: label.volumeUnit,
850
+ def: 0,
851
+ },
852
+ native: {},
853
+ });
854
+ }
855
+
856
+ // Last month consumption
857
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastMonth`, {
858
+ type: 'state',
859
+ common: {
860
+ name: `Verbrauch letzter Monat (${label.unit})`,
861
+ type: 'number',
862
+ role: STATE_ROLES.consumption,
863
+ read: true,
864
+ write: false,
865
+ unit: label.unit,
866
+ def: 0,
867
+ },
868
+ native: {},
869
+ });
870
+
871
+ if (type === 'gas') {
872
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastMonthVolume`, {
873
+ type: 'state',
874
+ common: {
875
+ name: `Verbrauch letzter Monat (${label.volumeUnit})`,
876
+ type: 'number',
877
+ role: STATE_ROLES.consumption,
878
+ read: true,
879
+ write: false,
880
+ unit: label.volumeUnit,
881
+ def: 0,
882
+ },
883
+ native: {},
884
+ });
885
+ }
886
+
768
887
  await adapter.setObjectNotExistsAsync(`${type}.statistics.lastDayStart`, {
769
888
  type: 'state',
770
889
  common: {
@@ -777,6 +896,18 @@ async function createUtilityStateStructure(adapter, type, _config = {}) {
777
896
  native: {},
778
897
  });
779
898
 
899
+ await adapter.setObjectNotExistsAsync(`${type}.statistics.lastWeekStart`, {
900
+ type: 'state',
901
+ common: {
902
+ name: 'Wochenzähler zurückgesetzt am',
903
+ type: 'number',
904
+ role: STATE_ROLES.timestamp,
905
+ read: true,
906
+ write: false,
907
+ },
908
+ native: {},
909
+ });
910
+
780
911
  await adapter.setObjectNotExistsAsync(`${type}.statistics.lastMonthStart`, {
781
912
  type: 'state',
782
913
  common: {
@@ -817,11 +948,12 @@ async function createUtilityStateStructure(adapter, type, _config = {}) {
817
948
  async function createMeterStructure(adapter, type, meterName, _config = {}) {
818
949
  const isGas = type === 'gas';
819
950
 
951
+ // All meters now include the meter name in labels for consistency
820
952
  const labels = {
821
- gas: { name: meterName === 'main' ? 'Gas' : `Gas (${meterName})`, unit: 'kWh', volumeUnit: 'm³' },
822
- water: { name: meterName === 'main' ? 'Wasser' : `Wasser (${meterName})`, unit: 'm³', volumeUnit: 'm³' },
823
- electricity: { name: meterName === 'main' ? 'Strom' : `Strom (${meterName})`, unit: 'kWh', volumeUnit: 'kWh' },
824
- pv: { name: meterName === 'main' ? 'PV' : `PV (${meterName})`, unit: 'kWh', volumeUnit: 'kWh' },
953
+ gas: { name: `Gas (${meterName})`, unit: 'kWh', volumeUnit: 'm³' },
954
+ water: { name: `Wasser (${meterName})`, unit: 'm³', volumeUnit: 'm³' },
955
+ electricity: { name: `Strom (${meterName})`, unit: 'kWh', volumeUnit: 'kWh' },
956
+ pv: { name: `PV (${meterName})`, unit: 'kWh', volumeUnit: 'kWh' },
825
957
  };
826
958
 
827
959
  const label = labels[type];
@@ -832,7 +964,7 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
832
964
  // Fallback to prevent crash
833
965
  return;
834
966
  }
835
- const basePath = meterName === 'main' ? type : `${type}.${meterName}`;
967
+ const basePath = `${type}.${meterName}`;
836
968
 
837
969
  // Create main channel
838
970
  await adapter.setObjectNotExistsAsync(basePath, {
@@ -895,6 +1027,20 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
895
1027
  },
896
1028
  native: {},
897
1029
  });
1030
+
1031
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.weeklyVolume`, {
1032
+ type: 'state',
1033
+ common: {
1034
+ name: 'Wöchentlicher Verbrauch (m³)',
1035
+ type: 'number',
1036
+ role: STATE_ROLES.consumption,
1037
+ read: true,
1038
+ write: false,
1039
+ unit: 'm³',
1040
+ def: 0,
1041
+ },
1042
+ native: {},
1043
+ });
898
1044
  }
899
1045
 
900
1046
  await adapter.setObjectNotExistsAsync(`${basePath}.consumption.daily`, {
@@ -939,6 +1085,65 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
939
1085
  native: {},
940
1086
  });
941
1087
 
1088
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.weekly`, {
1089
+ type: 'state',
1090
+ common: {
1091
+ name: `Wochenverbrauch (${label.unit})`,
1092
+ type: 'number',
1093
+ role: STATE_ROLES.consumption,
1094
+ read: true,
1095
+ write: false,
1096
+ unit: label.unit,
1097
+ def: 0,
1098
+ },
1099
+ native: {},
1100
+ });
1101
+
1102
+ if (_config.htNtEnabled) {
1103
+ const htNtStates = [
1104
+ 'dailyHT',
1105
+ 'dailyNT',
1106
+ 'monthlyHT',
1107
+ 'monthlyNT',
1108
+ 'yearlyHT',
1109
+ 'yearlyNT',
1110
+ 'weeklyHT',
1111
+ 'weeklyNT',
1112
+ ];
1113
+ const htNtLabels = {
1114
+ dailyHT: 'Tagesverbrauch Haupttarif (HT)',
1115
+ dailyNT: 'Tagesverbrauch Nebentarif (NT)',
1116
+ monthlyHT: 'Monatsverbrauch Haupttarif (HT)',
1117
+ monthlyNT: 'Monatsverbrauch Nebentarif (NT)',
1118
+ yearlyHT: 'Jahresverbrauch Haupttarif (HT)',
1119
+ yearlyNT: 'Jahresverbrauch Nebentarif (NT)',
1120
+ weeklyHT: 'Wochenverbrauch Haupttarif (HT)',
1121
+ weeklyNT: 'Wochenverbrauch Nebentarif (NT)',
1122
+ weeklyVolumeHT: 'Wochenverbrauch Haupttarif (m³)',
1123
+ weeklyVolumeNT: 'Wochenverbrauch Nebentarif (m³)',
1124
+ };
1125
+
1126
+ if (type === 'gas') {
1127
+ htNtStates.push('weeklyVolumeHT', 'weeklyVolumeNT');
1128
+ }
1129
+
1130
+ for (const state of htNtStates) {
1131
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.${state}`, {
1132
+ type: 'state',
1133
+ common: {
1134
+ name: `${htNtLabels[state]} (${label.unit})`,
1135
+ type: 'number',
1136
+ role: STATE_ROLES.consumption,
1137
+ read: true,
1138
+ write: false,
1139
+ unit: label.unit,
1140
+ def: 0,
1141
+ },
1142
+ native: {},
1143
+ });
1144
+ }
1145
+ }
1146
+
942
1147
  await adapter.setObjectNotExistsAsync(`${basePath}.consumption.lastUpdate`, {
943
1148
  type: 'state',
944
1149
  common: {
@@ -1002,6 +1207,59 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1002
1207
  native: {},
1003
1208
  });
1004
1209
 
1210
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.weekly`, {
1211
+ type: 'state',
1212
+ common: {
1213
+ name: 'Wochenkosten (€)',
1214
+ type: 'number',
1215
+ role: STATE_ROLES.cost,
1216
+ read: true,
1217
+ write: false,
1218
+ unit: '€',
1219
+ def: 0,
1220
+ },
1221
+ native: {},
1222
+ });
1223
+
1224
+ if (_config.htNtEnabled) {
1225
+ const htNtCostStates = [
1226
+ 'dailyHT',
1227
+ 'dailyNT',
1228
+ 'monthlyHT',
1229
+ 'monthlyNT',
1230
+ 'yearlyHT',
1231
+ 'yearlyNT',
1232
+ 'weeklyHT',
1233
+ 'weeklyNT',
1234
+ ];
1235
+ const htNtCostLabels = {
1236
+ dailyHT: 'Tageskosten Haupttarif (HT)',
1237
+ dailyNT: 'Tageskosten Nebentarif (NT)',
1238
+ monthlyHT: 'Monatskosten Haupttarif (HT)',
1239
+ monthlyNT: 'Monatskosten Nebentarif (NT)',
1240
+ yearlyHT: 'Jahreskosten Haupttarif (HT)',
1241
+ yearlyNT: 'Jahreskosten Nebentarif (NT)',
1242
+ weeklyHT: 'Wochenkosten Haupttarif (HT)',
1243
+ weeklyNT: 'Wochenkosten Nebentarif (NT)',
1244
+ };
1245
+
1246
+ for (const state of htNtCostStates) {
1247
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.${state}`, {
1248
+ type: 'state',
1249
+ common: {
1250
+ name: `${htNtCostLabels[state]} (€)`,
1251
+ type: 'number',
1252
+ role: STATE_ROLES.cost,
1253
+ read: true,
1254
+ write: false,
1255
+ unit: '€',
1256
+ def: 0,
1257
+ },
1258
+ native: {},
1259
+ });
1260
+ }
1261
+ }
1262
+
1005
1263
  await adapter.setObjectNotExistsAsync(`${basePath}.costs.totalYearly`, {
1006
1264
  type: 'state',
1007
1265
  common: {
@@ -1278,27 +1536,27 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1278
1536
  native: {},
1279
1537
  });
1280
1538
 
1281
- await adapter.setObjectNotExistsAsync(`${basePath}.info.lastSync`, {
1539
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.sensorActive`, {
1282
1540
  type: 'state',
1283
1541
  common: {
1284
- name: 'Letzte Synchronisation',
1285
- type: 'number',
1286
- role: STATE_ROLES.timestamp,
1542
+ name: 'Sensor aktiv',
1543
+ type: 'boolean',
1544
+ role: 'indicator.reachable',
1287
1545
  read: true,
1288
1546
  write: false,
1547
+ def: false,
1289
1548
  },
1290
1549
  native: {},
1291
1550
  });
1292
1551
 
1293
- await adapter.setObjectNotExistsAsync(`${basePath}.info.sensorActive`, {
1552
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.lastSync`, {
1294
1553
  type: 'state',
1295
1554
  common: {
1296
- name: 'Sensor aktiv',
1297
- type: 'boolean',
1298
- role: STATE_ROLES.indicator,
1555
+ name: 'Letzte Synchronisation',
1556
+ type: 'number',
1557
+ role: STATE_ROLES.timestamp,
1299
1558
  read: true,
1300
1559
  write: false,
1301
- def: false,
1302
1560
  },
1303
1561
  native: {},
1304
1562
  });
@@ -1383,6 +1641,68 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1383
1641
  });
1384
1642
  }
1385
1643
 
1644
+ // Last week consumption
1645
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastWeek`, {
1646
+ type: 'state',
1647
+ common: {
1648
+ name: `Verbrauch letzte Woche (${label.unit})`,
1649
+ type: 'number',
1650
+ role: STATE_ROLES.consumption,
1651
+ read: true,
1652
+ write: false,
1653
+ unit: label.unit,
1654
+ def: 0,
1655
+ },
1656
+ native: {},
1657
+ });
1658
+
1659
+ if (type === 'gas') {
1660
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastWeekVolume`, {
1661
+ type: 'state',
1662
+ common: {
1663
+ name: `Verbrauch letzte Woche (${label.volumeUnit})`,
1664
+ type: 'number',
1665
+ role: STATE_ROLES.consumption,
1666
+ read: true,
1667
+ write: false,
1668
+ unit: label.volumeUnit,
1669
+ def: 0,
1670
+ },
1671
+ native: {},
1672
+ });
1673
+ }
1674
+
1675
+ // Last month consumption
1676
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastMonth`, {
1677
+ type: 'state',
1678
+ common: {
1679
+ name: `Verbrauch letzter Monat (${label.unit})`,
1680
+ type: 'number',
1681
+ role: STATE_ROLES.consumption,
1682
+ read: true,
1683
+ write: false,
1684
+ unit: label.unit,
1685
+ def: 0,
1686
+ },
1687
+ native: {},
1688
+ });
1689
+
1690
+ if (type === 'gas') {
1691
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastMonthVolume`, {
1692
+ type: 'state',
1693
+ common: {
1694
+ name: `Verbrauch letzter Monat (${label.volumeUnit})`,
1695
+ type: 'number',
1696
+ role: STATE_ROLES.consumption,
1697
+ read: true,
1698
+ write: false,
1699
+ unit: label.volumeUnit,
1700
+ def: 0,
1701
+ },
1702
+ native: {},
1703
+ });
1704
+ }
1705
+
1386
1706
  await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastDayStart`, {
1387
1707
  type: 'state',
1388
1708
  common: {
@@ -1419,6 +1739,18 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1419
1739
  native: {},
1420
1740
  });
1421
1741
 
1742
+ await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastWeekStart`, {
1743
+ type: 'state',
1744
+ common: {
1745
+ name: 'Wochenzähler zurückgesetzt am',
1746
+ type: 'number',
1747
+ role: STATE_ROLES.timestamp,
1748
+ read: true,
1749
+ write: false,
1750
+ },
1751
+ native: {},
1752
+ });
1753
+
1422
1754
  adapter.log.debug(`Meter state structure created for ${type}.${meterName}`);
1423
1755
  }
1424
1756
 
@@ -1523,6 +1855,61 @@ async function createTotalsStructure(adapter, type) {
1523
1855
  native: {},
1524
1856
  });
1525
1857
 
1858
+ if (type === 'gas') {
1859
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.weeklyVolume`, {
1860
+ type: 'state',
1861
+ common: {
1862
+ name: 'Wochenverbrauch Gesamt (m³)',
1863
+ type: 'number',
1864
+ role: STATE_ROLES.consumption,
1865
+ read: true,
1866
+ write: false,
1867
+ unit: 'm³',
1868
+ def: 0,
1869
+ },
1870
+ native: {},
1871
+ });
1872
+
1873
+ // Also add HT/NT volume totals if gas has HT/NT enabled
1874
+ if (adapter.config.gasHtNtEnabled) {
1875
+ const hntVolumeStates = ['weeklyVolumeHT', 'weeklyVolumeNT'];
1876
+ const hntVolumeLabels = {
1877
+ weeklyVolumeHT: 'Wochenverbrauch Gesamt Haupttarif (HT) (m³)',
1878
+ weeklyVolumeNT: 'Wochenverbrauch Gesamt Nebentarif (NT) (m³)',
1879
+ };
1880
+
1881
+ for (const state of hntVolumeStates) {
1882
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.${state}`, {
1883
+ type: 'state',
1884
+ common: {
1885
+ name: hntVolumeLabels[state],
1886
+ type: 'number',
1887
+ role: STATE_ROLES.consumption,
1888
+ read: true,
1889
+ write: false,
1890
+ unit: 'm³',
1891
+ def: 0,
1892
+ },
1893
+ native: {},
1894
+ });
1895
+ }
1896
+ }
1897
+ }
1898
+
1899
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption.weekly`, {
1900
+ type: 'state',
1901
+ common: {
1902
+ name: `Wochenverbrauch Gesamt (${label.unit})`,
1903
+ type: 'number',
1904
+ role: STATE_ROLES.consumption,
1905
+ read: true,
1906
+ write: false,
1907
+ unit: label.unit,
1908
+ def: 0,
1909
+ },
1910
+ native: {},
1911
+ });
1912
+
1526
1913
  // COST STATES (totals)
1527
1914
  await adapter.setObjectNotExistsAsync(`${basePath}.costs`, {
1528
1915
  type: 'channel',
@@ -1560,6 +1947,20 @@ async function createTotalsStructure(adapter, type) {
1560
1947
  native: {},
1561
1948
  });
1562
1949
 
1950
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs.weekly`, {
1951
+ type: 'state',
1952
+ common: {
1953
+ name: 'Wochenkosten Gesamt (€)',
1954
+ type: 'number',
1955
+ role: STATE_ROLES.cost,
1956
+ read: true,
1957
+ write: false,
1958
+ unit: '€',
1959
+ def: 0,
1960
+ },
1961
+ native: {},
1962
+ });
1963
+
1563
1964
  await adapter.setObjectNotExistsAsync(`${basePath}.costs.totalYearly`, {
1564
1965
  type: 'state',
1565
1966
  common: {
@@ -1577,11 +1978,82 @@ async function createTotalsStructure(adapter, type) {
1577
1978
  adapter.log.debug(`Totals state structure created for ${type}`);
1578
1979
  }
1579
1980
 
1981
+ /**
1982
+ * Creates history structure for a specific year
1983
+ *
1984
+ * @param {object} adapter - The adapter instance
1985
+ * @param {string} type - 'gas', 'water', 'electricity', 'pv'
1986
+ * @param {string} meterName - Meter name
1987
+ * @param {number|string} year - Year (YYYY)
1988
+ * @returns {Promise<void>}
1989
+ */
1990
+ async function createHistoryStructure(adapter, type, meterName, year) {
1991
+ const basePath = `${type}.${meterName}.history.${year}`;
1992
+
1993
+ await adapter.setObjectNotExistsAsync(`${type}.${meterName}.history`, {
1994
+ type: 'channel',
1995
+ common: { name: 'Historie' },
1996
+ native: {},
1997
+ });
1998
+
1999
+ await adapter.setObjectNotExistsAsync(basePath, {
2000
+ type: 'channel',
2001
+ common: { name: `Jahr ${year}` },
2002
+ native: { year },
2003
+ });
2004
+
2005
+ const unit = type === 'water' ? 'm³' : 'kWh';
2006
+
2007
+ await adapter.setObjectNotExistsAsync(`${basePath}.consumption`, {
2008
+ type: 'state',
2009
+ common: {
2010
+ name: `Jahresverbrauch ${year} (${unit})`,
2011
+ type: 'number',
2012
+ role: STATE_ROLES.consumption,
2013
+ read: true,
2014
+ write: false,
2015
+ unit: unit,
2016
+ def: 0,
2017
+ },
2018
+ native: {},
2019
+ });
2020
+
2021
+ if (type === 'gas') {
2022
+ await adapter.setObjectNotExistsAsync(`${basePath}.volume`, {
2023
+ type: 'state',
2024
+ common: {
2025
+ name: `Jahresverbrauch ${year} (m³)`,
2026
+ type: 'number',
2027
+ role: STATE_ROLES.consumption,
2028
+ read: true,
2029
+ write: false,
2030
+ unit: 'm³',
2031
+ def: 0,
2032
+ },
2033
+ native: {},
2034
+ });
2035
+ }
2036
+
2037
+ await adapter.setObjectNotExistsAsync(`${basePath}.costs`, {
2038
+ type: 'state',
2039
+ common: {
2040
+ name: `Jahreskosten ${year} (€)`,
2041
+ type: 'number',
2042
+ role: STATE_ROLES.cost,
2043
+ read: true,
2044
+ write: false,
2045
+ unit: '€',
2046
+ def: 0,
2047
+ },
2048
+ native: {},
2049
+ });
2050
+ }
2051
+
1580
2052
  module.exports = {
1581
2053
  createUtilityStateStructure,
1582
2054
  createMeterStructure,
1583
2055
  createTotalsStructure,
2056
+ createHistoryStructure,
1584
2057
  deleteUtilityStateStructure,
1585
- safeSetObjectNotExists,
1586
2058
  STATE_ROLES,
1587
2059
  };