homebridge-smartsystem 6.2.5 → 6.3.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/.vscode/settings.json +2 -1
- package/README.md +4 -0
- package/config.dhcpcd +5 -0
- package/config.json +63 -150
- package/duotecno/logger.js +1 -0
- package/duotecno/logger.ts +1 -0
- package/duotecno/master.js +1 -1
- package/duotecno/master.ts +2 -1
- package/duotecno/system.js +7 -2
- package/duotecno/system.ts +11 -2
- package/duotecno/types.js +3 -2
- package/duotecno/types.ts +4 -3
- package/index.spec.js +17 -19
- package/index.spec.ts +22 -19
- package/package.json +1 -1
- package/server/p1.js +22 -22
- package/server/p1.ts +15 -13
- package/server/powerbase.js +6 -4
- package/server/powerbase.ts +9 -5
- package/server/shelly.js +144 -11
- package/server/shelly.ts +161 -15
- package/server/smappee.js +4 -3
- package/server/smappee.ts +6 -3
- package/server/smartapp.js +5 -5
- package/server/smartapp.ts +6 -6
- package/server/views/power.ejs +20 -11
- package/config.p1.json +0 -59
- package/config.server.json +0 -4
- package/config.settings.json +0 -9
- package/config.smappee.json +0 -183
- package/config.smartapp.json +0 -69
- package/config.system.json +0 -247
package/server/p1.js
CHANGED
|
@@ -92,48 +92,48 @@ class P1 extends powerbase_1.PowerBase {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
consume(data) {
|
|
95
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
95
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
|
96
96
|
if (!data['32.7.0'])
|
|
97
97
|
return;
|
|
98
|
-
this.voltages = [(_a = data['32.7.0']) === null || _a === void 0 ? void 0 : _a.value, (_b = data['52.7.0']) === null || _b === void 0 ? void 0 : _b.value, (_c = data['72.7.0']) === null || _c === void 0 ? void 0 : _c.value];
|
|
99
|
-
this.realtime = { totalPower: ((_d = data['1.7.0']) === null || _d === void 0 ? void 0 : _d.value) * 1000,
|
|
100
|
-
totalReactivePower: ((_e = data['2.7.0']) === null || _e === void 0 ? void 0 : _e.value) * 1000,
|
|
101
|
-
totalExportEnergy: ((_f = data['2.8.1']) === null || _f === void 0 ? void 0 : _f.value) + ((_g = data['2.8.2']) === null || _g === void 0 ? void 0 : _g.value),
|
|
102
|
-
totalImportEnergy: ((_h = data['1.8.1']) === null || _h === void 0 ? void 0 : _h.value) + ((_j = data['1.8.2']) === null || _j === void 0 ? void 0 : _j.value),
|
|
103
|
-
monitorStatus: 0, utcTimeStamp: new Date().getTime() };
|
|
104
98
|
this.channels = {
|
|
105
99
|
0: {
|
|
106
|
-
name: "Zonnepanelen L1", type: "CT", flow: "PRODUCTION",
|
|
107
|
-
power: ((
|
|
100
|
+
name: "Zonnepanelen L1", type: "CT", flow: "PRODUCTION", voltage: (_a = data['32.7.0']) === null || _a === void 0 ? void 0 : _a.value,
|
|
101
|
+
power: ((_b = data['22.7.0']) === null || _b === void 0 ? void 0 : _b.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 0,
|
|
108
102
|
current: 0, apparentPower: 0, cosPhi: 0, formula: ""
|
|
109
103
|
},
|
|
110
104
|
1: {
|
|
111
|
-
name: "Zonnepanelen L2", type: "CT", flow: "PRODUCTION",
|
|
112
|
-
power: ((
|
|
105
|
+
name: "Zonnepanelen L2", type: "CT", flow: "PRODUCTION", voltage: (_c = data['52.7.0']) === null || _c === void 0 ? void 0 : _c.value,
|
|
106
|
+
power: ((_d = data['42.7.0']) === null || _d === void 0 ? void 0 : _d.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 1,
|
|
113
107
|
current: 0, apparentPower: 0, cosPhi: 0, formula: ""
|
|
114
108
|
},
|
|
115
109
|
2: {
|
|
116
|
-
name: "Zonnepanelen L3", type: "CT", flow: "PRODUCTION",
|
|
117
|
-
power: ((
|
|
110
|
+
name: "Zonnepanelen L3", type: "CT", flow: "PRODUCTION", voltage: (_e = data['72.7.0']) === null || _e === void 0 ? void 0 : _e.value,
|
|
111
|
+
power: ((_f = data['62.7.0']) === null || _f === void 0 ? void 0 : _f.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 2,
|
|
118
112
|
current: 0, apparentPower: 0, cosPhi: 0, formula: ""
|
|
119
113
|
},
|
|
120
114
|
3: {
|
|
121
|
-
name: "Grid L1", type: "CT", flow: "CONSUMPTION",
|
|
122
|
-
power: ((
|
|
123
|
-
current: (
|
|
115
|
+
name: "Grid L1", type: "CT", flow: "CONSUMPTION", voltage: (_g = data['32.7.0']) === null || _g === void 0 ? void 0 : _g.value,
|
|
116
|
+
power: ((_h = data['21.7.0']) === null || _h === void 0 ? void 0 : _h.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 0,
|
|
117
|
+
current: (_j = data['31.7.0']) === null || _j === void 0 ? void 0 : _j.value, apparentPower: 0, cosPhi: 0, formula: ""
|
|
124
118
|
},
|
|
125
119
|
4: {
|
|
126
|
-
name: "Grid L2", type: "CT", flow: "CONSUMPTION",
|
|
127
|
-
power: ((
|
|
128
|
-
current: (
|
|
120
|
+
name: "Grid L2", type: "CT", flow: "CONSUMPTION", voltage: (_k = data['52.7.0']) === null || _k === void 0 ? void 0 : _k.value,
|
|
121
|
+
power: ((_l = data['41.7.0']) === null || _l === void 0 ? void 0 : _l.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 1,
|
|
122
|
+
current: (_m = data['51.7.0']) === null || _m === void 0 ? void 0 : _m.value, apparentPower: 0, cosPhi: 0, formula: ""
|
|
129
123
|
},
|
|
130
124
|
5: {
|
|
131
|
-
name: "Grid L3", type: "CT", flow: "CONSUMPTION",
|
|
132
|
-
power: ((
|
|
133
|
-
current: (
|
|
125
|
+
name: "Grid L3", type: "CT", flow: "CONSUMPTION", voltage: (_o = data['72.7.0']) === null || _o === void 0 ? void 0 : _o.value,
|
|
126
|
+
power: ((_p = data['61.7.0']) === null || _p === void 0 ? void 0 : _p.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 2,
|
|
127
|
+
current: (_q = data['71.7.0']) === null || _q === void 0 ? void 0 : _q.value, apparentPower: 0, cosPhi: 0, formula: ""
|
|
134
128
|
}
|
|
135
129
|
};
|
|
136
130
|
// console.log(data);
|
|
131
|
+
this.voltages = [(_r = data['32.7.0']) === null || _r === void 0 ? void 0 : _r.value, (_s = data['52.7.0']) === null || _s === void 0 ? void 0 : _s.value, (_t = data['72.7.0']) === null || _t === void 0 ? void 0 : _t.value];
|
|
132
|
+
this.realtime = { totalPower: ((_u = data['1.7.0']) === null || _u === void 0 ? void 0 : _u.value) * 1000,
|
|
133
|
+
totalReactivePower: ((_v = data['2.7.0']) === null || _v === void 0 ? void 0 : _v.value) * 1000,
|
|
134
|
+
totalExportEnergy: ((_w = data['2.8.1']) === null || _w === void 0 ? void 0 : _w.value) + ((_x = data['2.8.2']) === null || _x === void 0 ? void 0 : _x.value),
|
|
135
|
+
totalImportEnergy: ((_y = data['1.8.1']) === null || _y === void 0 ? void 0 : _y.value) + ((_z = data['1.8.2']) === null || _z === void 0 ? void 0 : _z.value),
|
|
136
|
+
monitorStatus: 0, utcTimeStamp: new Date().getTime() };
|
|
137
137
|
this.production = this.realtime.totalPower;
|
|
138
138
|
this.consumption = this.realtime.totalReactivePower;
|
|
139
139
|
}
|
package/server/p1.ts
CHANGED
|
@@ -182,43 +182,45 @@ export class P1 extends PowerBase {
|
|
|
182
182
|
|
|
183
183
|
consume(data: P1Data) {
|
|
184
184
|
if (! data['32.7.0']) return;
|
|
185
|
-
|
|
186
|
-
this.voltages = [data['32.7.0']?.value, data['52.7.0']?.value, data['72.7.0']?.value];
|
|
187
|
-
this.realtime = { totalPower: data['1.7.0']?.value * 1000,
|
|
188
|
-
totalReactivePower: data['2.7.0']?.value * 1000,
|
|
189
|
-
totalExportEnergy: data['2.8.1']?.value + data['2.8.2']?.value,
|
|
190
|
-
totalImportEnergy: data['1.8.1']?.value + data['1.8.2']?.value,
|
|
191
|
-
monitorStatus: 0, utcTimeStamp: new Date().getTime() };
|
|
192
185
|
|
|
193
186
|
this.channels = {
|
|
194
187
|
0: {
|
|
195
|
-
name: "Zonnepanelen L1", type: "CT", flow: "PRODUCTION",
|
|
188
|
+
name: "Zonnepanelen L1", type: "CT", flow: "PRODUCTION", voltage: data['32.7.0']?.value,
|
|
196
189
|
power: data['22.7.0']?.value * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 0,
|
|
197
190
|
current: 0, apparentPower: 0, cosPhi: 0, formula: ""},
|
|
198
191
|
1: {
|
|
199
|
-
name: "Zonnepanelen L2", type: "CT", flow: "PRODUCTION",
|
|
192
|
+
name: "Zonnepanelen L2", type: "CT", flow: "PRODUCTION", voltage: data['52.7.0']?.value,
|
|
200
193
|
power: data['42.7.0']?.value * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 1,
|
|
201
194
|
current: 0, apparentPower: 0, cosPhi: 0, formula: ""},
|
|
202
195
|
2: {
|
|
203
|
-
name: "Zonnepanelen L3", type: "CT", flow: "PRODUCTION",
|
|
196
|
+
name: "Zonnepanelen L3", type: "CT", flow: "PRODUCTION", voltage: data['72.7.0']?.value,
|
|
204
197
|
power: data['62.7.0']?.value * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 2,
|
|
205
198
|
current: 0, apparentPower: 0, cosPhi: 0, formula: ""},
|
|
206
199
|
3: {
|
|
207
|
-
name: "Grid L1", type: "CT", flow: "CONSUMPTION",
|
|
200
|
+
name: "Grid L1", type: "CT", flow: "CONSUMPTION", voltage: data['32.7.0']?.value,
|
|
208
201
|
power: data['21.7.0']?.value * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 0,
|
|
209
202
|
current: data['31.7.0']?.value, apparentPower: 0, cosPhi: 0, formula: ""},
|
|
210
203
|
4: {
|
|
211
|
-
name: "Grid L2", type: "CT", flow: "CONSUMPTION",
|
|
204
|
+
name: "Grid L2", type: "CT", flow: "CONSUMPTION", voltage: data['52.7.0']?.value,
|
|
212
205
|
power: data['41.7.0']?.value * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 1,
|
|
213
206
|
current: data['51.7.0']?.value, apparentPower: 0, cosPhi: 0, formula: ""},
|
|
214
207
|
5: {
|
|
215
|
-
name: "Grid L3", type: "CT", flow: "CONSUMPTION",
|
|
208
|
+
name: "Grid L3", type: "CT", flow: "CONSUMPTION", voltage: data['72.7.0']?.value,
|
|
216
209
|
power: data['61.7.0']?.value * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 2,
|
|
217
210
|
current: data['71.7.0']?.value, apparentPower: 0, cosPhi: 0, formula: ""}
|
|
218
211
|
};
|
|
219
212
|
// console.log(data);
|
|
220
213
|
|
|
214
|
+
this.voltages = [data['32.7.0']?.value, data['52.7.0']?.value, data['72.7.0']?.value];
|
|
215
|
+
this.realtime = { totalPower: data['1.7.0']?.value * 1000,
|
|
216
|
+
totalReactivePower: data['2.7.0']?.value * 1000,
|
|
217
|
+
totalExportEnergy: data['2.8.1']?.value + data['2.8.2']?.value,
|
|
218
|
+
totalImportEnergy: data['1.8.1']?.value + data['1.8.2']?.value,
|
|
219
|
+
monitorStatus: 0, utcTimeStamp: new Date().getTime() };
|
|
220
|
+
|
|
221
221
|
this.production = this.realtime.totalPower;
|
|
222
222
|
this.consumption = this.realtime.totalReactivePower;
|
|
223
|
+
|
|
224
|
+
|
|
223
225
|
}
|
|
224
226
|
}
|
package/server/powerbase.js
CHANGED
|
@@ -17,6 +17,7 @@ const base_1 = require("./base");
|
|
|
17
17
|
// used by Smappee, Shelly PM, P1 Meter, ...
|
|
18
18
|
// Johan Coppieters, Jul 2022.
|
|
19
19
|
//
|
|
20
|
+
const kPercentChange = 0.05;
|
|
20
21
|
class PowerBase extends base_1.Base {
|
|
21
22
|
constructor(type, system) {
|
|
22
23
|
super(type);
|
|
@@ -71,7 +72,8 @@ class PowerBase extends base_1.Base {
|
|
|
71
72
|
//
|
|
72
73
|
updateConfig(config) {
|
|
73
74
|
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
-
|
|
75
|
+
if (config.address)
|
|
76
|
+
this.config.address = config.address;
|
|
75
77
|
this.writeConfig();
|
|
76
78
|
return this.init(false);
|
|
77
79
|
});
|
|
@@ -97,8 +99,8 @@ class PowerBase extends base_1.Base {
|
|
|
97
99
|
// for all bindings
|
|
98
100
|
this.bindings.forEach(b => {
|
|
99
101
|
const power = this.evalPower(b.channel);
|
|
100
|
-
if ((!isNaN(power)) && (isNaN(b.power) || (Math.abs(b.power - power) > Math.abs(b.power *
|
|
101
|
-
(0, logger_1.debug)("power", "APPLY to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power *
|
|
102
|
+
if ((!isNaN(power)) && (isNaN(b.power) || (Math.abs(b.power - power) > Math.abs(b.power * kPercentChange)))) {
|
|
103
|
+
(0, logger_1.debug)("power", "APPLY to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power * kPercentChange));
|
|
102
104
|
this.applyBinding(b, power)
|
|
103
105
|
.then(value => {
|
|
104
106
|
b.power = value;
|
|
@@ -107,7 +109,7 @@ class PowerBase extends base_1.Base {
|
|
|
107
109
|
.catch((e) => (0, logger_1.err)(this.type, e));
|
|
108
110
|
}
|
|
109
111
|
else {
|
|
110
|
-
(0, logger_1.debug)("power", "DONT to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power *
|
|
112
|
+
(0, logger_1.debug)("power", "DONT to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power * kPercentChange));
|
|
111
113
|
}
|
|
112
114
|
});
|
|
113
115
|
}
|
package/server/powerbase.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { Context } from "./webapp";
|
|
|
9
9
|
// Johan Coppieters, Jul 2022.
|
|
10
10
|
//
|
|
11
11
|
|
|
12
|
+
const kPercentChange = 0.05;
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
export class PowerBase extends Base {
|
|
14
16
|
system: System;
|
|
@@ -26,7 +28,7 @@ export class PowerBase extends Base {
|
|
|
26
28
|
totalExportEnergy: number, totalImportEnergy: number,
|
|
27
29
|
monitorStatus: number, utcTimeStamp: number }
|
|
28
30
|
channels: { [publishIndex: number]: {
|
|
29
|
-
name: string, type: string, flow: string,
|
|
31
|
+
name: string, type: string, flow: string, voltage: number,
|
|
30
32
|
power: number, exportEnergy: number, importEnergy: number, phaseId: number,
|
|
31
33
|
current: number, apparentPower: number, cosPhi: number, formula: string} };
|
|
32
34
|
|
|
@@ -85,7 +87,9 @@ export class PowerBase extends Base {
|
|
|
85
87
|
// config mgt
|
|
86
88
|
//
|
|
87
89
|
async updateConfig(config: any) {
|
|
88
|
-
|
|
90
|
+
if (config.address)
|
|
91
|
+
this.config.address = config.address;
|
|
92
|
+
|
|
89
93
|
this.writeConfig();
|
|
90
94
|
|
|
91
95
|
return this.init(false);
|
|
@@ -112,8 +116,8 @@ export class PowerBase extends Base {
|
|
|
112
116
|
// for all bindings
|
|
113
117
|
this.bindings.forEach( b => {
|
|
114
118
|
const power = this.evalPower(b.channel);
|
|
115
|
-
if ((!isNaN(power)) && (isNaN(b.power) || (Math.abs(b.power - power) > Math.abs(b.power *
|
|
116
|
-
debug("power", "APPLY to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power *
|
|
119
|
+
if ((!isNaN(power)) && (isNaN(b.power) || (Math.abs(b.power - power) > Math.abs(b.power * kPercentChange)))) {
|
|
120
|
+
debug("power", "APPLY to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power * kPercentChange))
|
|
117
121
|
this.applyBinding(b, power)
|
|
118
122
|
.then(value => {
|
|
119
123
|
b.power = value;
|
|
@@ -122,7 +126,7 @@ export class PowerBase extends Base {
|
|
|
122
126
|
.catch((e) => err(this.type, e));
|
|
123
127
|
|
|
124
128
|
} else {
|
|
125
|
-
debug("power", "DONT to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power *
|
|
129
|
+
debug("power", "DONT to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power * kPercentChange))
|
|
126
130
|
}
|
|
127
131
|
});
|
|
128
132
|
}
|
package/server/shelly.js
CHANGED
|
@@ -15,10 +15,58 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.Shelly = void 0;
|
|
16
16
|
const logger_1 = require("../duotecno/logger");
|
|
17
17
|
const powerbase_1 = require("./powerbase");
|
|
18
|
+
const api_1 = require("../duotecno/api");
|
|
19
|
+
const HTTPstatus = {
|
|
20
|
+
"wifi_sta": {
|
|
21
|
+
"connected": true,
|
|
22
|
+
"ssid": "Tele Coppieters",
|
|
23
|
+
"ip": "192.168.0.95",
|
|
24
|
+
"rssi": -73
|
|
25
|
+
},
|
|
26
|
+
"cloud": {
|
|
27
|
+
"enabled": true,
|
|
28
|
+
"connected": true
|
|
29
|
+
},
|
|
30
|
+
"mqtt": {
|
|
31
|
+
"connected": false
|
|
32
|
+
},
|
|
33
|
+
"time": "18:17",
|
|
34
|
+
"unixtime": 1663863464,
|
|
35
|
+
"serial": 7,
|
|
36
|
+
"has_update": true,
|
|
37
|
+
"mac": "C45BBE7928B9",
|
|
38
|
+
"cfg_changed_cnt": 1,
|
|
39
|
+
"actions_stats": { "skipped": 0 },
|
|
40
|
+
"relays": [{ "ison": true, "has_timer": false, "timer_started": 0, "timer_duration": 0, "timer_remaining": 0, "overpower": false, "is_valid": true, "source": "http" }],
|
|
41
|
+
"emeters": [
|
|
42
|
+
{ "power": 10.66, "pf": 1.00, "current": 0.08, "voltage": 232.06, "is_valid": true, "total": 5.3, "total_returned": 0.0 },
|
|
43
|
+
{ "power": 0.00, "pf": 0.00, "current": 0.01, "voltage": 232.06, "is_valid": true, "total": 0.0, "total_returned": 0.0 },
|
|
44
|
+
{ "power": 0.00, "pf": 0.00, "current": 0.01, "voltage": 232.05, "is_valid": true, "total": 0.0, "total_returned": 0.0 }
|
|
45
|
+
],
|
|
46
|
+
"total_power": 10.66,
|
|
47
|
+
"fs_mounted": true,
|
|
48
|
+
"update": { "status": "pending", "has_update": true, "new_version": "20220830-080542/v1.12-3EM-gcf4f7c2", "old_version": "20220209-094824/v1.11.8-g8c7bb8d" },
|
|
49
|
+
"ram_total": 49440,
|
|
50
|
+
"ram_free": 30960,
|
|
51
|
+
"fs_size": 233681,
|
|
52
|
+
"fs_free": 156875,
|
|
53
|
+
"uptime": 1364
|
|
54
|
+
};
|
|
55
|
+
const HttpEMeter0 = {
|
|
56
|
+
"power": 10.95,
|
|
57
|
+
"pf": 1.00,
|
|
58
|
+
"current": 0.08,
|
|
59
|
+
"voltage": 231.21,
|
|
60
|
+
"is_valid": true,
|
|
61
|
+
"total": 7.2,
|
|
62
|
+
"total_returned": 0.0
|
|
63
|
+
};
|
|
18
64
|
class Shelly extends powerbase_1.PowerBase {
|
|
19
65
|
constructor(system) {
|
|
20
|
-
super("
|
|
66
|
+
super("shelly", system);
|
|
21
67
|
this.timer = null;
|
|
68
|
+
this.meters = [];
|
|
69
|
+
this.nrPhases = 0;
|
|
22
70
|
// init is called by super
|
|
23
71
|
}
|
|
24
72
|
//
|
|
@@ -31,22 +79,43 @@ class Shelly extends powerbase_1.PowerBase {
|
|
|
31
79
|
//
|
|
32
80
|
// config mgt
|
|
33
81
|
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
82
|
+
updateConfig(config) {
|
|
83
|
+
const _super = Object.create(null, {
|
|
84
|
+
updateConfig: { get: () => super.updateConfig }
|
|
85
|
+
});
|
|
86
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
this.config.addresses = config.addresses;
|
|
88
|
+
return _super.updateConfig.call(this, config);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
38
91
|
subscribe() {
|
|
39
92
|
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
-
|
|
93
|
+
const kInterval = 5;
|
|
41
94
|
if (this.config.address) {
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
95
|
+
yield this.getDeviceConfig(this.config.address, true);
|
|
96
|
+
}
|
|
97
|
+
if (this.config.addresses) {
|
|
98
|
+
const list = this.config.addresses.split(",");
|
|
99
|
+
for (let inx in list) {
|
|
100
|
+
const phases = (this.config.address) ? false : (inx == "0");
|
|
101
|
+
yield this.getDeviceConfig(list[inx], phases);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
(0, logger_1.debug)("shelly", "meters: " + JSON.stringify(this.meters));
|
|
105
|
+
if (this.meters.length) {
|
|
106
|
+
this.timer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
this.realtimeCounter += kInterval;
|
|
108
|
+
// fetch data
|
|
109
|
+
const ok = yield this.fetchData();
|
|
110
|
+
if (ok)
|
|
111
|
+
this.consume();
|
|
46
112
|
// call process/bindings/rules
|
|
47
113
|
this.applyBindings();
|
|
48
114
|
this.applyRules();
|
|
49
|
-
},
|
|
115
|
+
}), kInterval * 1000);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
(0, logger_1.log)("shelly", "No Shelly PM devices configured");
|
|
50
119
|
}
|
|
51
120
|
});
|
|
52
121
|
}
|
|
@@ -58,6 +127,70 @@ class Shelly extends powerbase_1.PowerBase {
|
|
|
58
127
|
}
|
|
59
128
|
});
|
|
60
129
|
}
|
|
130
|
+
getDeviceConfig(address, phases) {
|
|
131
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
try {
|
|
133
|
+
// debug("shelly", "fetching from " + address + "/status");
|
|
134
|
+
const res = yield (0, api_1.get)(address, 80, "/status", undefined);
|
|
135
|
+
(0, logger_1.debug)("shelly", "received status from " + address + " -> " + res);
|
|
136
|
+
const status = JSON.parse(res);
|
|
137
|
+
// enrich meter with its address and index + add to the global meter list
|
|
138
|
+
status.emeters.forEach((m, inx) => {
|
|
139
|
+
m.index = inx; // index with this PM device
|
|
140
|
+
m.phase = phases;
|
|
141
|
+
if (phases) {
|
|
142
|
+
m.phaseNr = this.nrPhases;
|
|
143
|
+
this.nrPhases++;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
m.phaseNr = -1;
|
|
147
|
+
}
|
|
148
|
+
m.address = address;
|
|
149
|
+
this.meters.push(m);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
(0, logger_1.err)("shelly", error.message);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
fetchData() {
|
|
158
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
+
try {
|
|
160
|
+
for (let meter in this.meters) {
|
|
161
|
+
// debug("shelly", "fetching from " + this.config.address + "/emeter/" + meter);
|
|
162
|
+
const res = yield (0, api_1.get)(this.meters[meter].address, 80, "/emeter/" + this.meters[meter].index, undefined);
|
|
163
|
+
(0, logger_1.debug)("shelly", "received meter: " + meter + " -> " + res);
|
|
164
|
+
this.meters[meter] = Object.assign(Object.assign({}, this.meters[meter]), JSON.parse(res));
|
|
165
|
+
}
|
|
166
|
+
;
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
(0, logger_1.err)("shelly", error.message);
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
consume() {
|
|
176
|
+
// copy this.meters into the powerbase structures
|
|
177
|
+
this.meters.forEach((meter, inx) => {
|
|
178
|
+
this.channels[inx] = {
|
|
179
|
+
name: "Meter " + (inx + 1), type: "CT", flow: "PRODUCTION",
|
|
180
|
+
power: meter.power, exportEnergy: meter.total_returned, importEnergy: meter.total, phaseId: meter.phaseNr,
|
|
181
|
+
current: meter.power / meter.voltage, apparentPower: 0, cosPhi: 0, formula: "", voltage: meter.voltage
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
this.voltages = [0, 0, 0];
|
|
185
|
+
this.meters.filter(m => m.phase).forEach(m => this.voltages[m.phaseNr] = m.voltage);
|
|
186
|
+
this.realtime = { totalPower: this.meters.reduce((prev, curr) => curr.power + prev, 0),
|
|
187
|
+
totalReactivePower: 0,
|
|
188
|
+
totalExportEnergy: this.meters.reduce((prev, curr) => curr.total_returned + prev, 0),
|
|
189
|
+
totalImportEnergy: this.meters.reduce((prev, curr) => curr.total + prev, 0),
|
|
190
|
+
monitorStatus: 0, utcTimeStamp: new Date().getTime() };
|
|
191
|
+
this.production = this.realtime.totalPower;
|
|
192
|
+
this.consumption = this.realtime.totalReactivePower;
|
|
193
|
+
}
|
|
61
194
|
}
|
|
62
195
|
exports.Shelly = Shelly;
|
|
63
196
|
//# sourceMappingURL=shelly.js.map
|
package/server/shelly.ts
CHANGED
|
@@ -3,21 +3,86 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { System } from "../duotecno/system";
|
|
6
|
-
import {
|
|
7
|
-
import { debug, log } from "../duotecno/logger";
|
|
6
|
+
import { ShellyConfig } from "../duotecno/types";
|
|
7
|
+
import { debug, err, log } from "../duotecno/logger";
|
|
8
8
|
import { PowerBase } from "./powerbase";
|
|
9
|
+
import { get } from "../duotecno/api";
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
const HTTPstatus = {
|
|
13
|
+
"wifi_sta": {
|
|
14
|
+
"connected": true,
|
|
15
|
+
"ssid": "Tele Coppieters",
|
|
16
|
+
"ip": "192.168.0.95",
|
|
17
|
+
"rssi": -73
|
|
18
|
+
},
|
|
19
|
+
"cloud": {
|
|
20
|
+
"enabled": true,
|
|
21
|
+
"connected": true
|
|
22
|
+
},
|
|
23
|
+
"mqtt": {
|
|
24
|
+
"connected": false
|
|
25
|
+
},
|
|
26
|
+
"time": "18:17",
|
|
27
|
+
"unixtime": 1663863464,
|
|
28
|
+
"serial": 7,
|
|
29
|
+
"has_update": true,
|
|
30
|
+
"mac": "C45BBE7928B9",
|
|
31
|
+
"cfg_changed_cnt": 1,
|
|
32
|
+
"actions_stats": { "skipped": 0 },
|
|
33
|
+
"relays": [{ "ison": true, "has_timer": false, "timer_started": 0, "timer_duration": 0, "timer_remaining": 0, "overpower": false, "is_valid": true, "source": "http" }],
|
|
34
|
+
"emeters": [
|
|
35
|
+
{ "power": 10.66, "pf": 1.00, "current": 0.08, "voltage": 232.06, "is_valid": true, "total": 5.3, "total_returned": 0.0 },
|
|
36
|
+
{ "power": 0.00, "pf": 0.00, "current": 0.01, "voltage": 232.06, "is_valid": true, "total": 0.0, "total_returned": 0.0 },
|
|
37
|
+
{ "power": 0.00, "pf": 0.00, "current": 0.01, "voltage": 232.05, "is_valid": true, "total": 0.0, "total_returned": 0.0 }
|
|
38
|
+
],
|
|
39
|
+
"total_power": 10.66,
|
|
40
|
+
"fs_mounted": true,
|
|
41
|
+
"update": { "status": "pending", "has_update": true, "new_version": "20220830-080542/v1.12-3EM-gcf4f7c2", "old_version": "20220209-094824/v1.11.8-g8c7bb8d" },
|
|
42
|
+
"ram_total": 49440,
|
|
43
|
+
"ram_free": 30960,
|
|
44
|
+
"fs_size": 233681,
|
|
45
|
+
"fs_free": 156875,
|
|
46
|
+
"uptime": 1364
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const HttpEMeter0 = {
|
|
50
|
+
"power":10.95,
|
|
51
|
+
"pf":1.00,
|
|
52
|
+
"current":0.08,
|
|
53
|
+
"voltage":231.21,
|
|
54
|
+
"is_valid":true,
|
|
55
|
+
"total":7.2,
|
|
56
|
+
"total_returned":0.0
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
interface Meter {
|
|
60
|
+
address: string;
|
|
61
|
+
index: number;
|
|
62
|
+
phase: boolean;
|
|
63
|
+
phaseNr: number;
|
|
64
|
+
power: number;
|
|
65
|
+
pf: number;
|
|
66
|
+
current: number;
|
|
67
|
+
voltage: number;
|
|
68
|
+
is_valid: boolean;
|
|
69
|
+
total: number;
|
|
70
|
+
total_returned: number;
|
|
71
|
+
}
|
|
11
72
|
|
|
12
73
|
export class Shelly extends PowerBase {
|
|
13
|
-
config:
|
|
74
|
+
config: ShellyConfig; // redefine
|
|
14
75
|
timer: NodeJS.Timer;
|
|
15
76
|
|
|
77
|
+
meters: Array<Meter>;
|
|
78
|
+
nrPhases: number;
|
|
16
79
|
|
|
17
80
|
constructor(system: System) {
|
|
18
|
-
super("
|
|
81
|
+
super("shelly", system);
|
|
19
82
|
|
|
20
83
|
this.timer = null;
|
|
84
|
+
this.meters = [];
|
|
85
|
+
this.nrPhases = 0;
|
|
21
86
|
// init is called by super
|
|
22
87
|
}
|
|
23
88
|
|
|
@@ -33,26 +98,43 @@ export class Shelly extends PowerBase {
|
|
|
33
98
|
//
|
|
34
99
|
// config mgt
|
|
35
100
|
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
101
|
+
async updateConfig(config: any) {
|
|
102
|
+
this.config.addresses = config.addresses;
|
|
103
|
+
return super.updateConfig(config);
|
|
104
|
+
}
|
|
40
105
|
|
|
41
106
|
|
|
42
107
|
async subscribe() {
|
|
43
|
-
|
|
44
|
-
|
|
108
|
+
const kInterval = 5;
|
|
109
|
+
|
|
45
110
|
if (this.config.address) {
|
|
46
|
-
this.
|
|
47
|
-
|
|
111
|
+
await this.getDeviceConfig(this.config.address, true);
|
|
112
|
+
}
|
|
113
|
+
if (this.config.addresses) {
|
|
114
|
+
const list = this.config.addresses.split(",");
|
|
115
|
+
for (let inx in list) {
|
|
116
|
+
const phases = (this.config.address) ? false : (inx == "0");
|
|
117
|
+
await this.getDeviceConfig(list[inx], phases);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
debug("shelly", "meters: " + JSON.stringify(this.meters));
|
|
121
|
+
|
|
122
|
+
if (this.meters.length) {
|
|
123
|
+
this.timer = setInterval(async () => {
|
|
124
|
+
this.realtimeCounter += kInterval;
|
|
48
125
|
|
|
49
|
-
// fetch data
|
|
50
|
-
|
|
126
|
+
// fetch data
|
|
127
|
+
const ok = await this.fetchData();
|
|
128
|
+
if (ok)
|
|
129
|
+
this.consume();
|
|
51
130
|
|
|
52
131
|
// call process/bindings/rules
|
|
53
132
|
this.applyBindings();
|
|
54
133
|
this.applyRules();
|
|
55
|
-
},
|
|
134
|
+
}, kInterval * 1000);
|
|
135
|
+
|
|
136
|
+
} else {
|
|
137
|
+
log("shelly", "No Shelly PM devices configured");
|
|
56
138
|
}
|
|
57
139
|
}
|
|
58
140
|
|
|
@@ -63,4 +145,68 @@ export class Shelly extends PowerBase {
|
|
|
63
145
|
}
|
|
64
146
|
}
|
|
65
147
|
|
|
148
|
+
async getDeviceConfig(address: string, phases: boolean) {
|
|
149
|
+
try {
|
|
150
|
+
// debug("shelly", "fetching from " + address + "/status");
|
|
151
|
+
const res = await get(address, 80, "/status", undefined);
|
|
152
|
+
debug("shelly", "received status from " + address + " -> " + res);
|
|
153
|
+
const status = JSON.parse(res);
|
|
154
|
+
|
|
155
|
+
// enrich meter with its address and index + add to the global meter list
|
|
156
|
+
status.emeters.forEach((m, inx) => {
|
|
157
|
+
m.index = inx; // index with this PM device
|
|
158
|
+
m.phase = phases;
|
|
159
|
+
if (phases) {
|
|
160
|
+
m.phaseNr = this.nrPhases;
|
|
161
|
+
this.nrPhases++;
|
|
162
|
+
} else {
|
|
163
|
+
m.phaseNr = -1;
|
|
164
|
+
}
|
|
165
|
+
m.address = address;
|
|
166
|
+
this.meters.push(m);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
} catch(error) {
|
|
170
|
+
err("shelly", error.message)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async fetchData(): Promise<boolean> {
|
|
175
|
+
try {
|
|
176
|
+
for (let meter in this.meters) {
|
|
177
|
+
// debug("shelly", "fetching from " + this.config.address + "/emeter/" + meter);
|
|
178
|
+
const res = await get(this.meters[meter].address, 80, "/emeter/"+this.meters[meter].index, undefined);
|
|
179
|
+
debug("shelly", "received meter: " + meter + " -> " + res);
|
|
180
|
+
this.meters[meter] = {... this.meters[meter], ...JSON.parse(res)};
|
|
181
|
+
};
|
|
182
|
+
return true;
|
|
183
|
+
|
|
184
|
+
} catch(error) {
|
|
185
|
+
err("shelly", error.message);
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
consume() {
|
|
191
|
+
// copy this.meters into the powerbase structures
|
|
192
|
+
this.meters.forEach((meter, inx) => {
|
|
193
|
+
this.channels[inx] = {
|
|
194
|
+
name: "Meter " + (inx+1), type: "CT", flow: "PRODUCTION",
|
|
195
|
+
power: meter.power, exportEnergy: meter.total_returned, importEnergy: meter.total, phaseId: meter.phaseNr,
|
|
196
|
+
current: meter.power / meter.voltage, apparentPower: 0, cosPhi: 0, formula: "", voltage: meter.voltage}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
this.voltages = [0,0,0];
|
|
200
|
+
this.meters.filter(m => m.phase).forEach(m => this.voltages[m.phaseNr] = m.voltage);
|
|
201
|
+
|
|
202
|
+
this.realtime = { totalPower: this.meters.reduce((prev, curr) => curr.power + prev, 0),
|
|
203
|
+
totalReactivePower: 0,
|
|
204
|
+
totalExportEnergy: this.meters.reduce((prev, curr) => curr.total_returned + prev, 0),
|
|
205
|
+
totalImportEnergy: this.meters.reduce((prev, curr) => curr.total + prev, 0),
|
|
206
|
+
monitorStatus: 0, utcTimeStamp: new Date().getTime() };
|
|
207
|
+
|
|
208
|
+
this.production = this.realtime.totalPower;
|
|
209
|
+
this.consumption = this.realtime.totalReactivePower;
|
|
210
|
+
}
|
|
211
|
+
|
|
66
212
|
}
|
package/server/smappee.js
CHANGED
|
@@ -213,6 +213,9 @@ class Smappee extends powerbase_1.PowerBase {
|
|
|
213
213
|
this.realtime = { totalPower: message.totalPower, totalReactivePower: message.totalReactivePower,
|
|
214
214
|
totalExportEnergy: message.totalExportEnergy, totalImportEnergy: message.totalImportEnergy,
|
|
215
215
|
monitorStatus: message.monitorStatus, utcTimeStamp: message.utcTimeStamp };
|
|
216
|
+
message.voltages.forEach(v => {
|
|
217
|
+
this.voltages[v.phaseId] = v.voltage;
|
|
218
|
+
});
|
|
216
219
|
message.channelPowers.forEach(p => {
|
|
217
220
|
const inx = p.publishIndex;
|
|
218
221
|
if (!this.channels[inx]) {
|
|
@@ -223,14 +226,12 @@ class Smappee extends powerbase_1.PowerBase {
|
|
|
223
226
|
this.channels[inx].exportEnergy = p.exportEnergy;
|
|
224
227
|
this.channels[inx].importEnergy = p.importEnergy;
|
|
225
228
|
this.channels[inx].phaseId = p.phaseId;
|
|
229
|
+
this.channels[inx].voltage = this.voltages[p.phaseId] || 0;
|
|
226
230
|
this.channels[inx].current = p.current;
|
|
227
231
|
this.channels[inx].apparentPower = p.apparentPower;
|
|
228
232
|
this.channels[inx].cosPhi = p.cosPhi;
|
|
229
233
|
this.channels[inx].formula = p.formula;
|
|
230
234
|
});
|
|
231
|
-
message.voltages.forEach(v => {
|
|
232
|
-
this.voltages[v.phaseId] = v.voltage;
|
|
233
|
-
});
|
|
234
235
|
this.calcProducAndConsumption(message);
|
|
235
236
|
}
|
|
236
237
|
// check rules
|