@etainabl/nodejs-sdk 1.1.26 → 1.1.27
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/dist/consumption.js +1 -1
- package/dist/consumption.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/monitoring.d.ts +1 -0
- package/dist/monitoring.js +16 -0
- package/dist/monitoring.js.map +1 -0
- package/dist/units.d.ts +3 -3
- package/package.json +1 -1
- package/src/consumption.ts +1 -1
- package/src/index.ts +2 -0
- package/src/monitoring.ts +17 -0
- package/lib/readingsInterpolate.d.ts +0 -1
- package/lib/readingsInterpolate.js +0 -73
- package/lib/readingsInterpolate.js.map +0 -1
- package/src/lib/readingsInterpolate.ts +0 -100
package/dist/consumption.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import moment from 'moment';
|
|
2
2
|
export const dayNightConsumption = (data) => data.reduce((acc, item) => {
|
|
3
|
-
const hour = moment(item.date).hour(); // End Time of HH consumption period
|
|
3
|
+
const hour = moment.utc(item.date).hour(); // End Time of HH consumption period
|
|
4
4
|
if (hour >= 0 && hour < 7) {
|
|
5
5
|
acc.nightConsumption += item.consumption;
|
|
6
6
|
}
|
package/dist/consumption.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumption.js","sourceRoot":"","sources":["../src/consumption.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAa5B,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAwB,EAAE,IAAqB,EAAuB,EAAE;IACnJ,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,oCAAoC;
|
|
1
|
+
{"version":3,"file":"consumption.js","sourceRoot":"","sources":["../src/consumption.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAa5B,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAwB,EAAE,IAAqB,EAAuB,EAAE;IACnJ,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,oCAAoC;IAE/E,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE;QACzB,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,WAAW,CAAC;KAC1C;SAAM;QACL,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,WAAW,CAAC;KACxC;IAED,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC;IAEpC,OAAO,GAAG,CAAC;AACb,CAAC,EAAE;IACD,cAAc,EAAE,CAAC;IACjB,gBAAgB,EAAE,CAAC;IACnB,WAAW,EAAE,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAqB,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;AAEvI,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAuB,EAAE,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE5F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,SAAiB,EAAE,SAAiC,EAAE,OAA+B,EAAE,EAAE;IACzI,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAE9E,OAAO,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;AAC/E,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ import db from './db.js';
|
|
|
4
4
|
import slack from './slack.js';
|
|
5
5
|
import * as units from './units.js';
|
|
6
6
|
import * as consumption from './consumption.js';
|
|
7
|
-
|
|
7
|
+
import * as monitoring from './monitoring.js';
|
|
8
|
+
export { api, logger, consumption, monitoring, db, slack, units };
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,6 @@ import db from './db.js';
|
|
|
4
4
|
import slack from './slack.js';
|
|
5
5
|
import * as units from './units.js';
|
|
6
6
|
import * as consumption from './consumption.js';
|
|
7
|
-
|
|
7
|
+
import * as monitoring from './monitoring.js';
|
|
8
|
+
export { api, logger, consumption, monitoring, db, slack, units };
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EACL,GAAG,EACH,MAAM,EACN,WAAW,EACX,UAAU,EACV,EAAE,EACF,KAAK,EACL,KAAK,EACN,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sendHeartbeat: () => Promise<boolean>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import logger from './logger.js';
|
|
3
|
+
const log = logger('monitoring');
|
|
4
|
+
export const sendHeartbeat = async () => {
|
|
5
|
+
if (!process.env.HEARTBEAT_URL)
|
|
6
|
+
return false;
|
|
7
|
+
try {
|
|
8
|
+
await axios.post(process.env.HEARTBEAT_URL);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
log.warn(`Failed to send heartbeat: ${e.message || e}`);
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=monitoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitoring.js","sourceRoot":"","sources":["../src/monitoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAEjC,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE;IACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI;QACA,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAE5C,OAAO,IAAI,CAAC;KACf;IAAC,OAAO,CAAM,EAAE;QACb,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC,CAAC;QACxD,OAAO,KAAK,CAAC;KAChB;AACL,CAAC,CAAA"}
|
package/dist/units.d.ts
CHANGED
|
@@ -5,9 +5,9 @@ interface Item {
|
|
|
5
5
|
value: number;
|
|
6
6
|
[key: string]: any;
|
|
7
7
|
}
|
|
8
|
-
export type AccountType = 'electricity' | 'gas' | 'water' | 'waste' | 'solar' | 'heating' | 'flow' | 'cooling' | 'temperature' | 'other';
|
|
9
|
-
export type ETNUnit = 'kwh' | 'kg' | 'm3' | 'lbs' | 'tonnes' | 'wh' | 'mwh' | 'ft3' | 'hcf' | 'm3/h' | 'qty' | 'l' | 'C';
|
|
10
|
-
export type BaseUnit = 'kwh' | 'm3' | 'C' | 'kg' | 'm3/h';
|
|
8
|
+
export declare type AccountType = 'electricity' | 'gas' | 'water' | 'waste' | 'solar' | 'heating' | 'flow' | 'cooling' | 'temperature' | 'other';
|
|
9
|
+
export declare type ETNUnit = 'kwh' | 'kg' | 'm3' | 'lbs' | 'tonnes' | 'wh' | 'mwh' | 'ft3' | 'hcf' | 'm3/h' | 'qty' | 'l' | 'C';
|
|
10
|
+
export declare type BaseUnit = 'kwh' | 'm3' | 'C' | 'kg' | 'm3/h';
|
|
11
11
|
export declare const accountTypeMap: {
|
|
12
12
|
[key: string]: BaseUnit;
|
|
13
13
|
};
|
package/package.json
CHANGED
package/src/consumption.ts
CHANGED
|
@@ -12,7 +12,7 @@ interface DayNightConsumption {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export const dayNightConsumption = (data: ConsumptionData[]) => data.reduce((acc: DayNightConsumption, item: ConsumptionData): DayNightConsumption => {
|
|
15
|
-
const hour = moment(item.date).hour(); // End Time of HH consumption period
|
|
15
|
+
const hour = moment.utc(item.date).hour(); // End Time of HH consumption period
|
|
16
16
|
|
|
17
17
|
if (hour >= 0 && hour < 7) {
|
|
18
18
|
acc.nightConsumption += item.consumption;
|
package/src/index.ts
CHANGED
|
@@ -4,11 +4,13 @@ import db from './db.js';
|
|
|
4
4
|
import slack from './slack.js';
|
|
5
5
|
import * as units from './units.js';
|
|
6
6
|
import * as consumption from './consumption.js';
|
|
7
|
+
import * as monitoring from './monitoring.js';
|
|
7
8
|
|
|
8
9
|
export {
|
|
9
10
|
api,
|
|
10
11
|
logger,
|
|
11
12
|
consumption,
|
|
13
|
+
monitoring,
|
|
12
14
|
db,
|
|
13
15
|
slack,
|
|
14
16
|
units
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
import logger from './logger.js';
|
|
4
|
+
|
|
5
|
+
const log = logger('monitoring');
|
|
6
|
+
|
|
7
|
+
export const sendHeartbeat = async () => {
|
|
8
|
+
if (!process.env.HEARTBEAT_URL) return false;
|
|
9
|
+
try {
|
|
10
|
+
await axios.post(process.env.HEARTBEAT_URL);
|
|
11
|
+
|
|
12
|
+
return true;
|
|
13
|
+
} catch (e: any) {
|
|
14
|
+
log.warn(`Failed to send heartbeat: ${e.message || e}`);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function interpolate(meterReadings: any[], targetDate: any, method?: string): any;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import moment from 'moment';
|
|
2
|
-
export function interpolate(meterReadings, targetDate, method = 'linear') {
|
|
3
|
-
if (method === 'cubic') {
|
|
4
|
-
return cubicInterpolation(meterReadings, targetDate);
|
|
5
|
-
}
|
|
6
|
-
if (method === 'linear') {
|
|
7
|
-
return linearInterpolation(meterReadings, targetDate);
|
|
8
|
-
}
|
|
9
|
-
throw new Error(`Interpolation method ${method} not supported`);
|
|
10
|
-
}
|
|
11
|
-
function linearInterpolation(meterReadings, targetDate) {
|
|
12
|
-
// Ensure the meter readings are sorted by date
|
|
13
|
-
meterReadings.sort((a, b) => moment(a.submittedAt).unix() - moment(b.submittedAt).unix());
|
|
14
|
-
const targetTimestamp = moment(targetDate).startOf('minute').unix();
|
|
15
|
-
// Find the two nearest meter readings for the target date
|
|
16
|
-
let lowerIndex = -1;
|
|
17
|
-
let upperIndex = -1;
|
|
18
|
-
for (let i = 0; i < meterReadings.length; i++) {
|
|
19
|
-
const reading = meterReadings[i];
|
|
20
|
-
if (moment(reading.submittedAt).startOf('minute').unix() <= targetTimestamp) {
|
|
21
|
-
lowerIndex = i;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
upperIndex = i;
|
|
25
|
-
break;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
// If no readings are available for interpolation, return null
|
|
29
|
-
if (lowerIndex === -1 || upperIndex === -1) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
const lowerReading = meterReadings[lowerIndex];
|
|
33
|
-
const upperReading = meterReadings[upperIndex];
|
|
34
|
-
// Linear interpolation formula
|
|
35
|
-
const consumption = lowerReading.value
|
|
36
|
-
+ ((targetTimestamp - moment(lowerReading.submittedAt).startOf('minute').unix())
|
|
37
|
-
/ (moment(upperReading.submittedAt).startOf('minute').unix() - moment(lowerReading.submittedAt).startOf('minute').unix()))
|
|
38
|
-
* (upperReading.value - lowerReading.value);
|
|
39
|
-
return consumption;
|
|
40
|
-
}
|
|
41
|
-
;
|
|
42
|
-
function cubicInterpolateCalc(x0, y0, x1, y1, x2, y2, x3, y3, x) {
|
|
43
|
-
const t = (x - x1) / (x2 - x1);
|
|
44
|
-
const t2 = t * t;
|
|
45
|
-
const t3 = t2 * t;
|
|
46
|
-
const a0 = y3 - y2 - y0 + y1;
|
|
47
|
-
const a1 = y0 - y1 - a0;
|
|
48
|
-
const a2 = y2 - y0;
|
|
49
|
-
const a3 = y1;
|
|
50
|
-
return a0 * t3 + a1 * t2 + a2 * t + a3;
|
|
51
|
-
}
|
|
52
|
-
function cubicInterpolation(meterReadings, targetDate) {
|
|
53
|
-
// Ensure the meter readings are sorted by date
|
|
54
|
-
meterReadings.sort((a, b) => moment(a.submittedAt).unix() - moment(b.submittedAt).unix());
|
|
55
|
-
const targetTimestamp = new Date(targetDate).getTime();
|
|
56
|
-
// Find the four nearest meter readings for the target date
|
|
57
|
-
let startIndex = -1;
|
|
58
|
-
for (let i = 0; i < meterReadings.length; i++) {
|
|
59
|
-
const reading = meterReadings[i];
|
|
60
|
-
if (moment(reading.submittedAt).startOf('minute').unix() > targetTimestamp) {
|
|
61
|
-
startIndex = i - 2;
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// If no readings are available for interpolation, return null
|
|
66
|
-
if (startIndex < 0 || startIndex + 3 >= meterReadings.length) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
const readings = meterReadings.slice(startIndex, startIndex + 4);
|
|
70
|
-
const interpolatedConsumption = cubicInterpolateCalc(moment(readings[0].submittedAt).startOf('minute').unix(), readings[0].value, moment(readings[1].submittedAt).startOf('minute').unix(), readings[1].value, moment(readings[2].submittedAt).startOf('minute').unix(), readings[2].value, moment(readings[3].submittedAt).startOf('minute').unix(), readings[3].value, targetTimestamp);
|
|
71
|
-
return interpolatedConsumption;
|
|
72
|
-
}
|
|
73
|
-
//# sourceMappingURL=readingsInterpolate.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"readingsInterpolate.js","sourceRoot":"","sources":["../src/lib/readingsInterpolate.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,UAAU,WAAW,CAAC,aAAoB,EAAE,UAAe,EAAE,SAAiB,QAAQ;IAC1F,IAAI,MAAM,KAAK,OAAO,EAAE;QACtB,OAAO,kBAAkB,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;KACtD;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE;QACvB,OAAO,mBAAmB,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;KACvD;IAED,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,gBAAgB,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,mBAAmB,CAAC,aAAoB,EAAE,UAAe;IAChE,+CAA+C;IAC/C,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAE1F,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpE,0DAA0D;IAC1D,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;IACpB,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QAC7C,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,EAAE;YAC3E,UAAU,GAAG,CAAC,CAAC;SAChB;aAAM;YACL,UAAU,GAAG,CAAC,CAAC;YACf,MAAM;SACP;KACF;IAED,8DAA8D;IAC9D,IAAI,UAAU,KAAK,CAAC,CAAC,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE;QAC1C,OAAO,IAAI,CAAC;KACb;IAED,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAE/C,+BAA+B;IAC/B,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK;UAClC,CAAC,CAAC,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;cAC5E,CAAC,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;cACxH,CAAC,YAAY,CAAC,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAEhD,OAAO,WAAW,CAAC;AACrB,CAAC;AAAA,CAAC;AAEF,SAAS,oBAAoB,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,CAAS;IACnI,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IACjB,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAElB,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAC7B,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IACxB,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IACnB,MAAM,EAAE,GAAG,EAAE,CAAC;IAEd,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,kBAAkB,CAAC,aAAoB,EAAE,UAAe;IAC7D,+CAA+C;IAC/C,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAE1F,MAAM,eAAe,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IAEvD,2DAA2D;IAC3D,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QAC3C,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,GAAG,eAAe,EAAE;YAC5E,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC;YACnB,MAAM;SACL;KACJ;IAED,8DAA8D;IAC9D,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE;QAC1D,OAAO,IAAI,CAAC;KACf;IAED,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;IAEjE,MAAM,uBAAuB,GAAG,oBAAoB,CAChD,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EACxD,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EACxD,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EACxD,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EACxD,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACjB,eAAe,CAClB,CAAC;IAEF,OAAO,uBAAuB,CAAC;AACnC,CAAC"}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import moment from 'moment';
|
|
2
|
-
|
|
3
|
-
export function interpolate(meterReadings: any[], targetDate: any, method: string = 'linear') {
|
|
4
|
-
if (method === 'cubic') {
|
|
5
|
-
return cubicInterpolation(meterReadings, targetDate);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
if (method === 'linear') {
|
|
9
|
-
return linearInterpolation(meterReadings, targetDate);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
throw new Error(`Interpolation method ${method} not supported`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function linearInterpolation(meterReadings: any[], targetDate: any) {
|
|
16
|
-
// Ensure the meter readings are sorted by date
|
|
17
|
-
meterReadings.sort((a, b) => moment(a.submittedAt).unix() - moment(b.submittedAt).unix());
|
|
18
|
-
|
|
19
|
-
const targetTimestamp = moment(targetDate).startOf('minute').unix();
|
|
20
|
-
|
|
21
|
-
// Find the two nearest meter readings for the target date
|
|
22
|
-
let lowerIndex = -1;
|
|
23
|
-
let upperIndex = -1;
|
|
24
|
-
for (let i = 0; i < meterReadings.length; i++) {
|
|
25
|
-
const reading = meterReadings[i];
|
|
26
|
-
if (moment(reading.submittedAt).startOf('minute').unix() <= targetTimestamp) {
|
|
27
|
-
lowerIndex = i;
|
|
28
|
-
} else {
|
|
29
|
-
upperIndex = i;
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// If no readings are available for interpolation, return null
|
|
35
|
-
if (lowerIndex === -1 || upperIndex === -1) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const lowerReading = meterReadings[lowerIndex];
|
|
40
|
-
const upperReading = meterReadings[upperIndex];
|
|
41
|
-
|
|
42
|
-
// Linear interpolation formula
|
|
43
|
-
const consumption = lowerReading.value
|
|
44
|
-
+ ((targetTimestamp - moment(lowerReading.submittedAt).startOf('minute').unix())
|
|
45
|
-
/ (moment(upperReading.submittedAt).startOf('minute').unix() - moment(lowerReading.submittedAt).startOf('minute').unix()))
|
|
46
|
-
* (upperReading.value - lowerReading.value);
|
|
47
|
-
|
|
48
|
-
return consumption;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
function cubicInterpolateCalc(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x: number) {
|
|
52
|
-
const t = (x - x1) / (x2 - x1);
|
|
53
|
-
const t2 = t * t;
|
|
54
|
-
const t3 = t2 * t;
|
|
55
|
-
|
|
56
|
-
const a0 = y3 - y2 - y0 + y1;
|
|
57
|
-
const a1 = y0 - y1 - a0;
|
|
58
|
-
const a2 = y2 - y0;
|
|
59
|
-
const a3 = y1;
|
|
60
|
-
|
|
61
|
-
return a0 * t3 + a1 * t2 + a2 * t + a3;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function cubicInterpolation(meterReadings: any[], targetDate: any) {
|
|
65
|
-
// Ensure the meter readings are sorted by date
|
|
66
|
-
meterReadings.sort((a, b) => moment(a.submittedAt).unix() - moment(b.submittedAt).unix());
|
|
67
|
-
|
|
68
|
-
const targetTimestamp = new Date(targetDate).getTime();
|
|
69
|
-
|
|
70
|
-
// Find the four nearest meter readings for the target date
|
|
71
|
-
let startIndex = -1;
|
|
72
|
-
for (let i = 0; i < meterReadings.length; i++) {
|
|
73
|
-
const reading = meterReadings[i];
|
|
74
|
-
if (moment(reading.submittedAt).startOf('minute').unix() > targetTimestamp) {
|
|
75
|
-
startIndex = i - 2;
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// If no readings are available for interpolation, return null
|
|
81
|
-
if (startIndex < 0 || startIndex + 3 >= meterReadings.length) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const readings = meterReadings.slice(startIndex, startIndex + 4);
|
|
86
|
-
|
|
87
|
-
const interpolatedConsumption = cubicInterpolateCalc(
|
|
88
|
-
moment(readings[0].submittedAt).startOf('minute').unix(),
|
|
89
|
-
readings[0].value,
|
|
90
|
-
moment(readings[1].submittedAt).startOf('minute').unix(),
|
|
91
|
-
readings[1].value,
|
|
92
|
-
moment(readings[2].submittedAt).startOf('minute').unix(),
|
|
93
|
-
readings[2].value,
|
|
94
|
-
moment(readings[3].submittedAt).startOf('minute').unix(),
|
|
95
|
-
readings[3].value,
|
|
96
|
-
targetTimestamp
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
return interpolatedConsumption;
|
|
100
|
-
}
|