homebridge-smartsystem 6.2.6 → 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/README.md +3 -0
- package/config.json +18 -193
- 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/package.json +1 -1
- package/server/p1.js +22 -22
- package/server/p1.ts +15 -13
- package/server/powerbase.js +2 -1
- package/server/powerbase.ts +4 -2
- 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 +5 -5
- package/server/views/power.ejs +20 -11
package/README.md
CHANGED
|
@@ -275,6 +275,9 @@ Homebridge UI version
|
|
|
275
275
|
- 4: fixed empty name bug in unit-def / new accessory()
|
|
276
276
|
- 5: fixed bad displayNames in published units
|
|
277
277
|
- 6: power: 5% instead of 10% for power rules
|
|
278
|
+
|
|
279
|
+
### v6.3.x - 23-09-2022 - Shelly PM release
|
|
280
|
+
- 1: first test 1 PM
|
|
278
281
|
|
|
279
282
|
|
|
280
283
|
## Hardware
|
package/config.json
CHANGED
|
@@ -247,9 +247,14 @@
|
|
|
247
247
|
"power": null
|
|
248
248
|
}
|
|
249
249
|
],
|
|
250
|
-
"address": "192.168.0.
|
|
250
|
+
"address": "192.168.0.207"
|
|
251
|
+
},
|
|
252
|
+
"shelly": {
|
|
253
|
+
"address": "192.168.0.95",
|
|
254
|
+
"addresses": "192.168.0.95,192.168.0.95",
|
|
255
|
+
"rules": [],
|
|
256
|
+
"bindings": []
|
|
251
257
|
},
|
|
252
|
-
"shelly": false,
|
|
253
258
|
"smartapp": {
|
|
254
259
|
"apiPort": 8581,
|
|
255
260
|
"apiHost": "192.168.0.99",
|
|
@@ -373,184 +378,16 @@
|
|
|
373
378
|
}
|
|
374
379
|
],
|
|
375
380
|
"cunits": [
|
|
376
|
-
{
|
|
377
|
-
"logicalNodeAddress": 252,
|
|
378
|
-
"logicalAddress": 102,
|
|
379
|
-
"masterAddress": "192.168.0.97",
|
|
380
|
-
"masterPort": 5001,
|
|
381
|
-
"name": "unit 252:102",
|
|
382
|
-
"displayName": "unit 252:102",
|
|
383
|
-
"extendedType": 0,
|
|
384
|
-
"active": "N",
|
|
385
|
-
"used": "N",
|
|
386
|
-
"group": 0
|
|
387
|
-
},
|
|
388
|
-
{
|
|
389
|
-
"logicalNodeAddress": 252,
|
|
390
|
-
"logicalAddress": 103,
|
|
391
|
-
"masterAddress": "192.168.0.97",
|
|
392
|
-
"masterPort": 5001,
|
|
393
|
-
"name": "unit 252:103",
|
|
394
|
-
"displayName": "unit 252:103",
|
|
395
|
-
"extendedType": 0,
|
|
396
|
-
"active": "N",
|
|
397
|
-
"used": "N",
|
|
398
|
-
"group": 0
|
|
399
|
-
},
|
|
400
|
-
{
|
|
401
|
-
"logicalNodeAddress": 252,
|
|
402
|
-
"logicalAddress": 100,
|
|
403
|
-
"masterAddress": "192.168.0.97",
|
|
404
|
-
"masterPort": 5001,
|
|
405
|
-
"name": "unit 252:100",
|
|
406
|
-
"displayName": "unit 252:100",
|
|
407
|
-
"extendedType": 0,
|
|
408
|
-
"active": "N",
|
|
409
|
-
"used": "N",
|
|
410
|
-
"group": 0
|
|
411
|
-
},
|
|
412
|
-
{
|
|
413
|
-
"logicalNodeAddress": 252,
|
|
414
|
-
"logicalAddress": 101,
|
|
415
|
-
"masterAddress": "192.168.0.97",
|
|
416
|
-
"masterPort": 5001,
|
|
417
|
-
"name": "unit 252:101",
|
|
418
|
-
"displayName": "unit 252:101",
|
|
419
|
-
"extendedType": 0,
|
|
420
|
-
"active": "N",
|
|
421
|
-
"used": "N",
|
|
422
|
-
"group": 0
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
"logicalNodeAddress": 252,
|
|
426
|
-
"logicalAddress": 105,
|
|
427
|
-
"masterAddress": "192.168.0.97",
|
|
428
|
-
"masterPort": 5001,
|
|
429
|
-
"name": "unit 252:105",
|
|
430
|
-
"displayName": "unit 252:105",
|
|
431
|
-
"extendedType": 0,
|
|
432
|
-
"active": "N",
|
|
433
|
-
"used": "N",
|
|
434
|
-
"group": 0
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
"logicalNodeAddress": 252,
|
|
438
|
-
"logicalAddress": 104,
|
|
439
|
-
"masterAddress": "192.168.0.97",
|
|
440
|
-
"masterPort": 5001,
|
|
441
|
-
"name": "unit 252:104",
|
|
442
|
-
"displayName": "unit 252:104",
|
|
443
|
-
"extendedType": 0,
|
|
444
|
-
"active": "N",
|
|
445
|
-
"used": "N",
|
|
446
|
-
"group": 0
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
"logicalNodeAddress": 252,
|
|
450
|
-
"logicalAddress": 106,
|
|
451
|
-
"masterAddress": "192.168.0.97",
|
|
452
|
-
"masterPort": 5001,
|
|
453
|
-
"name": "unit 252:106",
|
|
454
|
-
"displayName": "unit 252:106",
|
|
455
|
-
"extendedType": 0,
|
|
456
|
-
"active": "N",
|
|
457
|
-
"used": "N",
|
|
458
|
-
"group": 0
|
|
459
|
-
},
|
|
460
381
|
{
|
|
461
382
|
"logicalNodeAddress": 3,
|
|
462
|
-
"logicalAddress":
|
|
463
|
-
"masterAddress": "192.168.0.97",
|
|
464
|
-
"masterPort": 5001,
|
|
465
|
-
"name": "unit 3:1",
|
|
466
|
-
"displayName": "unit 3:1",
|
|
467
|
-
"extendedType": 0,
|
|
468
|
-
"active": "N",
|
|
469
|
-
"used": "N",
|
|
470
|
-
"group": 0
|
|
471
|
-
},
|
|
472
|
-
{
|
|
473
|
-
"logicalNodeAddress": 3,
|
|
474
|
-
"logicalAddress": 0,
|
|
475
|
-
"masterAddress": "192.168.0.97",
|
|
476
|
-
"masterPort": 5001,
|
|
477
|
-
"name": "unit 3:0",
|
|
478
|
-
"displayName": "unit 3:0",
|
|
479
|
-
"extendedType": 0,
|
|
480
|
-
"active": "N",
|
|
481
|
-
"used": "N",
|
|
482
|
-
"group": 0
|
|
483
|
-
},
|
|
484
|
-
{
|
|
485
|
-
"logicalNodeAddress": 3,
|
|
486
|
-
"logicalAddress": 30,
|
|
487
|
-
"masterAddress": "192.168.0.97",
|
|
488
|
-
"masterPort": 5001,
|
|
489
|
-
"name": "unit 3:30",
|
|
490
|
-
"displayName": "unit 3:30",
|
|
491
|
-
"extendedType": 0,
|
|
492
|
-
"active": "N",
|
|
493
|
-
"used": "N",
|
|
494
|
-
"group": 0
|
|
495
|
-
},
|
|
496
|
-
{
|
|
497
|
-
"logicalNodeAddress": 3,
|
|
498
|
-
"logicalAddress": 29,
|
|
499
|
-
"masterAddress": "192.168.0.97",
|
|
500
|
-
"masterPort": 5001,
|
|
501
|
-
"name": "unit 3:29",
|
|
502
|
-
"displayName": "unit 3:29",
|
|
503
|
-
"extendedType": 0,
|
|
504
|
-
"active": "N",
|
|
505
|
-
"used": "N",
|
|
506
|
-
"group": 0
|
|
507
|
-
},
|
|
508
|
-
{
|
|
509
|
-
"logicalNodeAddress": 3,
|
|
510
|
-
"logicalAddress": 35,
|
|
511
|
-
"masterAddress": "192.168.0.97",
|
|
512
|
-
"masterPort": 5001,
|
|
513
|
-
"name": "unit 3:35",
|
|
514
|
-
"displayName": "unit 3:35",
|
|
515
|
-
"extendedType": 0,
|
|
516
|
-
"active": "N",
|
|
517
|
-
"used": "N",
|
|
518
|
-
"group": 0
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
"logicalNodeAddress": 3,
|
|
522
|
-
"logicalAddress": 34,
|
|
523
|
-
"masterAddress": "192.168.0.97",
|
|
524
|
-
"masterPort": 5001,
|
|
525
|
-
"name": "unit 3:34",
|
|
526
|
-
"displayName": "unit 3:34",
|
|
527
|
-
"extendedType": 0,
|
|
528
|
-
"active": "N",
|
|
529
|
-
"used": "N",
|
|
530
|
-
"group": 0
|
|
531
|
-
},
|
|
532
|
-
{
|
|
533
|
-
"logicalNodeAddress": 3,
|
|
534
|
-
"logicalAddress": 33,
|
|
535
|
-
"masterAddress": "192.168.0.97",
|
|
536
|
-
"masterPort": 5001,
|
|
537
|
-
"name": "unit 3:33",
|
|
538
|
-
"displayName": "unit 3:33",
|
|
539
|
-
"extendedType": 0,
|
|
540
|
-
"active": "N",
|
|
541
|
-
"used": "N",
|
|
542
|
-
"group": 0
|
|
543
|
-
},
|
|
544
|
-
{
|
|
545
|
-
"logicalNodeAddress": 3,
|
|
546
|
-
"logicalAddress": 36,
|
|
383
|
+
"logicalAddress": 4,
|
|
547
384
|
"masterAddress": "192.168.0.97",
|
|
548
385
|
"masterPort": 5001,
|
|
549
|
-
"name": "
|
|
550
|
-
"displayName": "
|
|
551
|
-
"extendedType":
|
|
552
|
-
"active": "
|
|
553
|
-
"used": "
|
|
386
|
+
"name": "All off",
|
|
387
|
+
"displayName": "Alles uitje",
|
|
388
|
+
"extendedType": 7,
|
|
389
|
+
"active": "Y",
|
|
390
|
+
"used": "Y",
|
|
554
391
|
"group": 0
|
|
555
392
|
},
|
|
556
393
|
{
|
|
@@ -558,23 +395,11 @@
|
|
|
558
395
|
"logicalAddress": 5,
|
|
559
396
|
"masterAddress": "192.168.0.97",
|
|
560
397
|
"masterPort": 5001,
|
|
561
|
-
"name": "
|
|
562
|
-
"displayName": "
|
|
563
|
-
"extendedType":
|
|
564
|
-
"active": "
|
|
565
|
-
"used": "
|
|
566
|
-
"group": 0
|
|
567
|
-
},
|
|
568
|
-
{
|
|
569
|
-
"logicalNodeAddress": 3,
|
|
570
|
-
"logicalAddress": 4,
|
|
571
|
-
"masterAddress": "192.168.0.97",
|
|
572
|
-
"masterPort": 5001,
|
|
573
|
-
"name": "unit 3:4",
|
|
574
|
-
"displayName": "unit 3:4",
|
|
575
|
-
"extendedType": 0,
|
|
576
|
-
"active": "N",
|
|
577
|
-
"used": "N",
|
|
398
|
+
"name": "All on",
|
|
399
|
+
"displayName": "Alles aantje",
|
|
400
|
+
"extendedType": 7,
|
|
401
|
+
"active": "Y",
|
|
402
|
+
"used": "Y",
|
|
578
403
|
"group": 0
|
|
579
404
|
}
|
|
580
405
|
]
|
package/duotecno/logger.js
CHANGED
package/duotecno/logger.ts
CHANGED
package/duotecno/master.js
CHANGED
|
@@ -466,6 +466,7 @@ class Master extends base_1.Base {
|
|
|
466
466
|
receiveUnitInfo(message) {
|
|
467
467
|
const unitInfo = protocol_1.Protocol.makeUnitInfo(message);
|
|
468
468
|
let unit = this.findUnit(unitInfo.logicalNodeAddress, unitInfo.logicalAddress);
|
|
469
|
+
this.info("> received unit info: " + unitInfo.name + " (" + unitInfo.logicalNodeAddress + "," + unitInfo.logicalAddress + ") for unit: " + (unit === null || unit === void 0 ? void 0 : unit.name));
|
|
469
470
|
if (!unit) {
|
|
470
471
|
// find if in config
|
|
471
472
|
const cunit = this.system.config.cunits.find(u => ((u.logicalNodeAddress == unitInfo.logicalNodeAddress) && (u.logicalAddress == unitInfo.logicalAddress)));
|
|
@@ -473,7 +474,6 @@ class Master extends base_1.Base {
|
|
|
473
474
|
unitInfo.extendedType = cunit.extendedType;
|
|
474
475
|
const node = this.findNode(unitInfo.logicalNodeAddress);
|
|
475
476
|
if (node) {
|
|
476
|
-
this.info("> received unit info: " + unitInfo.name + " (" + unitInfo.logicalNodeAddress + "," + unitInfo.logicalAddress + ")");
|
|
477
477
|
unit = new protocol_1.Unit(node, unitInfo, this.system.config.mood);
|
|
478
478
|
node.units.push(unit);
|
|
479
479
|
}
|
package/duotecno/master.ts
CHANGED
|
@@ -520,6 +520,8 @@ export class Master extends Base {
|
|
|
520
520
|
|
|
521
521
|
let unit = this.findUnit(unitInfo.logicalNodeAddress, unitInfo.logicalAddress);
|
|
522
522
|
|
|
523
|
+
this.info("> received unit info: " + unitInfo.name + " ("+unitInfo.logicalNodeAddress+","+unitInfo.logicalAddress+") for unit: " + unit?.name);
|
|
524
|
+
|
|
523
525
|
if (!unit) {
|
|
524
526
|
// find if in config
|
|
525
527
|
const cunit = this.system.config.cunits.find(u => ((u.logicalNodeAddress == unitInfo.logicalNodeAddress) && (u.logicalAddress == unitInfo.logicalAddress)));
|
|
@@ -527,7 +529,6 @@ export class Master extends Base {
|
|
|
527
529
|
|
|
528
530
|
const node = this.findNode(unitInfo.logicalNodeAddress);
|
|
529
531
|
if (node) {
|
|
530
|
-
this.info("> received unit info: " + unitInfo.name + " ("+unitInfo.logicalNodeAddress+","+unitInfo.logicalAddress+")");
|
|
531
532
|
unit = new Unit(node, unitInfo, this.system.config.mood);
|
|
532
533
|
node.units.push(unit);
|
|
533
534
|
} else {
|
package/duotecno/system.js
CHANGED
|
@@ -159,28 +159,33 @@ class System extends base_1.Base {
|
|
|
159
159
|
this.writeConfig();
|
|
160
160
|
}
|
|
161
161
|
setActiveState(item) {
|
|
162
|
+
// for nodes
|
|
162
163
|
if (item instanceof protocol_1.Node) {
|
|
163
164
|
const masterAddress = item.master.getAddress();
|
|
164
165
|
const masterPort = item.master.getPort();
|
|
166
|
+
// check if at least 1 unit is used
|
|
165
167
|
const unitConfig = this.config.cunits.find((cu) => (cu.logicalNodeAddress === item.logicalAddress) &&
|
|
166
168
|
(cu.masterAddress === masterAddress) &&
|
|
167
169
|
(cu.masterPort == masterPort));
|
|
168
|
-
// there
|
|
170
|
+
// if there is: set active to true of this node
|
|
169
171
|
item.active = !!unitConfig;
|
|
170
172
|
}
|
|
173
|
+
// for units
|
|
171
174
|
if (item instanceof protocol_1.Unit) {
|
|
172
175
|
const masterAddress = item.node.master.getAddress();
|
|
173
176
|
const masterPort = item.node.master.getPort();
|
|
177
|
+
// see if there is a config record for this unit
|
|
174
178
|
const unitConfig = this.config.cunits.find((cu) => (cu.logicalAddress === item.logicalAddress) &&
|
|
175
179
|
(cu.logicalNodeAddress === item.logicalNodeAddress) &&
|
|
176
180
|
(cu.masterAddress === masterAddress) &&
|
|
177
181
|
(cu.masterPort == masterPort));
|
|
178
|
-
|
|
182
|
+
// if there is: copy active, used, displayName and group
|
|
179
183
|
if (unitConfig) {
|
|
180
184
|
item.active = (unitConfig.active === "Y");
|
|
181
185
|
item.used = (unitConfig.used === "Y");
|
|
182
186
|
item.displayName = unitConfig.displayName || unitConfig.name;
|
|
183
187
|
item.group = unitConfig.group;
|
|
188
|
+
(0, logger_1.log)("system", "System.setActiveState(unit) -> config name: " + unitConfig.name + ", item name: " + item.name);
|
|
184
189
|
}
|
|
185
190
|
else {
|
|
186
191
|
item.active = false;
|
package/duotecno/system.ts
CHANGED
|
@@ -166,31 +166,40 @@ export class System extends Base {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
setActiveState(item: Node | Unit) {
|
|
169
|
+
// for nodes
|
|
169
170
|
if (item instanceof Node) {
|
|
170
171
|
const masterAddress = item.master.getAddress();
|
|
171
172
|
const masterPort = item.master.getPort();
|
|
173
|
+
|
|
174
|
+
// check if at least 1 unit is used
|
|
172
175
|
const unitConfig = this.config.cunits.find((cu: UnitConfig) =>
|
|
173
176
|
(cu.logicalNodeAddress === item.logicalAddress) &&
|
|
174
177
|
(cu.masterAddress === masterAddress) &&
|
|
175
178
|
(cu.masterPort == masterPort));
|
|
176
179
|
|
|
177
|
-
// there
|
|
180
|
+
// if there is: set active to true of this node
|
|
178
181
|
item.active = !! unitConfig;
|
|
179
182
|
}
|
|
183
|
+
|
|
184
|
+
// for units
|
|
180
185
|
if (item instanceof Unit) {
|
|
181
186
|
const masterAddress = item.node.master.getAddress();
|
|
182
187
|
const masterPort = item.node.master.getPort();
|
|
188
|
+
|
|
189
|
+
// see if there is a config record for this unit
|
|
183
190
|
const unitConfig = this.config.cunits.find((cu: UnitConfig) =>
|
|
184
191
|
(cu.logicalAddress === item.logicalAddress) &&
|
|
185
192
|
(cu.logicalNodeAddress === item.logicalNodeAddress) &&
|
|
186
193
|
(cu.masterAddress === masterAddress) &&
|
|
187
194
|
(cu.masterPort == masterPort));
|
|
188
|
-
|
|
195
|
+
|
|
196
|
+
// if there is: copy active, used, displayName and group
|
|
189
197
|
if (unitConfig) {
|
|
190
198
|
item.active = (unitConfig.active === "Y");
|
|
191
199
|
item.used = (unitConfig.used === "Y");
|
|
192
200
|
item.displayName = unitConfig.displayName || unitConfig.name;
|
|
193
201
|
item.group = unitConfig.group;
|
|
202
|
+
log("system", "System.setActiveState(unit) -> config name: " + unitConfig.name + ", item name: " + item.name);
|
|
194
203
|
} else {
|
|
195
204
|
item.active = false;
|
|
196
205
|
}
|
package/duotecno/types.js
CHANGED
|
@@ -273,8 +273,9 @@ exports.Sanitizers = {
|
|
|
273
273
|
return config;
|
|
274
274
|
},
|
|
275
275
|
shelly: function (config) {
|
|
276
|
-
|
|
277
|
-
|
|
276
|
+
const cfg = exports.Sanitizers.powerbase(config);
|
|
277
|
+
cfg.addresses = (config === null || config === void 0 ? void 0 : config.addresses) || "";
|
|
278
|
+
return cfg;
|
|
278
279
|
},
|
|
279
280
|
switchConfig: function (aSwitch) {
|
|
280
281
|
const sw = exports.Sanitizers.unitDef(aSwitch);
|
package/duotecno/types.ts
CHANGED
|
@@ -273,6 +273,7 @@ export interface SmappeeConfig extends PowerBaseConfig {
|
|
|
273
273
|
export interface P1Config extends PowerBaseConfig {
|
|
274
274
|
}
|
|
275
275
|
export interface ShellyConfig extends PowerBaseConfig {
|
|
276
|
+
addresses: string;
|
|
276
277
|
}
|
|
277
278
|
|
|
278
279
|
////////////////
|
|
@@ -453,9 +454,9 @@ export const Sanitizers = {
|
|
|
453
454
|
return config;
|
|
454
455
|
},
|
|
455
456
|
shelly: function(config: ShellyConfig): ShellyConfig {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
return
|
|
457
|
+
const cfg = <ShellyConfig> Sanitizers.powerbase(config);
|
|
458
|
+
cfg.addresses = config?.addresses || "";
|
|
459
|
+
return cfg;
|
|
459
460
|
},
|
|
460
461
|
|
|
461
462
|
switchConfig: function(aSwitch): Switch {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-smartsystem",
|
|
3
3
|
"displayname": "Duotecno Bridge",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.3.1",
|
|
5
5
|
"description": "SmartServer (Proxy Websockets to TCP sockets, Smappee MQTT, Duotecno IP Nodes, Homekit interface)",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"author": "Johan Coppieters",
|
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
|
@@ -72,7 +72,8 @@ class PowerBase extends base_1.Base {
|
|
|
72
72
|
//
|
|
73
73
|
updateConfig(config) {
|
|
74
74
|
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
-
|
|
75
|
+
if (config.address)
|
|
76
|
+
this.config.address = config.address;
|
|
76
77
|
this.writeConfig();
|
|
77
78
|
return this.init(false);
|
|
78
79
|
});
|
package/server/powerbase.ts
CHANGED
|
@@ -28,7 +28,7 @@ export class PowerBase extends Base {
|
|
|
28
28
|
totalExportEnergy: number, totalImportEnergy: number,
|
|
29
29
|
monitorStatus: number, utcTimeStamp: number }
|
|
30
30
|
channels: { [publishIndex: number]: {
|
|
31
|
-
name: string, type: string, flow: string,
|
|
31
|
+
name: string, type: string, flow: string, voltage: number,
|
|
32
32
|
power: number, exportEnergy: number, importEnergy: number, phaseId: number,
|
|
33
33
|
current: number, apparentPower: number, cosPhi: number, formula: string} };
|
|
34
34
|
|
|
@@ -87,7 +87,9 @@ export class PowerBase extends Base {
|
|
|
87
87
|
// config mgt
|
|
88
88
|
//
|
|
89
89
|
async updateConfig(config: any) {
|
|
90
|
-
|
|
90
|
+
if (config.address)
|
|
91
|
+
this.config.address = config.address;
|
|
92
|
+
|
|
91
93
|
this.writeConfig();
|
|
92
94
|
|
|
93
95
|
return this.init(false);
|
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
|
package/server/smappee.ts
CHANGED
|
@@ -215,6 +215,10 @@ export class Smappee extends PowerBase {
|
|
|
215
215
|
totalExportEnergy: message.totalExportEnergy, totalImportEnergy: message.totalImportEnergy,
|
|
216
216
|
monitorStatus: message.monitorStatus, utcTimeStamp: message.utcTimeStamp };
|
|
217
217
|
|
|
218
|
+
message.voltages.forEach(v => {
|
|
219
|
+
this.voltages[v.phaseId] = v.voltage;
|
|
220
|
+
});
|
|
221
|
+
|
|
218
222
|
message.channelPowers.forEach(p => {
|
|
219
223
|
const inx = p.publishIndex;
|
|
220
224
|
if (! this.channels[inx]) {
|
|
@@ -225,14 +229,13 @@ export class Smappee extends PowerBase {
|
|
|
225
229
|
this.channels[inx].exportEnergy = p.exportEnergy;
|
|
226
230
|
this.channels[inx].importEnergy = p.importEnergy;
|
|
227
231
|
this.channels[inx].phaseId = p.phaseId;
|
|
232
|
+
this.channels[inx].voltage = this.voltages[p.phaseId] || 0;
|
|
228
233
|
this.channels[inx].current = p.current;
|
|
229
234
|
this.channels[inx].apparentPower = p.apparentPower;
|
|
230
235
|
this.channels[inx].cosPhi = p.cosPhi;
|
|
231
236
|
this.channels[inx].formula = p.formula;
|
|
232
237
|
});
|
|
233
|
-
|
|
234
|
-
this.voltages[v.phaseId] = v.voltage;
|
|
235
|
-
});
|
|
238
|
+
|
|
236
239
|
|
|
237
240
|
this.calcProducAndConsumption(message);
|
|
238
241
|
}
|
package/server/smartapp.js
CHANGED
|
@@ -30,6 +30,7 @@ const api_1 = require("../duotecno/api");
|
|
|
30
30
|
const config_1 = require("../duotecno/config");
|
|
31
31
|
const kMaster = { name: "master", type: "string", default: "0.0.0.0:5001" };
|
|
32
32
|
const kAddress = { name: "address", type: "string", default: "0.0.0.0" };
|
|
33
|
+
const kAddresses = { name: "addresses", type: "string", default: "[0.0.0.0]" };
|
|
33
34
|
const kAddressOld = { name: "addressX", type: "string", default: "0.0.0.0" };
|
|
34
35
|
const kPort = { name: "port", type: "integer", default: 80 };
|
|
35
36
|
const kPortOld = { name: "portX", type: "integer", default: 80 };
|
|
@@ -328,7 +329,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
328
329
|
power.updateRule(id, this.scrapeRule(context));
|
|
329
330
|
}
|
|
330
331
|
else if (context.action === "save") {
|
|
331
|
-
power.updateConfig({ address: context.getParam(kAddress), uid: context.getParam(kUID) });
|
|
332
|
+
power.updateConfig({ address: context.getParam(kAddress), uid: context.getParam(kUID), addresses: context.getParam(kAddresses), });
|
|
332
333
|
}
|
|
333
334
|
else if (context.action === "badd") {
|
|
334
335
|
const binding = power.newBinding();
|
|
@@ -818,7 +819,6 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
818
819
|
updateNodes(master, nodes, params) {
|
|
819
820
|
return __awaiter(this, void 0, void 0, function* () {
|
|
820
821
|
if (nodes) {
|
|
821
|
-
// let nodeArr = nodes.split(",").map(s => parseInt(s));
|
|
822
822
|
let nodeArr = nodes.map(s => parseInt(s));
|
|
823
823
|
nodeArr.forEach(adr => {
|
|
824
824
|
let node = this.system.findNode(master, adr);
|
|
@@ -875,7 +875,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
875
875
|
else { // context.action === "node"
|
|
876
876
|
// reponding to /units/[master ip address:port]/[logical node address]
|
|
877
877
|
const nodeLogicalAddress = parseInt(context.id);
|
|
878
|
-
let response = yield this.getNodeInfo(master, nodeLogicalAddress);
|
|
878
|
+
let response = yield this.getNodeInfo(master, nodeLogicalAddress, true);
|
|
879
879
|
let N = this.sortCopy(response.node, context);
|
|
880
880
|
return this.ejs("nodeDetail", context, { message: response.message, node: N });
|
|
881
881
|
}
|
|
@@ -908,7 +908,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
908
908
|
this.system.updateSystem(true);
|
|
909
909
|
}
|
|
910
910
|
}
|
|
911
|
-
getNodeInfo(master, nodeLogicalAddress) {
|
|
911
|
+
getNodeInfo(master, nodeLogicalAddress, doFetch = false) {
|
|
912
912
|
return __awaiter(this, void 0, void 0, function* () {
|
|
913
913
|
let node = this.system.findNode(master, nodeLogicalAddress);
|
|
914
914
|
if (!node) {
|
|
@@ -916,7 +916,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
916
916
|
return { node: new protocol_1.Node(this.system.masters[0], { name: "No node" }), message: "Node not found" };
|
|
917
917
|
}
|
|
918
918
|
else {
|
|
919
|
-
if (node.nrUnits != node.units.length)
|
|
919
|
+
if ((node.nrUnits != node.units.length) || doFetch)
|
|
920
920
|
yield master.fetchAllUnits(node);
|
|
921
921
|
yield node.master.requestNodeStatus(node);
|
|
922
922
|
return { node };
|
package/server/smartapp.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { setToken } from "../duotecno/config";
|
|
|
26
26
|
|
|
27
27
|
const kMaster = {name: "master", type: "string", default: "0.0.0.0:5001"} as const;
|
|
28
28
|
const kAddress = {name: "address", type: "string", default: "0.0.0.0"} as const;
|
|
29
|
+
const kAddresses = {name: "addresses", type: "string", default: "[0.0.0.0]"} as const;
|
|
29
30
|
const kAddressOld = {name: "addressX", type: "string", default: "0.0.0.0"} as const;
|
|
30
31
|
const kPort = {name: "port", type: "integer", default: 80} as const;
|
|
31
32
|
const kPortOld = {name: "portX", type: "integer", default: 80} as const;
|
|
@@ -357,7 +358,7 @@ export class SmartApp extends WebApp {
|
|
|
357
358
|
power.updateRule( id, this.scrapeRule(context) );
|
|
358
359
|
|
|
359
360
|
} else if (context.action === "save") {
|
|
360
|
-
power.updateConfig({address: context.getParam(kAddress), uid: context.getParam(kUID)});
|
|
361
|
+
power.updateConfig({address: context.getParam(kAddress), uid: context.getParam(kUID), addresses: context.getParam(kAddresses), });
|
|
361
362
|
|
|
362
363
|
|
|
363
364
|
} else if (context.action === "badd") {
|
|
@@ -879,7 +880,6 @@ export class SmartApp extends WebApp {
|
|
|
879
880
|
|
|
880
881
|
async updateNodes(master: Master, nodes, params) {
|
|
881
882
|
if (nodes) {
|
|
882
|
-
// let nodeArr = nodes.split(",").map(s => parseInt(s));
|
|
883
883
|
let nodeArr = nodes.map(s => parseInt(s));
|
|
884
884
|
nodeArr.forEach(adr => {
|
|
885
885
|
let node = this.system.findNode(master, adr);
|
|
@@ -935,7 +935,7 @@ export class SmartApp extends WebApp {
|
|
|
935
935
|
} else { // context.action === "node"
|
|
936
936
|
// reponding to /units/[master ip address:port]/[logical node address]
|
|
937
937
|
const nodeLogicalAddress = parseInt(context.id);
|
|
938
|
-
let response = await this.getNodeInfo(master, nodeLogicalAddress);
|
|
938
|
+
let response = await this.getNodeInfo(master, nodeLogicalAddress, true);
|
|
939
939
|
let N = this.sortCopy(response.node, context);
|
|
940
940
|
return this.ejs("nodeDetail", context, { message: response.message, node: N });
|
|
941
941
|
}
|
|
@@ -970,14 +970,14 @@ export class SmartApp extends WebApp {
|
|
|
970
970
|
}
|
|
971
971
|
}
|
|
972
972
|
|
|
973
|
-
async getNodeInfo(master: Master, nodeLogicalAddress: number) {
|
|
973
|
+
async getNodeInfo(master: Master, nodeLogicalAddress: number, doFetch = false) {
|
|
974
974
|
let node = this.system.findNode(master, nodeLogicalAddress);
|
|
975
975
|
if (! node) {
|
|
976
976
|
// if no node found, make some dummy node for the display system. (should not occur !)
|
|
977
977
|
return { node: new Node(this.system.masters[0], {name: "No node"}), message: "Node not found" };
|
|
978
978
|
|
|
979
979
|
} else {
|
|
980
|
-
if (node.nrUnits != node.units.length)
|
|
980
|
+
if ((node.nrUnits != node.units.length) || doFetch)
|
|
981
981
|
await master.fetchAllUnits(node);
|
|
982
982
|
await node.master.requestNodeStatus(node);
|
|
983
983
|
return { node };
|
package/server/views/power.ejs
CHANGED
|
@@ -38,6 +38,14 @@
|
|
|
38
38
|
</div>
|
|
39
39
|
<% } %>
|
|
40
40
|
|
|
41
|
+
<% if (type === "shelly") { %>
|
|
42
|
+
<div class="row">
|
|
43
|
+
<div class="input-field col s12">
|
|
44
|
+
<input placeholder="xx.xx.xx.xx, ..." id="addresses" name="addresses" type="text" value="<%= config.addresses %>">
|
|
45
|
+
<label for="addresses">Addresses (komma separated)</label>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<% } %>
|
|
41
49
|
|
|
42
50
|
<h1>Rules
|
|
43
51
|
<button class="right btn waves-effect waves-light" type="submit" name="action" value="add">Add rule</button>
|
|
@@ -91,17 +99,17 @@
|
|
|
91
99
|
<h1>Info</h1>
|
|
92
100
|
<% if (realtime) { %>
|
|
93
101
|
<table>
|
|
94
|
-
<tr><th>
|
|
95
|
-
<th>
|
|
96
|
-
<th>
|
|
102
|
+
<tr><th>Total Power</th><th>Total Reactive</th>
|
|
103
|
+
<th>Total Export</th><th>Total Import</th>
|
|
104
|
+
<th>Status</th></tr>
|
|
97
105
|
<tr><td><%= realtime.totalPower %></td><td><%= realtime.totalReactivePower %></td>
|
|
98
106
|
<td><%= watt(realtime.totalExportEnergy) %>h</td><td><%= watt(realtime.totalImportEnergy) %>h</td>
|
|
99
107
|
<td><%= realtime.monitorStatus %></td></tr>
|
|
100
|
-
<tr><th>
|
|
108
|
+
<tr><th>Time</th>
|
|
101
109
|
<% for (let inx in voltages) { %>
|
|
102
110
|
<th>Phase <%= inx %></th>
|
|
103
111
|
<% } %>
|
|
104
|
-
<th>
|
|
112
|
+
<th>Poll Counter</th>
|
|
105
113
|
</tr>
|
|
106
114
|
<tr><td><%= datetime(new Date(realtime.utcTimeStamp)) %></td>
|
|
107
115
|
<% for (let inx in voltages) { %>
|
|
@@ -114,16 +122,17 @@
|
|
|
114
122
|
|
|
115
123
|
<h1>Channels</h1>
|
|
116
124
|
<table>
|
|
117
|
-
<tr><th>
|
|
118
|
-
<th>
|
|
119
|
-
<th>
|
|
120
|
-
<th>cosPhi</th><th>
|
|
125
|
+
<tr><th>Index</th><th>Name</th>
|
|
126
|
+
<th>Power</th><th>Total Export</th><th>Total Import</th>
|
|
127
|
+
<th>Phase</th><th>Current</th><th>Voltage</th><th>Apparent Power</th>
|
|
128
|
+
<th>cosPhi</th><th>Type</th><th>Flow</th><th>Formula</th></tr>
|
|
121
129
|
<% for (let inx in channels) { const ch = channels[inx]; %>
|
|
122
130
|
<tr><td><%= inx %></td>
|
|
123
131
|
<td><%= ch.name %></td>
|
|
124
|
-
<td><%= ch.power %></td>
|
|
132
|
+
<td><%= watt(ch.power) %></td>
|
|
125
133
|
<td><%= watt(ch.exportEnergy) %></td><td><%= watt(ch.importEnergy) %></td>
|
|
126
|
-
<td><%= ch.phaseId %></td><td><%= ch.current
|
|
134
|
+
<td><%= (ch.phaseId >= 0) ? ch.phaseId : "-" %></td><td><%= Math.round(ch.current*100)/100 %>A</td>
|
|
135
|
+
<td><%= ch.voltage %>V</td>
|
|
127
136
|
<td><%= ch.apparentPower %></td><td><%= ch.cosPhi %></td>
|
|
128
137
|
<td><%= ch.type %></td><td><%= ch.flow %></td><td><%= ch.formula %></td>
|
|
129
138
|
</tr>
|