iobroker.utility-monitor 1.4.6 → 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 +98 -55
  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 +90 -31
  21. package/io-package.json +39 -3
  22. package/lib/billingManager.js +235 -123
  23. package/lib/calculator.js +19 -138
  24. package/lib/consumptionManager.js +9 -252
  25. package/lib/importManager.js +300 -0
  26. package/lib/messagingHandler.js +4 -2
  27. package/lib/meter/MeterRegistry.js +110 -0
  28. package/lib/multiMeterManager.js +397 -174
  29. package/lib/stateManager.js +502 -31
  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 +71 -8
  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: {
@@ -896,6 +1027,20 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
896
1027
  },
897
1028
  native: {},
898
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
+ });
899
1044
  }
900
1045
 
901
1046
  await adapter.setObjectNotExistsAsync(`${basePath}.consumption.daily`, {
@@ -940,6 +1085,65 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
940
1085
  native: {},
941
1086
  });
942
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
+
943
1147
  await adapter.setObjectNotExistsAsync(`${basePath}.consumption.lastUpdate`, {
944
1148
  type: 'state',
945
1149
  common: {
@@ -1003,6 +1207,59 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1003
1207
  native: {},
1004
1208
  });
1005
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
+
1006
1263
  await adapter.setObjectNotExistsAsync(`${basePath}.costs.totalYearly`, {
1007
1264
  type: 'state',
1008
1265
  common: {
@@ -1279,27 +1536,27 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1279
1536
  native: {},
1280
1537
  });
1281
1538
 
1282
- await adapter.setObjectNotExistsAsync(`${basePath}.info.lastSync`, {
1539
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.sensorActive`, {
1283
1540
  type: 'state',
1284
1541
  common: {
1285
- name: 'Letzte Synchronisation',
1286
- type: 'number',
1287
- role: STATE_ROLES.timestamp,
1542
+ name: 'Sensor aktiv',
1543
+ type: 'boolean',
1544
+ role: 'indicator.reachable',
1288
1545
  read: true,
1289
1546
  write: false,
1547
+ def: false,
1290
1548
  },
1291
1549
  native: {},
1292
1550
  });
1293
1551
 
1294
- await adapter.setObjectNotExistsAsync(`${basePath}.info.sensorActive`, {
1552
+ await adapter.setObjectNotExistsAsync(`${basePath}.info.lastSync`, {
1295
1553
  type: 'state',
1296
1554
  common: {
1297
- name: 'Sensor aktiv',
1298
- type: 'boolean',
1299
- role: STATE_ROLES.indicator,
1555
+ name: 'Letzte Synchronisation',
1556
+ type: 'number',
1557
+ role: STATE_ROLES.timestamp,
1300
1558
  read: true,
1301
1559
  write: false,
1302
- def: false,
1303
1560
  },
1304
1561
  native: {},
1305
1562
  });
@@ -1384,6 +1641,68 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1384
1641
  });
1385
1642
  }
1386
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
+
1387
1706
  await adapter.setObjectNotExistsAsync(`${basePath}.statistics.lastDayStart`, {
1388
1707
  type: 'state',
1389
1708
  common: {
@@ -1420,6 +1739,18 @@ async function createMeterStructure(adapter, type, meterName, _config = {}) {
1420
1739
  native: {},
1421
1740
  });
1422
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
+
1423
1754
  adapter.log.debug(`Meter state structure created for ${type}.${meterName}`);
1424
1755
  }
1425
1756
 
@@ -1524,6 +1855,61 @@ async function createTotalsStructure(adapter, type) {
1524
1855
  native: {},
1525
1856
  });
1526
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
+
1527
1913
  // COST STATES (totals)
1528
1914
  await adapter.setObjectNotExistsAsync(`${basePath}.costs`, {
1529
1915
  type: 'channel',
@@ -1561,6 +1947,20 @@ async function createTotalsStructure(adapter, type) {
1561
1947
  native: {},
1562
1948
  });
1563
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
+
1564
1964
  await adapter.setObjectNotExistsAsync(`${basePath}.costs.totalYearly`, {
1565
1965
  type: 'state',
1566
1966
  common: {
@@ -1578,11 +1978,82 @@ async function createTotalsStructure(adapter, type) {
1578
1978
  adapter.log.debug(`Totals state structure created for ${type}`);
1579
1979
  }
1580
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
+
1581
2052
  module.exports = {
1582
2053
  createUtilityStateStructure,
1583
2054
  createMeterStructure,
1584
2055
  createTotalsStructure,
2056
+ createHistoryStructure,
1585
2057
  deleteUtilityStateStructure,
1586
- safeSetObjectNotExists,
1587
2058
  STATE_ROLES,
1588
2059
  };