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/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: ((_k = data['22.7.0']) === null || _k === void 0 ? void 0 : _k.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 0,
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: ((_l = data['42.7.0']) === null || _l === void 0 ? void 0 : _l.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 1,
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: ((_m = data['62.7.0']) === null || _m === void 0 ? void 0 : _m.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 2,
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: ((_o = data['21.7.0']) === null || _o === void 0 ? void 0 : _o.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 0,
123
- current: (_p = data['31.7.0']) === null || _p === void 0 ? void 0 : _p.value, apparentPower: 0, cosPhi: 0, formula: ""
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: ((_q = data['41.7.0']) === null || _q === void 0 ? void 0 : _q.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 1,
128
- current: (_r = data['51.7.0']) === null || _r === void 0 ? void 0 : _r.value, apparentPower: 0, cosPhi: 0, formula: ""
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: ((_s = data['61.7.0']) === null || _s === void 0 ? void 0 : _s.value) * 1000, exportEnergy: 0, importEnergy: 0, phaseId: 2,
133
- current: (_t = data['71.7.0']) === null || _t === void 0 ? void 0 : _t.value, apparentPower: 0, cosPhi: 0, formula: ""
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
  }
@@ -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
- this.config.address = config.address;
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 * 0.1)))) {
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 * 0.1));
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 * 0.1));
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
  }
@@ -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
- this.config.address = config.address;
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 * 0.1)))) {
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 * 0.1))
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 * 0.1))
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("p1", system);
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
- // async updateConfig(config: any) {
35
- // // nothing special... for now
36
- // return super.updateConfig(config);
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
- (0, logger_1.log)("shelly", "connecting to Shelly: " + this.config.address);
93
+ const kInterval = 5;
41
94
  if (this.config.address) {
42
- this.timer = setInterval(() => {
43
- this.realtimeCounter++;
44
- // fetch data from API
45
- (0, logger_1.debug)("shelly", "call shelly API");
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
- }, 5000);
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 { P1Config } from "../duotecno/types";
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: P1Config; // redefine
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("p1", system);
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
- // async updateConfig(config: any) {
37
- // // nothing special... for now
38
- // return super.updateConfig(config);
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
- log("shelly", "connecting to Shelly: " + this.config.address);
44
-
108
+ const kInterval = 5;
109
+
45
110
  if (this.config.address) {
46
- this.timer = setInterval(() => {
47
- this.realtimeCounter++;
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 from API
50
- debug("shelly", "call shelly API");
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
- }, 5000);
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