iobroker.utility-monitor 1.4.6 → 1.5.1
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 +110 -62
- package/admin/custom/.vite/manifest.json +90 -0
- package/admin/custom/@mf-types/Components.d.ts +2 -0
- package/admin/custom/@mf-types/compiled-types/Components/CSVImporter.d.ts +11 -0
- package/admin/custom/@mf-types/compiled-types/Components.d.ts +2 -0
- package/admin/custom/@mf-types.d.ts +3 -0
- package/admin/custom/@mf-types.zip +0 -0
- package/admin/custom/CSVImporter_v15_11.js +4415 -0
- package/admin/custom/assets/Components-i0AZ59nl.js +18887 -0
- package/admin/custom/assets/UtilityMonitor__loadShare__react__loadShare__-Da99Mak4.js +42 -0
- package/admin/custom/assets/UtilityMonitor__mf_v__runtimeInit__mf_v__-BmC4OGk6.js +16 -0
- package/admin/custom/assets/_commonjsHelpers-Dj2_voLF.js +30 -0
- package/admin/custom/assets/hostInit-DEXfeB0W.js +10 -0
- package/admin/custom/assets/index-B3WVNJTz.js +401 -0
- package/admin/custom/assets/index-VBwl8x_k.js +64 -0
- package/admin/custom/assets/preload-helper-BelkbqnE.js +61 -0
- package/admin/custom/assets/virtualExposes-CqCLUNLT.js +19 -0
- package/admin/custom/index.html +12 -0
- package/admin/custom/mf-manifest.json +1 -0
- package/admin/jsonConfig.json +90 -31
- package/io-package.json +15 -31
- package/lib/billingManager.js +382 -137
- package/lib/calculator.js +41 -146
- package/lib/consumptionManager.js +9 -252
- package/lib/importManager.js +300 -0
- package/lib/messagingHandler.js +4 -2
- package/lib/meter/MeterRegistry.js +110 -0
- package/lib/multiMeterManager.js +580 -173
- package/lib/stateManager.js +502 -31
- package/lib/utils/billingHelper.js +69 -0
- package/lib/utils/consumptionHelper.js +47 -0
- package/lib/utils/helpers.js +234 -0
- package/lib/utils/stateCache.js +147 -0
- package/lib/utils/typeMapper.js +19 -0
- package/main.js +67 -8
- package/package.json +10 -4
package/lib/calculator.js
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
|
+
const helpers = require('./utils/helpers');
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* Calculator module for
|
|
3
|
-
*
|
|
4
|
+
* @file Calculator module for utility consumption and cost calculations.
|
|
5
|
+
* Contains functions for gas conversion, cost calculation, and tariff handling.
|
|
6
|
+
* @module calculator
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
|
-
* Converts gas volume from m³ to kWh
|
|
8
|
-
*
|
|
10
|
+
* Converts gas volume from cubic meters (m³) to kilowatt-hours (kWh).
|
|
11
|
+
* The conversion uses the standard German gas billing formula:
|
|
12
|
+
* kWh = m³ × Brennwert (calorific value) × Z-Zahl (state number)
|
|
13
|
+
* Brennwert: Energy content per cubic meter, typically 9.5-11.5 kWh/m³.
|
|
14
|
+
* Z-Zahl: Correction factor for temperature and pressure differences.
|
|
9
15
|
*
|
|
10
|
-
* @param {number} m3 -
|
|
11
|
-
* @param {number} brennwert - Calorific value
|
|
12
|
-
* @param {number} zZahl -
|
|
13
|
-
* @returns {number} Energy in kWh
|
|
16
|
+
* @param {number} m3 - Gas volume in cubic meters (must be >= 0)
|
|
17
|
+
* @param {number} brennwert - Calorific value in kWh/m³ (must be > 0)
|
|
18
|
+
* @param {number} zZahl - State number (must be > 0 and <= 1)
|
|
19
|
+
* @returns {number} Energy consumption in kWh
|
|
20
|
+
* @throws {RangeError} If parameters are outside valid ranges
|
|
14
21
|
*/
|
|
15
22
|
function convertGasM3ToKWh(m3, brennwert = 11.5, zZahl = 0.95) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
const cleanM3 = helpers.ensureNumber(m3);
|
|
24
|
+
const cleanBrennwert = helpers.ensureNumber(brennwert);
|
|
25
|
+
const cleanZZahl = helpers.ensureNumber(zZahl);
|
|
26
|
+
|
|
27
|
+
// Validate parameters
|
|
28
|
+
if (cleanM3 < 0 || cleanBrennwert <= 0 || cleanZZahl <= 0 || cleanZZahl > 1) {
|
|
29
|
+
throw new RangeError(
|
|
30
|
+
'Invalid parameters for gas conversion: m3 must be >= 0, brennwert must be > 0, zZahl must be > 0 and <= 1',
|
|
31
|
+
);
|
|
21
32
|
}
|
|
22
|
-
|
|
33
|
+
|
|
34
|
+
return cleanM3 * cleanBrennwert * cleanZZahl;
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
/**
|
|
@@ -52,74 +64,14 @@ function calculateCost(consumption, price) {
|
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*/
|
|
60
|
-
function ensureNumber(value) {
|
|
61
|
-
if (value === undefined || value === null || value === '') {
|
|
62
|
-
return 0;
|
|
63
|
-
}
|
|
64
|
-
if (typeof value === 'string') {
|
|
65
|
-
const normalized = value.replace(',', '.');
|
|
66
|
-
const parsed = parseFloat(normalized);
|
|
67
|
-
return isNaN(parsed) ? 0 : parsed;
|
|
68
|
-
}
|
|
69
|
-
const num = Number(value);
|
|
70
|
-
return isNaN(num) ? 0 : num;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Rounds a number to specified decimal places
|
|
67
|
+
* Checks if the current time falls within the High Tariff (HT) period.
|
|
68
|
+
* German electricity providers often offer dual-tariff rates:
|
|
69
|
+
* HT (Haupttarif): Higher rate during peak hours (typically 6:00-22:00)
|
|
70
|
+
* NT (Nebentarif): Lower rate during off-peak hours (typically 22:00-6:00)
|
|
75
71
|
*
|
|
76
|
-
* @param {
|
|
77
|
-
* @param {
|
|
78
|
-
* @returns {
|
|
79
|
-
*/
|
|
80
|
-
function roundToDecimals(value, decimals = 2) {
|
|
81
|
-
const numValue = ensureNumber(value);
|
|
82
|
-
const factor = Math.pow(10, decimals);
|
|
83
|
-
return Math.round(numValue * factor) / factor;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Parses a German date string (DD.MM.YYYY) into a Date object
|
|
88
|
-
*
|
|
89
|
-
* @param {string} dateStr - Date string in format DD.MM.YYYY
|
|
90
|
-
* @returns {Date|null} Date object or null if invalid
|
|
91
|
-
*/
|
|
92
|
-
function parseGermanDate(dateStr) {
|
|
93
|
-
if (!dateStr || typeof dateStr !== 'string') {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
const parts = dateStr.trim().split('.');
|
|
97
|
-
if (parts.length !== 3) {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
let day = parseInt(parts[0], 10);
|
|
101
|
-
let month = parseInt(parts[1], 10) - 1; // Month is 0-indexed
|
|
102
|
-
let year = parseInt(parts[2], 10);
|
|
103
|
-
|
|
104
|
-
// Handle 2-digit years (e.g. 25 -> 2025)
|
|
105
|
-
if (year < 100) {
|
|
106
|
-
year += 2000;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (isNaN(day) || isNaN(month) || isNaN(year)) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Create date at noon to avoid timezone shift issues (especially with ISO export)
|
|
114
|
-
return new Date(year, month, day, 12, 0, 0);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Checks if the current time is within the High Tariff (HT) period
|
|
119
|
-
*
|
|
120
|
-
* @param {object} config - Adapter configuration
|
|
121
|
-
* @param {string} type - Utility type: 'gas' or 'strom'
|
|
122
|
-
* @returns {boolean} True if current time is HT, false if NT
|
|
72
|
+
* @param {object} config - Adapter configuration object with HT/NT settings
|
|
73
|
+
* @param {string} type - Utility type identifier: 'gas', 'strom', 'wasser', or 'pv'
|
|
74
|
+
* @returns {boolean} True if current time is within HT period, false for NT
|
|
123
75
|
*/
|
|
124
76
|
function isHTTime(config, type) {
|
|
125
77
|
if (!config || !type) {
|
|
@@ -180,75 +132,18 @@ const DEFAULTS = {
|
|
|
180
132
|
MIN_CONSUMPTION: 0,
|
|
181
133
|
};
|
|
182
134
|
|
|
183
|
-
/**
|
|
184
|
-
* Formats a Date object to YYYY-MM-DD HH:mm:ss string
|
|
185
|
-
*
|
|
186
|
-
* @param {Date} date - Date object
|
|
187
|
-
* @returns {string|null} Formatted date string or null
|
|
188
|
-
*/
|
|
189
|
-
function formatDateString(date) {
|
|
190
|
-
if (!date) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
if (!(date instanceof Date)) {
|
|
194
|
-
// If it's already a string, try to parse and re-format, or just return if it handles
|
|
195
|
-
// But the error said "calculator.formatDateString is not a function", so we need it here.
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const pad = num => num.toString().padStart(2, '0');
|
|
200
|
-
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Parses a date string (ISO or standard format)
|
|
205
|
-
*
|
|
206
|
-
* @param {string} dateStr - Date string
|
|
207
|
-
* @returns {Date|null} Date object or null
|
|
208
|
-
*/
|
|
209
|
-
function parseDateString(dateStr) {
|
|
210
|
-
if (!dateStr) {
|
|
211
|
-
return null;
|
|
212
|
-
}
|
|
213
|
-
const date = new Date(dateStr);
|
|
214
|
-
if (isNaN(date.getTime())) {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
return date;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Checks if a year is a leap year
|
|
222
|
-
*
|
|
223
|
-
* @param {number} year - Year to check
|
|
224
|
-
* @returns {boolean} True if leap year
|
|
225
|
-
*/
|
|
226
|
-
function isLeapYear(year) {
|
|
227
|
-
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Calculates the difference in months between two dates
|
|
232
|
-
*
|
|
233
|
-
* @param {Date} startDate - Start date
|
|
234
|
-
* @param {Date} endDate - End date
|
|
235
|
-
* @returns {number} Difference in months
|
|
236
|
-
*/
|
|
237
|
-
function getMonthsDifference(startDate, endDate) {
|
|
238
|
-
return (endDate.getFullYear() - startDate.getFullYear()) * 12 + (endDate.getMonth() - startDate.getMonth());
|
|
239
|
-
}
|
|
240
|
-
|
|
241
135
|
module.exports = {
|
|
242
136
|
convertGasM3ToKWh,
|
|
243
137
|
getCurrentPrice,
|
|
244
138
|
calculateCost,
|
|
245
|
-
ensureNumber,
|
|
246
|
-
roundToDecimals,
|
|
247
|
-
parseGermanDate,
|
|
248
139
|
isHTTime,
|
|
249
|
-
formatDateString,
|
|
250
|
-
parseDateString,
|
|
251
|
-
isLeapYear,
|
|
252
|
-
getMonthsDifference,
|
|
253
140
|
DEFAULTS,
|
|
141
|
+
// Re-export helpers for backward compatibility
|
|
142
|
+
ensureNumber: helpers.ensureNumber,
|
|
143
|
+
roundToDecimals: helpers.roundToDecimals,
|
|
144
|
+
parseGermanDate: helpers.parseGermanDate,
|
|
145
|
+
formatDateString: helpers.formatDateString,
|
|
146
|
+
parseDateString: s => helpers.parseGermanDate(s),
|
|
147
|
+
isLeapYear: helpers.isLeapYear,
|
|
148
|
+
getMonthsDifference: helpers.getMonthsDifference,
|
|
254
149
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const calculator = require('./calculator');
|
|
4
|
+
const { getConfigType } = require('./utils/typeMapper');
|
|
4
5
|
const stateManager = require('./stateManager');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* ConsumptionManager handles
|
|
8
|
-
*
|
|
8
|
+
* ConsumptionManager handles utility initialization and price updates.
|
|
9
|
+
* NOTE: Sensor handling has been moved to MultiMeterManager since v1.4.6.
|
|
9
10
|
*/
|
|
10
11
|
class ConsumptionManager {
|
|
11
12
|
/**
|
|
@@ -13,22 +14,6 @@ class ConsumptionManager {
|
|
|
13
14
|
*/
|
|
14
15
|
constructor(adapter) {
|
|
15
16
|
this.adapter = adapter;
|
|
16
|
-
this.lastSensorValues = {};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Maps internal utility type to config/state name
|
|
21
|
-
*
|
|
22
|
-
* @param {string} type - gas, water, or electricity
|
|
23
|
-
* @returns {string} - gas, wasser, or strom
|
|
24
|
-
*/
|
|
25
|
-
getConfigType(type) {
|
|
26
|
-
const mapping = {
|
|
27
|
-
electricity: 'strom',
|
|
28
|
-
water: 'wasser',
|
|
29
|
-
gas: 'gas',
|
|
30
|
-
};
|
|
31
|
-
return mapping[type] || type;
|
|
32
17
|
}
|
|
33
18
|
|
|
34
19
|
/**
|
|
@@ -50,7 +35,7 @@ class ConsumptionManager {
|
|
|
50
35
|
// State structure is now created by MultiMeterManager per meter (v1.4.6)
|
|
51
36
|
// Old createUtilityStateStructure removed - states are created under type.meterName.*
|
|
52
37
|
|
|
53
|
-
const configType =
|
|
38
|
+
const configType = getConfigType(type);
|
|
54
39
|
const sensorDPKey = `${configType}SensorDP`;
|
|
55
40
|
const sensorDP = this.adapter.config[sensorDPKey];
|
|
56
41
|
|
|
@@ -79,244 +64,16 @@ class ConsumptionManager {
|
|
|
79
64
|
}
|
|
80
65
|
|
|
81
66
|
// Note: All initialization moved to MultiMeterManager in v1.4.6:
|
|
67
|
+
// - State creation (per meter)
|
|
82
68
|
// - Sensor value restoration (per meter)
|
|
83
69
|
// - Period start timestamps (per meter)
|
|
70
|
+
// - Initial yearly consumption calculation (per meter)
|
|
84
71
|
// - Current price updates (per meter)
|
|
85
72
|
// - Cost calculations (per meter)
|
|
73
|
+
// - Billing countdown (per meter)
|
|
86
74
|
// Old type-level states (gas.info.*, gas.statistics.*) are no longer used
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
// NOTE: This is now handled per meter by MultiMeterManager in v1.4.6
|
|
90
|
-
// This legacy code path should not execute for new setups, but is kept for safety
|
|
91
|
-
const initialReadingKey = `${configType}InitialReading`;
|
|
92
|
-
const initialReading = this.adapter.config[initialReadingKey] || 0;
|
|
93
|
-
|
|
94
|
-
if (initialReading > 0 && sensorDP) {
|
|
95
|
-
// Get the main meter name to use the correct path
|
|
96
|
-
const mainMeterNameKey = `${configType}MainMeterName`;
|
|
97
|
-
const mainMeterName = this.adapter.config[mainMeterNameKey] || 'main';
|
|
98
|
-
const basePath = `${type}.${mainMeterName}`;
|
|
99
|
-
|
|
100
|
-
const sensorState = await this.adapter.getForeignStateAsync(sensorDP);
|
|
101
|
-
if (sensorState && typeof sensorState.val === 'number') {
|
|
102
|
-
let currentRaw = sensorState.val;
|
|
103
|
-
|
|
104
|
-
// Apply offset if configured (in original unit)
|
|
105
|
-
const offsetKey = `${configType}Offset`;
|
|
106
|
-
const offset = this.adapter.config[offsetKey] || 0;
|
|
107
|
-
if (offset !== 0) {
|
|
108
|
-
currentRaw = currentRaw - offset;
|
|
109
|
-
this.adapter.log.debug(`Applied offset for ${type}: -${offset}, new value: ${currentRaw}`);
|
|
110
|
-
}
|
|
111
|
-
let yearlyConsumption = Math.max(0, currentRaw - initialReading);
|
|
112
|
-
|
|
113
|
-
// For gas: convert m³ to kWh AFTER calculating the difference
|
|
114
|
-
if (type === 'gas') {
|
|
115
|
-
const brennwert = this.adapter.config.gasBrennwert || 11.5;
|
|
116
|
-
const zZahl = this.adapter.config.gasZahl || 0.95;
|
|
117
|
-
const yearlyVolume = yearlyConsumption;
|
|
118
|
-
yearlyConsumption = calculator.convertGasM3ToKWh(yearlyConsumption, brennwert, zZahl);
|
|
119
|
-
await this.adapter.setStateAsync(`${basePath}.consumption.yearlyVolume`, yearlyVolume, true);
|
|
120
|
-
this.adapter.log.info(
|
|
121
|
-
`Init yearly ${type}: ${yearlyConsumption.toFixed(2)} kWh = ${(currentRaw - initialReading).toFixed(2)} m³ (current: ${currentRaw.toFixed(2)} m³, initial: ${initialReading} m³)`,
|
|
122
|
-
);
|
|
123
|
-
} else {
|
|
124
|
-
this.adapter.log.info(
|
|
125
|
-
`Init yearly ${type}: ${yearlyConsumption.toFixed(2)} (current: ${currentRaw.toFixed(2)}, initial: ${initialReading})`,
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
await this.adapter.setStateAsync(`${basePath}.consumption.yearly`, yearlyConsumption, true);
|
|
130
|
-
if (typeof this.adapter.updateCosts === 'function') {
|
|
131
|
-
await this.adapter.updateCosts(type);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Note: Billing countdown is now handled per meter by billingManager.updateBillingCountdown()
|
|
137
|
-
// which is called during checkPeriodResets()
|
|
138
|
-
|
|
139
|
-
this.adapter.log.debug(`Initial cost calculation completed for ${type}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Handles sensor value updates
|
|
144
|
-
*
|
|
145
|
-
* NOTE: This method is DEPRECATED since v1.4.6!
|
|
146
|
-
* All sensor updates are now handled by MultiMeterManager.handleSensorUpdate()
|
|
147
|
-
* This method remains only as fallback but should NEVER be called in normal operation.
|
|
148
|
-
*
|
|
149
|
-
* @param {string} type - Utility type
|
|
150
|
-
* @param {string} sensorDP - Sensor datapoint ID
|
|
151
|
-
* @param {number} value - New sensor value
|
|
152
|
-
*/
|
|
153
|
-
async handleSensorUpdate(type, sensorDP, value) {
|
|
154
|
-
this.adapter.log.warn(
|
|
155
|
-
`consumptionManager.handleSensorUpdate() called - this is deprecated! All sensors should be handled by MultiMeterManager.`,
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
if (typeof value !== 'number' || value < 0) {
|
|
159
|
-
this.adapter.log.warn(`Invalid sensor value for ${type}: ${value}`);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
this.adapter.log.debug(`[DEPRECATED] Sensor update for ${type}: ${value}`);
|
|
164
|
-
|
|
165
|
-
const now = Date.now();
|
|
166
|
-
let consumption = value;
|
|
167
|
-
let consumptionM3 = null;
|
|
168
|
-
|
|
169
|
-
const configType = this.getConfigType(type);
|
|
170
|
-
|
|
171
|
-
// Apply offset FIRST
|
|
172
|
-
const offsetKey = `${configType}Offset`;
|
|
173
|
-
const offset = this.adapter.config[offsetKey] || 0;
|
|
174
|
-
if (offset !== 0) {
|
|
175
|
-
consumption = consumption - offset;
|
|
176
|
-
this.adapter.log.debug(`Applied offset for ${type}: -${offset}, new value: ${consumption}`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// For gas, convert m³ to kWh
|
|
180
|
-
if (type === 'gas') {
|
|
181
|
-
const brennwert = this.adapter.config.gasBrennwert || 11.5;
|
|
182
|
-
const zZahl = this.adapter.config.gasZahl || 0.95;
|
|
183
|
-
consumptionM3 = consumption;
|
|
184
|
-
await this.adapter.setStateAsync(`${type}.info.meterReadingVolume`, consumption, true);
|
|
185
|
-
consumption = calculator.convertGasM3ToKWh(consumption, brennwert, zZahl);
|
|
186
|
-
consumption = calculator.roundToDecimals(consumption, 2);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Update meter reading
|
|
190
|
-
await this.adapter.setStateAsync(`${type}.info.meterReading`, consumption, true);
|
|
191
|
-
|
|
192
|
-
// Calculate deltas
|
|
193
|
-
const lastValue = this.lastSensorValues[sensorDP];
|
|
194
|
-
this.lastSensorValues[sensorDP] = consumption;
|
|
195
|
-
|
|
196
|
-
if (lastValue === undefined || consumption < lastValue) {
|
|
197
|
-
if (lastValue !== undefined && consumption < lastValue) {
|
|
198
|
-
this.adapter.log.warn(
|
|
199
|
-
`${type}: Sensor value decreased (${lastValue} -> ${consumption}). Assuming meter reset or replacement.`,
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
if (typeof this.adapter.updateCosts === 'function') {
|
|
203
|
-
await this.adapter.updateCosts(type);
|
|
204
|
-
}
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const delta = consumption - lastValue;
|
|
209
|
-
this.adapter.log.debug(`${type} delta: ${delta}`);
|
|
210
|
-
|
|
211
|
-
// Track volume for gas
|
|
212
|
-
if (type === 'gas') {
|
|
213
|
-
const brennwert = this.adapter.config.gasBrennwert || 11.5;
|
|
214
|
-
const zZahl = this.adapter.config.gasZahl || 0.95;
|
|
215
|
-
const deltaVolume = delta / (brennwert * zZahl);
|
|
216
|
-
|
|
217
|
-
const dailyVolume = await this.adapter.getStateAsync(`${type}.consumption.dailyVolume`);
|
|
218
|
-
const monthlyVolume = await this.adapter.getStateAsync(`${type}.consumption.monthlyVolume`);
|
|
219
|
-
const yearlyVolume = await this.adapter.getStateAsync(`${type}.consumption.yearlyVolume`);
|
|
220
|
-
|
|
221
|
-
await this.adapter.setStateAsync(
|
|
222
|
-
`${type}.consumption.dailyVolume`,
|
|
223
|
-
calculator.roundToDecimals((dailyVolume?.val || 0) + deltaVolume, 2),
|
|
224
|
-
true,
|
|
225
|
-
);
|
|
226
|
-
await this.adapter.setStateAsync(
|
|
227
|
-
`${type}.consumption.monthlyVolume`,
|
|
228
|
-
calculator.roundToDecimals((monthlyVolume?.val || 0) + deltaVolume, 2),
|
|
229
|
-
true,
|
|
230
|
-
);
|
|
231
|
-
await this.adapter.setStateAsync(
|
|
232
|
-
`${type}.consumption.yearlyVolume`,
|
|
233
|
-
calculator.roundToDecimals((yearlyVolume?.val || 0) + deltaVolume, 3),
|
|
234
|
-
true,
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Update consumption values
|
|
239
|
-
const dailyState = await this.adapter.getStateAsync(`${type}.consumption.daily`);
|
|
240
|
-
await this.adapter.setStateAsync(
|
|
241
|
-
`${type}.consumption.daily`,
|
|
242
|
-
calculator.roundToDecimals((dailyState?.val || 0) + delta, 2),
|
|
243
|
-
true,
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
const monthlyState = await this.adapter.getStateAsync(`${type}.consumption.monthly`);
|
|
247
|
-
await this.adapter.setStateAsync(
|
|
248
|
-
`${type}.consumption.monthly`,
|
|
249
|
-
calculator.roundToDecimals((monthlyState?.val || 0) + delta, 2),
|
|
250
|
-
true,
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
// HT/NT tracking
|
|
254
|
-
const htNtEnabledKey = `${configType}HtNtEnabled`;
|
|
255
|
-
if (this.adapter.config[htNtEnabledKey]) {
|
|
256
|
-
const isHT = calculator.isHTTime(this.adapter.config, configType);
|
|
257
|
-
const suffix = isHT ? 'HT' : 'NT';
|
|
258
|
-
|
|
259
|
-
const dHTNT = await this.adapter.getStateAsync(`${type}.consumption.daily${suffix}`);
|
|
260
|
-
await this.adapter.setStateAsync(
|
|
261
|
-
`${type}.consumption.daily${suffix}`,
|
|
262
|
-
calculator.roundToDecimals((dHTNT?.val || 0) + delta, 2),
|
|
263
|
-
true,
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
const mHTNT = await this.adapter.getStateAsync(`${type}.consumption.monthly${suffix}`);
|
|
267
|
-
await this.adapter.setStateAsync(
|
|
268
|
-
`${type}.consumption.monthly${suffix}`,
|
|
269
|
-
calculator.roundToDecimals((mHTNT?.val || 0) + delta, 2),
|
|
270
|
-
true,
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
const yHTNT = await this.adapter.getStateAsync(`${type}.consumption.yearly${suffix}`);
|
|
274
|
-
await this.adapter.setStateAsync(
|
|
275
|
-
`${type}.consumption.yearly${suffix}`,
|
|
276
|
-
calculator.roundToDecimals((yHTNT?.val || 0) + delta, 2),
|
|
277
|
-
true,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Yearly consumption
|
|
282
|
-
const initialReadingKey = `${configType}InitialReading`;
|
|
283
|
-
const initialReading = this.adapter.config[initialReadingKey] || 0;
|
|
284
|
-
|
|
285
|
-
if (initialReading > 0) {
|
|
286
|
-
let yearlyAmount;
|
|
287
|
-
if (type === 'gas') {
|
|
288
|
-
const yearlyM3 = Math.max(0, (consumptionM3 || 0) - initialReading);
|
|
289
|
-
await this.adapter.setStateAsync(
|
|
290
|
-
`${type}.consumption.yearlyVolume`,
|
|
291
|
-
calculator.roundToDecimals(yearlyM3, 2),
|
|
292
|
-
true,
|
|
293
|
-
);
|
|
294
|
-
const brennwert = this.adapter.config.gasBrennwert || 11.5;
|
|
295
|
-
const zZahl = this.adapter.config.gasZahl || 0.95;
|
|
296
|
-
yearlyAmount = calculator.convertGasM3ToKWh(yearlyM3, brennwert, zZahl);
|
|
297
|
-
} else {
|
|
298
|
-
yearlyAmount = Math.max(0, consumption - initialReading);
|
|
299
|
-
}
|
|
300
|
-
await this.adapter.setStateAsync(
|
|
301
|
-
`${type}.consumption.yearly`,
|
|
302
|
-
calculator.roundToDecimals(yearlyAmount, 2),
|
|
303
|
-
true,
|
|
304
|
-
);
|
|
305
|
-
} else {
|
|
306
|
-
const yState = await this.adapter.getStateAsync(`${type}.consumption.yearly`);
|
|
307
|
-
await this.adapter.setStateAsync(
|
|
308
|
-
`${type}.consumption.yearly`,
|
|
309
|
-
calculator.roundToDecimals((yState?.val || 0) + delta, 2),
|
|
310
|
-
true,
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (typeof this.adapter.updateCosts === 'function') {
|
|
315
|
-
await this.adapter.updateCosts(type);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
await this.adapter.setStateAsync(`${type}.consumption.lastUpdate`, now, true);
|
|
319
|
-
await this.adapter.setStateAsync(`${type}.info.lastSync`, now, true);
|
|
76
|
+
this.adapter.log.debug(`${type} initialization delegated to MultiMeterManager`);
|
|
320
77
|
}
|
|
321
78
|
|
|
322
79
|
/**
|
|
@@ -327,7 +84,7 @@ class ConsumptionManager {
|
|
|
327
84
|
* @param {string} type - Utility type
|
|
328
85
|
*/
|
|
329
86
|
async updateCurrentPrice(type) {
|
|
330
|
-
const configType =
|
|
87
|
+
const configType = getConfigType(type);
|
|
331
88
|
|
|
332
89
|
// Get all meters for this type
|
|
333
90
|
const meters = this.adapter.multiMeterManager?.getMetersForType(type) || [];
|