homebridge-smartsystem 6.1.7 → 6.2.2
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 +14 -0
- package/accessories/accessory.js +4 -4
- package/accessories/accessory.ts +1 -2
- package/accessories/dimmer.js +6 -6
- package/accessories/dimmer.ts +1 -1
- package/accessories/door.js +2 -2
- package/accessories/door.ts +1 -1
- package/accessories/garagedoor.js +5 -4
- package/accessories/garagedoor.ts +2 -1
- package/accessories/lock.js +6 -5
- package/accessories/lock.ts +2 -1
- package/accessories/mood.js +2 -1
- package/accessories/mood.ts +2 -1
- package/accessories/temperature.js +2 -2
- package/accessories/temperature.ts +1 -1
- package/accessories/windowcovering.js +8 -7
- package/accessories/windowcovering.ts +2 -1
- package/config.json +169 -228
- package/config.smappee.json +178 -2
- package/config.smartapp.json +63 -0
- package/duotecno/Q.js +8 -8
- package/duotecno/Q.ts +1 -1
- package/duotecno/api.js +3 -3
- package/duotecno/api.ts +2 -2
- package/duotecno/config.js +24 -23
- package/duotecno/config.ts +23 -16
- package/duotecno/logger.js +51 -0
- package/duotecno/logger.ts +41 -0
- package/duotecno/master.js +4 -6
- package/duotecno/master.ts +2 -4
- package/duotecno/protocol.js +10 -9
- package/duotecno/protocol.ts +2 -1
- package/duotecno/system.js +17 -41
- package/duotecno/system.ts +7 -34
- package/duotecno/types.js +58 -131
- package/duotecno/types.ts +65 -125
- package/index.spec.js +22 -36
- package/index.spec.ts +20 -33
- package/package.json +2 -2
- package/server/base.js +10 -38
- package/server/base.ts +9 -42
- package/server/p1.js +9 -8
- package/server/p1.ts +6 -4
- package/server/platform.js +66 -36
- package/server/platform.ts +47 -12
- package/server/powerbase.js +15 -55
- package/server/powerbase.ts +4 -50
- package/server/shelly.js +4 -4
- package/server/shelly.ts +3 -2
- package/server/smappee.js +9 -8
- package/server/smappee.ts +4 -3
- package/server/smartapp.js +94 -39
- package/server/smartapp.ts +73 -20
- package/server/socapp.js +6 -6
- package/server/socapp.ts +2 -1
- package/server/somfy.js +12 -12
- package/server/somfy.ts +1 -1
- package/server/support.js +13 -13
- package/server/support.ts +4 -4
- package/server/views/style.ejs +1 -0
- package/server/views/switch-details.ejs +15 -5
- package/server/webapp.js +45 -47
- package/server/webapp.ts +29 -38
- package/config.groups.json +0 -8
- package/config.p1-dev.json +0 -59
- package/config.scenes.json +0 -15
package/server/smartapp.js
CHANGED
|
@@ -21,10 +21,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
exports.SmartApp = void 0;
|
|
22
22
|
const webapp_1 = require("./webapp");
|
|
23
23
|
const types_1 = require("../duotecno/types");
|
|
24
|
+
const logger_1 = require("../duotecno/logger");
|
|
24
25
|
const protocol_1 = require("../duotecno/protocol");
|
|
25
26
|
const http = require("http");
|
|
26
27
|
const somfy = require("./somfy");
|
|
27
28
|
const fs_1 = require("fs");
|
|
29
|
+
const api_1 = require("../duotecno/api");
|
|
30
|
+
const config_1 = require("../duotecno/config");
|
|
28
31
|
const kMaster = { name: "master", type: "string", default: "0.0.0.0:5001" };
|
|
29
32
|
const kAddress = { name: "address", type: "string", default: "0.0.0.0" };
|
|
30
33
|
const kAddressOld = { name: "addressX", type: "string", default: "0.0.0.0" };
|
|
@@ -46,12 +49,12 @@ var ContentType;
|
|
|
46
49
|
(function (ContentType) {
|
|
47
50
|
ContentType[ContentType["form"] = 0] = "form";
|
|
48
51
|
ContentType[ContentType["plain"] = 1] = "plain";
|
|
52
|
+
ContentType[ContentType["none"] = 2] = "none";
|
|
49
53
|
})(ContentType || (ContentType = {}));
|
|
50
54
|
;
|
|
51
55
|
class SmartApp extends webapp_1.WebApp {
|
|
52
|
-
constructor(system, power, platform
|
|
53
|
-
super("smartapp"
|
|
54
|
-
// this.readConfig();
|
|
56
|
+
constructor(system, power, platform) {
|
|
57
|
+
super("smartapp");
|
|
55
58
|
this.system = system;
|
|
56
59
|
// get status change updates
|
|
57
60
|
this.system.emitter.on('update', this.informChange.bind(this));
|
|
@@ -70,6 +73,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
70
73
|
this.addFile("nodeDetail", __dirname + "/views/node-details.ejs", "text/html");
|
|
71
74
|
this.addFile("serviceList", __dirname + "/views/service-list.ejs", "text/html");
|
|
72
75
|
this.addFile("power", __dirname + "/views/power.ejs", "text/html");
|
|
76
|
+
this.addFile("login", __dirname + "/views/login.ejs", "text/html");
|
|
73
77
|
this.addFile("settings", __dirname + "/views/settings.ejs", "text/html");
|
|
74
78
|
this.addFile("switchDetail", __dirname + "/views/switch-details.ejs", "text/html");
|
|
75
79
|
this.addFile("switchList", __dirname + "/views/switch-list.ejs", "text/html");
|
|
@@ -95,7 +99,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
95
99
|
context["notReadyMessage"] = "=== waiting >> found " +
|
|
96
100
|
this.system.allActiveUnits().length + " units out of " + this.system.activeUnitsConfig().length + " selected after " +
|
|
97
101
|
this.platform.startWaiting + " sec ===";
|
|
98
|
-
(0,
|
|
102
|
+
(0, logger_1.log)("smartapp", "Platform not ready -> " + context["notReadyMessage"]);
|
|
99
103
|
}
|
|
100
104
|
else {
|
|
101
105
|
context["notReady"] = false;
|
|
@@ -105,12 +109,35 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
105
109
|
//////////////
|
|
106
110
|
// password //
|
|
107
111
|
//////////////
|
|
108
|
-
pwOK(context, pw) {
|
|
109
|
-
return (
|
|
112
|
+
pwOK(context, user, pw) {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
this.token = "";
|
|
115
|
+
(0, config_1.setToken)("");
|
|
116
|
+
try {
|
|
117
|
+
const body = { username: user, password: pw };
|
|
118
|
+
const header = { "Content-Type": "application/json" };
|
|
119
|
+
const resp = yield (0, api_1.post)(this.config.apiHost, this.config.apiPort, "/api/auth/login", body, header);
|
|
120
|
+
const respBody = JSON.parse(resp);
|
|
121
|
+
if (respBody.access_token) {
|
|
122
|
+
this.token = respBody.access_token;
|
|
123
|
+
(0, config_1.setToken)(this.token);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
(0, logger_1.debug)("smartapp", "Login API failed " + JSON.stringify(e));
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
});
|
|
110
132
|
}
|
|
111
133
|
//////////////////////////////
|
|
112
134
|
// Router //
|
|
113
135
|
//////////////////////////////
|
|
136
|
+
needsLogin(context) {
|
|
137
|
+
const noNeed = ((context.request === "units") || (context.request === "switches")) &&
|
|
138
|
+
((context.action == "press") || (context.action == "get") || (context.action == "set"));
|
|
139
|
+
return super.needsLogin(context) && (context.request != "files") && (!noNeed);
|
|
140
|
+
}
|
|
114
141
|
doRequest(context) {
|
|
115
142
|
const _super = Object.create(null, {
|
|
116
143
|
doRequest: { get: () => super.doRequest }
|
|
@@ -356,7 +383,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
356
383
|
// Switches //
|
|
357
384
|
//////////////
|
|
358
385
|
initSwitchUnits() {
|
|
359
|
-
(0,
|
|
386
|
+
(0, logger_1.log)("smartapp", "Init " + this.switches.length + " Switches -> add units");
|
|
360
387
|
const smappee = this.power.smappee;
|
|
361
388
|
this.switches.forEach(swtch => {
|
|
362
389
|
swtch.unit = swtch.unit ||
|
|
@@ -379,17 +406,17 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
379
406
|
swtch.status = swtch.unit.status;
|
|
380
407
|
}
|
|
381
408
|
else {
|
|
382
|
-
(0,
|
|
409
|
+
(0, logger_1.log)("smartapp", "** error ** missing unit: " + (0, types_1.hex)(swtch.logicalNodeAddress) + "/" + (0, types_1.hex)(swtch.logicalAddress) + " **");
|
|
383
410
|
}
|
|
384
411
|
}
|
|
385
412
|
});
|
|
386
413
|
}
|
|
387
414
|
alertSwitch(type, plugNr, value) {
|
|
388
|
-
(0,
|
|
415
|
+
(0, logger_1.debug)("smartapp", "Received " + type + " switch status change: " + plugNr + " -> " + value);
|
|
389
416
|
if ((this.power.smappee) && (type === types_1.SwitchType.kSmappee)) {
|
|
390
417
|
this.switches.forEach(swtch => {
|
|
391
418
|
if ((swtch.type == types_1.SwitchType.kSmappee) && (swtch.plug == plugNr) && swtch.unit) {
|
|
392
|
-
(0,
|
|
419
|
+
(0, logger_1.debug)("smartapp", " -> Switch was attached to unit = " + swtch.unit.getDisplayName() + " -> setting state to " + value);
|
|
393
420
|
swtch.unit.setState(value);
|
|
394
421
|
}
|
|
395
422
|
});
|
|
@@ -440,7 +467,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
440
467
|
if (!((kind === "M") && (swtch.nomacro === "Y")))
|
|
441
468
|
this.setSwitch(swtch);
|
|
442
469
|
else
|
|
443
|
-
(0,
|
|
470
|
+
(0, logger_1.debug)("smartapp", "*** macro changed blocked by switch");
|
|
444
471
|
}
|
|
445
472
|
});
|
|
446
473
|
}
|
|
@@ -452,8 +479,9 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
452
479
|
const stype = context.getParam({ name: "type", type: "string", default: types_1.SwitchType.kNoType });
|
|
453
480
|
const name = context.getParam({ name: "name", type: "string", default: "--" });
|
|
454
481
|
const data = context.getParam({ name: "data", type: "string", default: "" });
|
|
482
|
+
const header = context.getParam({ name: "header", type: "string", default: "" });
|
|
455
483
|
const method = context.getParam({ name: "method", type: "string", default: "GET" });
|
|
456
|
-
return { name, unitName, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype, plug, data, method, nomacro, nostop };
|
|
484
|
+
return { name, unitName, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype, plug, data, header, method, nomacro, nostop };
|
|
457
485
|
}
|
|
458
486
|
updateSwitch(inx, swtch) {
|
|
459
487
|
if ((inx >= 0) && (inx < this.switches.length)) {
|
|
@@ -489,10 +517,10 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
489
517
|
swtch.unit.value = +newvalue;
|
|
490
518
|
}
|
|
491
519
|
if (!swtch) {
|
|
492
|
-
(0,
|
|
520
|
+
(0, logger_1.err)("smartapp", "Didn't find switch with inx: " + inx);
|
|
493
521
|
}
|
|
494
522
|
else if (!swtch.unit) {
|
|
495
|
-
(0,
|
|
523
|
+
(0, logger_1.err)("smartapp", "Don't have unit for switch: " + swtch.unitname);
|
|
496
524
|
}
|
|
497
525
|
else {
|
|
498
526
|
if ((swtch.type === types_1.SwitchType.kSmappee) && (this.power.smappee)) {
|
|
@@ -520,7 +548,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
520
548
|
this.somfy(swtch);
|
|
521
549
|
}
|
|
522
550
|
else {
|
|
523
|
-
(0,
|
|
551
|
+
(0, logger_1.err)("smartapp", "Don't know how to set a switch of type " + swtch.type);
|
|
524
552
|
}
|
|
525
553
|
}
|
|
526
554
|
}
|
|
@@ -565,8 +593,13 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
565
593
|
}
|
|
566
594
|
httpSwitch(swtch) {
|
|
567
595
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
568
|
-
|
|
569
|
-
|
|
596
|
+
let data = "";
|
|
597
|
+
if (swtch.data) {
|
|
598
|
+
// we have body data
|
|
599
|
+
data = this.makeVariableURL(swtch.data, !!swtch.unit.status, +swtch.unit.value);
|
|
600
|
+
}
|
|
601
|
+
(0, logger_1.log)("smartapp", "HTTP-Switch(" + !!swtch.unit.status + ") -> " + req);
|
|
602
|
+
this.wrequest(req, swtch.method, data, ContentType.form, swtch.header);
|
|
570
603
|
}
|
|
571
604
|
httpUpDown(swtch) {
|
|
572
605
|
let url = swtch.plug + "";
|
|
@@ -577,12 +610,17 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
577
610
|
if (val < parts.length) {
|
|
578
611
|
base += parts[val];
|
|
579
612
|
}
|
|
613
|
+
let data = "";
|
|
614
|
+
if (swtch.data) {
|
|
615
|
+
// we have body data
|
|
616
|
+
data = this.makeVariableURL(swtch.data, !!swtch.unit.status, +swtch.unit.value);
|
|
617
|
+
}
|
|
580
618
|
if ((val === 1) && (swtch.nostop == 'Y')) {
|
|
581
|
-
(0,
|
|
619
|
+
(0, logger_1.debug)("smartapp", "Blocked stop for HTTP-UpDown " + swtch.name);
|
|
582
620
|
}
|
|
583
621
|
else {
|
|
584
|
-
(0,
|
|
585
|
-
this.wrequest(base);
|
|
622
|
+
(0, logger_1.log)("smartapp", "UpDown(" + val + ") -> " + base);
|
|
623
|
+
this.wrequest(base, swtch.method, data, ContentType.form, swtch.header);
|
|
586
624
|
}
|
|
587
625
|
}
|
|
588
626
|
httpDimmer(swtch) {
|
|
@@ -593,28 +631,45 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
593
631
|
// we have body data
|
|
594
632
|
data = this.makeVariableURL(swtch.data, !!swtch.unit.status, +swtch.unit.value);
|
|
595
633
|
}
|
|
596
|
-
(0,
|
|
597
|
-
this.wrequest(req, swtch.method, data);
|
|
634
|
+
(0, logger_1.debug)("smartapp", "Dimmer(" + !!swtch.unit.status + "," + swtch.unit.value + ") -> " + req + " + " + data);
|
|
635
|
+
this.wrequest(req, swtch.method, data, ContentType.form, swtch.header);
|
|
598
636
|
}
|
|
599
|
-
wrequest(url, method = "GET", formdata, contentType = ContentType.form) {
|
|
637
|
+
wrequest(url, method = "GET", formdata = "", contentType = ContentType.form, header = "") {
|
|
600
638
|
try {
|
|
601
639
|
// const data = querystring.stringify(formdata);
|
|
602
640
|
const options = { method };
|
|
603
641
|
if (formdata) {
|
|
604
642
|
if (contentType === ContentType.form) {
|
|
605
|
-
options
|
|
643
|
+
options.headers = {
|
|
606
644
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
607
645
|
'Content-Length': Buffer.byteLength(formdata)
|
|
608
646
|
};
|
|
609
647
|
}
|
|
610
648
|
else if (contentType === ContentType.plain) {
|
|
611
|
-
options
|
|
649
|
+
options.headers = {
|
|
612
650
|
'Content-Type': 'text/plain',
|
|
613
651
|
'Accept': 'application/json'
|
|
614
652
|
};
|
|
615
653
|
}
|
|
616
654
|
}
|
|
617
655
|
;
|
|
656
|
+
if (header) {
|
|
657
|
+
if (!options.headers)
|
|
658
|
+
options.headers = {};
|
|
659
|
+
if (header.substring(0, 1) === "{") {
|
|
660
|
+
try {
|
|
661
|
+
options.headers = Object.assign(Object.assign({}, options.headers), JSON.parse(header));
|
|
662
|
+
}
|
|
663
|
+
catch (e) {
|
|
664
|
+
(0, logger_1.err)("smartapp", "error parsing :" + header + " -> " + JSON.stringify(e));
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
const s = header.split(":");
|
|
669
|
+
options.headers[s[0]] = s[1];
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
(0, logger_1.debug)("smartapp", "Sending to " + url + " with options: " + JSON.stringify(options));
|
|
618
673
|
const req = http.request(url, options, res => {
|
|
619
674
|
let resp = "";
|
|
620
675
|
// res.setEncoding('utf8');
|
|
@@ -627,10 +682,10 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
627
682
|
const x = JSON.parse(resp);
|
|
628
683
|
if ((x.type === "Buffer") && x.data)
|
|
629
684
|
x.data = x.data.toString();
|
|
630
|
-
(0,
|
|
685
|
+
(0, logger_1.debug)("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + JSON.stringify(x));
|
|
631
686
|
}
|
|
632
687
|
catch (e) {
|
|
633
|
-
(0,
|
|
688
|
+
(0, logger_1.debug)("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + resp);
|
|
634
689
|
}
|
|
635
690
|
;
|
|
636
691
|
});
|
|
@@ -639,15 +694,15 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
639
694
|
req.write(formdata);
|
|
640
695
|
}
|
|
641
696
|
req.on("error", (e) => {
|
|
642
|
-
(0,
|
|
697
|
+
(0, logger_1.log)("smartapp", "The request to " + url + ", error: " + e.message);
|
|
643
698
|
});
|
|
644
699
|
req.on("timeout", (e) => {
|
|
645
|
-
(0,
|
|
700
|
+
(0, logger_1.log)("smartapp", "The request to " + url + ", timeout: " + e.message);
|
|
646
701
|
});
|
|
647
702
|
req.end();
|
|
648
703
|
}
|
|
649
704
|
catch (e) {
|
|
650
|
-
(0,
|
|
705
|
+
(0, logger_1.log)("smartapp", "** http error ** " + e.message + " **");
|
|
651
706
|
}
|
|
652
707
|
}
|
|
653
708
|
//////////////
|
|
@@ -655,21 +710,21 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
655
710
|
//////////////
|
|
656
711
|
ohSwitch(swtch) {
|
|
657
712
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
658
|
-
(0,
|
|
659
|
-
this.wrequest(req, swtch.method, swtch.unit.status ? "ON" : "OFF", ContentType.plain);
|
|
713
|
+
(0, logger_1.debug)("smartapp", "OH-Switch(" + !!swtch.unit.status + ") -> " + req);
|
|
714
|
+
this.wrequest(req, swtch.method, swtch.unit.status ? "ON" : "OFF", ContentType.plain, swtch.header);
|
|
660
715
|
}
|
|
661
716
|
ohDimmer(swtch) {
|
|
662
717
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
663
718
|
const val = (swtch.unit.value == 1) ? "ON" : swtch.unit.value.toString();
|
|
664
|
-
(0,
|
|
665
|
-
this.wrequest(req, swtch.method, swtch.unit.status ? val : "OFF", ContentType.plain);
|
|
719
|
+
(0, logger_1.debug)("smartapp", "OH-Dimmer(" + !!swtch.unit.status + ", " + val + ") -> " + req);
|
|
720
|
+
this.wrequest(req, swtch.method, swtch.unit.status ? val : "OFF", ContentType.plain, swtch.header);
|
|
666
721
|
}
|
|
667
722
|
ohUpDown(swtch) {
|
|
668
723
|
let data;
|
|
669
724
|
// 1=stopped, 2-closed, 3=opened, 4=closing, 5=opening
|
|
670
725
|
if ((swtch.unit.status == 0) || (swtch.unit.status == 1) || (swtch.unit.status == 2)) {
|
|
671
726
|
if (swtch.nostop == 'Y')
|
|
672
|
-
(0,
|
|
727
|
+
(0, logger_1.debug)("smartapp", "Blocked stop for OH-UpDown " + swtch.name);
|
|
673
728
|
else
|
|
674
729
|
data = "STOP";
|
|
675
730
|
}
|
|
@@ -681,8 +736,8 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
681
736
|
}
|
|
682
737
|
if (data) {
|
|
683
738
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
684
|
-
(0,
|
|
685
|
-
this.wrequest(req, swtch.method, data, ContentType.plain);
|
|
739
|
+
(0, logger_1.debug)("smartapp", "OH-UpDown(" + swtch.unit.value + ", " + data + ") -> " + req);
|
|
740
|
+
this.wrequest(req, swtch.method, data, ContentType.plain, swtch.header);
|
|
686
741
|
}
|
|
687
742
|
}
|
|
688
743
|
//////////////////////////////
|
|
@@ -894,7 +949,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
894
949
|
}
|
|
895
950
|
setState(master, nodeLogicalAddress, unitLogicalAddress, value) {
|
|
896
951
|
return __awaiter(this, void 0, void 0, function* () {
|
|
897
|
-
(0,
|
|
952
|
+
(0, logger_1.log)("smartapp", "setState requested with node = " + nodeLogicalAddress + ", unit = " + unitLogicalAddress + " -> " + value);
|
|
898
953
|
if ((value === "Y") || (value === "N"))
|
|
899
954
|
value = (value === "Y");
|
|
900
955
|
else {
|
|
@@ -911,7 +966,7 @@ class SmartApp extends webapp_1.WebApp {
|
|
|
911
966
|
}
|
|
912
967
|
getState(master, nodeLogicalAddress, unitLogicalAddress) {
|
|
913
968
|
return __awaiter(this, void 0, void 0, function* () {
|
|
914
|
-
(0,
|
|
969
|
+
(0, logger_1.log)("smartapp", "getState requested with node = " + nodeLogicalAddress + ", unit = " + unitLogicalAddress);
|
|
915
970
|
let unit = this.system.findUnit(master, nodeLogicalAddress, unitLogicalAddress);
|
|
916
971
|
if (!unit)
|
|
917
972
|
return { message: "Unit not found " + master.getName() + "/" + nodeLogicalAddress + "/" + unitLogicalAddress };
|
package/server/smartapp.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
// v2.0: mar/apr 2020
|
|
10
10
|
|
|
11
11
|
import { Context, HttpResponse, WebApp } from "./webapp";
|
|
12
|
-
import {
|
|
12
|
+
import { Sanitizers, Rule, kEmptyRule, actionValue, Action, kEmptySwitch, Switch, SwitchType, hex, Binding, NetworkConfig, SettingsConfig, kEmptySettings } from "../duotecno/types";
|
|
13
|
+
import { log, debug, err } from "../duotecno/logger";
|
|
13
14
|
import { System } from "../duotecno/system";
|
|
14
15
|
import { Node, Unit } from "../duotecno/protocol";
|
|
15
16
|
import { Smappee } from "./smappee";
|
|
@@ -20,6 +21,8 @@ import * as somfy from "./somfy";
|
|
|
20
21
|
import { existsSync, truncateSync, writeFileSync } from 'fs';
|
|
21
22
|
import { Shelly } from './shelly';
|
|
22
23
|
import { P1 } from './p1';
|
|
24
|
+
import { post } from "../duotecno/api";
|
|
25
|
+
import { getConfig, setAPIConfig, setToken } from "../duotecno/config";
|
|
23
26
|
|
|
24
27
|
const kMaster = {name: "master", type: "string", default: "0.0.0.0:5001"} as const;
|
|
25
28
|
const kAddress = {name: "address", type: "string", default: "0.0.0.0"} as const;
|
|
@@ -42,7 +45,7 @@ const isMac = existsSync("/Volumes");
|
|
|
42
45
|
const kDHCPConfigFile = isMac ? "./config.dhcpcd" : "/etc/dhcpcd.conf";
|
|
43
46
|
|
|
44
47
|
|
|
45
|
-
enum ContentType {form, plain};
|
|
48
|
+
enum ContentType {form, plain, none};
|
|
46
49
|
|
|
47
50
|
export interface Power {
|
|
48
51
|
smappee?: Smappee;
|
|
@@ -57,10 +60,8 @@ export class SmartApp extends WebApp {
|
|
|
57
60
|
switches: Array<Switch>;
|
|
58
61
|
|
|
59
62
|
|
|
60
|
-
constructor(system: System, power: Power, platform: Platform
|
|
61
|
-
super("smartapp"
|
|
62
|
-
|
|
63
|
-
// this.readConfig();
|
|
63
|
+
constructor(system: System, power: Power, platform: Platform) {
|
|
64
|
+
super("smartapp");
|
|
64
65
|
this.system = system;
|
|
65
66
|
|
|
66
67
|
// get status change updates
|
|
@@ -84,6 +85,7 @@ export class SmartApp extends WebApp {
|
|
|
84
85
|
this.addFile("nodeDetail", __dirname + "/views/node-details.ejs", "text/html");
|
|
85
86
|
this.addFile("serviceList", __dirname + "/views/service-list.ejs", "text/html");
|
|
86
87
|
this.addFile("power", __dirname + "/views/power.ejs", "text/html");
|
|
88
|
+
this.addFile("login", __dirname + "/views/login.ejs", "text/html");
|
|
87
89
|
this.addFile("settings", __dirname + "/views/settings.ejs", "text/html");
|
|
88
90
|
this.addFile("switchDetail", __dirname + "/views/switch-details.ejs", "text/html");
|
|
89
91
|
this.addFile("switchList", __dirname + "/views/switch-list.ejs", "text/html");
|
|
@@ -101,6 +103,7 @@ export class SmartApp extends WebApp {
|
|
|
101
103
|
super.writeConfig();
|
|
102
104
|
}
|
|
103
105
|
|
|
106
|
+
|
|
104
107
|
readConfig() {
|
|
105
108
|
super.readConfig();
|
|
106
109
|
|
|
@@ -108,6 +111,7 @@ export class SmartApp extends WebApp {
|
|
|
108
111
|
this.switches = this.config.switches.map(s => Sanitizers.switchConfig(s));
|
|
109
112
|
}
|
|
110
113
|
|
|
114
|
+
|
|
111
115
|
checkReady(context: Context) {
|
|
112
116
|
if (this.platform && !this.platform.ready) {
|
|
113
117
|
context["notReady"] = true;
|
|
@@ -124,14 +128,35 @@ export class SmartApp extends WebApp {
|
|
|
124
128
|
//////////////
|
|
125
129
|
// password //
|
|
126
130
|
//////////////
|
|
127
|
-
pwOK(context: Context, pw: string): boolean {
|
|
128
|
-
|
|
131
|
+
async pwOK(context: Context, user: string, pw: string): Promise<boolean> {
|
|
132
|
+
this.token = "";
|
|
133
|
+
setToken("");
|
|
134
|
+
try {
|
|
135
|
+
const body = { username : user, password: pw };
|
|
136
|
+
const header = {"Content-Type": "application/json"};
|
|
137
|
+
const resp = await post(this.config.apiHost, this.config.apiPort, "/api/auth/login", body, header);
|
|
138
|
+
const respBody = JSON.parse(resp);
|
|
139
|
+
if (respBody.access_token) {
|
|
140
|
+
this.token = respBody.access_token;
|
|
141
|
+
setToken(this.token);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
} catch(e) {
|
|
145
|
+
debug("smartapp", "Login API failed " + JSON.stringify(e));
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
129
148
|
}
|
|
130
149
|
|
|
131
150
|
//////////////////////////////
|
|
132
151
|
// Router //
|
|
133
152
|
//////////////////////////////
|
|
134
153
|
|
|
154
|
+
needsLogin(context: Context): boolean {
|
|
155
|
+
const noNeed = ((context.request === "units") || (context.request === "switches")) &&
|
|
156
|
+
((context.action == "press") || (context.action == "get") || (context.action == "set"));
|
|
157
|
+
return super.needsLogin(context) && (context.request != "files") && (!noNeed);
|
|
158
|
+
}
|
|
159
|
+
|
|
135
160
|
async doRequest(context: Context): Promise<HttpResponse> {
|
|
136
161
|
this.checkReady(context);
|
|
137
162
|
|
|
@@ -230,7 +255,7 @@ export class SmartApp extends WebApp {
|
|
|
230
255
|
config = this.scrapeSettings(context);
|
|
231
256
|
context.request = "install";
|
|
232
257
|
this.write("settings", config);
|
|
233
|
-
this.writeDHCP(context, config);
|
|
258
|
+
this.writeDHCP(context, <SettingsConfig>config);
|
|
234
259
|
|
|
235
260
|
} else if (context.action === "reset") {
|
|
236
261
|
config = { ...kEmptySettings };
|
|
@@ -499,8 +524,9 @@ export class SmartApp extends WebApp {
|
|
|
499
524
|
const stype = context.getParam({name: "type", type: "string", default: SwitchType.kNoType });
|
|
500
525
|
const name = context.getParam({name: "name", type: "string", default: "--" });
|
|
501
526
|
const data = context.getParam({name: "data", type: "string", default: "" });
|
|
527
|
+
const header = context.getParam({name: "header", type: "string", default: "" });
|
|
502
528
|
const method = context.getParam({name: "method", type: "string", default: "GET" });
|
|
503
|
-
return { name, unitName, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype, plug, data, method, nomacro, nostop };
|
|
529
|
+
return { name, unitName, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype, plug, data, header, method, nomacro, nostop };
|
|
504
530
|
}
|
|
505
531
|
|
|
506
532
|
updateSwitch(inx: number, swtch: Switch) {
|
|
@@ -621,8 +647,15 @@ export class SmartApp extends WebApp {
|
|
|
621
647
|
|
|
622
648
|
httpSwitch(swtch: Switch) {
|
|
623
649
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
650
|
+
|
|
651
|
+
let data = "";
|
|
652
|
+
if (swtch.data) {
|
|
653
|
+
// we have body data
|
|
654
|
+
data = this.makeVariableURL(swtch.data, !!swtch.unit.status, +swtch.unit.value);
|
|
655
|
+
}
|
|
656
|
+
|
|
624
657
|
log("smartapp", "HTTP-Switch(" + !!swtch.unit.status + ") -> " + req);
|
|
625
|
-
this.wrequest(req);
|
|
658
|
+
this.wrequest(req, swtch.method, data, ContentType.form, swtch.header);
|
|
626
659
|
}
|
|
627
660
|
|
|
628
661
|
httpUpDown(swtch: Switch) {
|
|
@@ -635,12 +668,18 @@ export class SmartApp extends WebApp {
|
|
|
635
668
|
base += parts[val];
|
|
636
669
|
}
|
|
637
670
|
|
|
671
|
+
let data = "";
|
|
672
|
+
if (swtch.data) {
|
|
673
|
+
// we have body data
|
|
674
|
+
data = this.makeVariableURL(swtch.data, !!swtch.unit.status, +swtch.unit.value);
|
|
675
|
+
}
|
|
676
|
+
|
|
638
677
|
if ((val === 1) && (swtch.nostop == 'Y')) {
|
|
639
678
|
debug("smartapp", "Blocked stop for HTTP-UpDown " + swtch.name);
|
|
640
679
|
|
|
641
680
|
} else {
|
|
642
681
|
log("smartapp", "UpDown(" + val + ") -> " + base);
|
|
643
|
-
this.wrequest(base);
|
|
682
|
+
this.wrequest(base, swtch.method, data, ContentType.form, swtch.header);
|
|
644
683
|
}
|
|
645
684
|
}
|
|
646
685
|
|
|
@@ -654,29 +693,43 @@ export class SmartApp extends WebApp {
|
|
|
654
693
|
data = this.makeVariableURL(swtch.data, !!swtch.unit.status, +swtch.unit.value);
|
|
655
694
|
}
|
|
656
695
|
debug("smartapp", "Dimmer(" + !!swtch.unit.status + "," + swtch.unit.value + ") -> " + req + " + " + data);
|
|
657
|
-
this.wrequest(req, swtch.method, data);
|
|
696
|
+
this.wrequest(req, swtch.method, data, ContentType.form, swtch.header);
|
|
658
697
|
}
|
|
659
698
|
|
|
660
699
|
|
|
661
|
-
wrequest(url: string, method = "GET", formdata
|
|
700
|
+
wrequest(url: string, method = "GET", formdata = "", contentType = ContentType.form, header = "") {
|
|
662
701
|
try {
|
|
663
702
|
// const data = querystring.stringify(formdata);
|
|
664
|
-
const options = { method };
|
|
703
|
+
const options: {method: string, [key: string]: object | string} = { method };
|
|
665
704
|
|
|
666
705
|
if (formdata) {
|
|
667
706
|
if (contentType === ContentType.form) {
|
|
668
|
-
options
|
|
707
|
+
options.headers = {
|
|
669
708
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
670
709
|
'Content-Length': Buffer.byteLength(formdata)
|
|
671
710
|
}
|
|
672
711
|
} else if (contentType === ContentType.plain) {
|
|
673
|
-
options
|
|
712
|
+
options.headers = {
|
|
674
713
|
'Content-Type': 'text/plain',
|
|
675
714
|
'Accept': 'application/json'
|
|
676
715
|
};
|
|
677
716
|
}
|
|
678
717
|
};
|
|
718
|
+
if (header) {
|
|
719
|
+
if (!options.headers) options.headers = {};
|
|
720
|
+
if (header.substring(0,1) === "{") {
|
|
721
|
+
try {
|
|
722
|
+
options.headers = { ...<object>options.headers, ...JSON.parse(header)};
|
|
723
|
+
} catch(e) {
|
|
724
|
+
err("smartapp", "error parsing :" + header + " -> " + JSON.stringify(e));
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
const s = header.split(":");
|
|
728
|
+
options.headers[s[0]] = s[1];
|
|
729
|
+
}
|
|
730
|
+
}
|
|
679
731
|
|
|
732
|
+
debug("smartapp", "Sending to " + url + " with options: " + JSON.stringify(options));
|
|
680
733
|
const req = http.request(url, options, res => {
|
|
681
734
|
let resp = "";
|
|
682
735
|
// res.setEncoding('utf8');
|
|
@@ -717,13 +770,13 @@ export class SmartApp extends WebApp {
|
|
|
717
770
|
ohSwitch(swtch: Switch) {
|
|
718
771
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
719
772
|
debug("smartapp", "OH-Switch(" + !!swtch.unit.status + ") -> " + req);
|
|
720
|
-
this.wrequest(req, swtch.method, swtch.unit.status ? "ON" : "OFF", ContentType.plain);
|
|
773
|
+
this.wrequest(req, swtch.method, swtch.unit.status ? "ON" : "OFF", ContentType.plain, swtch.header);
|
|
721
774
|
}
|
|
722
775
|
ohDimmer(swtch: Switch) {
|
|
723
776
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
724
777
|
const val = (swtch.unit.value == 1) ? "ON" : swtch.unit.value.toString();
|
|
725
778
|
debug("smartapp", "OH-Dimmer(" + !!swtch.unit.status + ", " + val + ") -> " + req);
|
|
726
|
-
this.wrequest(req, swtch.method, swtch.unit.status ? val : "OFF", ContentType.plain);
|
|
779
|
+
this.wrequest(req, swtch.method, swtch.unit.status ? val : "OFF", ContentType.plain, swtch.header);
|
|
727
780
|
}
|
|
728
781
|
ohUpDown(swtch: Switch) {
|
|
729
782
|
let data;
|
|
@@ -745,7 +798,7 @@ export class SmartApp extends WebApp {
|
|
|
745
798
|
if (data) {
|
|
746
799
|
const req = this.makeVariableURL(swtch.plug, !!swtch.unit.status, +swtch.unit.value);
|
|
747
800
|
debug("smartapp", "OH-UpDown(" + <number>swtch.unit.value +", " + data + ") -> " + req);
|
|
748
|
-
this.wrequest(req, swtch.method, data, ContentType.plain);
|
|
801
|
+
this.wrequest(req, swtch.method, data, ContentType.plain, swtch.header);
|
|
749
802
|
}
|
|
750
803
|
}
|
|
751
804
|
|
package/server/socapp.js
CHANGED
|
@@ -14,7 +14,7 @@ const fs = require("fs");
|
|
|
14
14
|
const mime = require("mime-types");
|
|
15
15
|
const net = require("net");
|
|
16
16
|
const ws_1 = require("ws");
|
|
17
|
-
const
|
|
17
|
+
const logger_1 = require("../duotecno/logger");
|
|
18
18
|
const support_1 = require("./support");
|
|
19
19
|
const webapp_1 = require("./webapp");
|
|
20
20
|
////////////////
|
|
@@ -26,7 +26,7 @@ class SocApp extends webapp_1.WebApp {
|
|
|
26
26
|
// hashmap with all connected clients
|
|
27
27
|
this.clients = {};
|
|
28
28
|
if (this.config.debug)
|
|
29
|
-
|
|
29
|
+
logger_1.logSettings["proxy"] = logger_1.LogLevel.debug;
|
|
30
30
|
this.port = port || this.config.port || this.port || 80;
|
|
31
31
|
this.system = system;
|
|
32
32
|
this.support = new support_1.Support(system);
|
|
@@ -53,7 +53,7 @@ class SocApp extends webapp_1.WebApp {
|
|
|
53
53
|
return this.serveFile(context, context.path);
|
|
54
54
|
}
|
|
55
55
|
catch (error) {
|
|
56
|
-
(0,
|
|
56
|
+
(0, logger_1.err)("proxy", "Error serving URL: " + context.req.url);
|
|
57
57
|
return this.serveFile(context, "/index.html");
|
|
58
58
|
// return this.notFound(context.req.url);
|
|
59
59
|
}
|
|
@@ -61,7 +61,7 @@ class SocApp extends webapp_1.WebApp {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
serve() {
|
|
64
|
-
(0,
|
|
64
|
+
(0, logger_1.log)("proxy", "SocApp - Start http server on port " + this.port);
|
|
65
65
|
super.serve(() => {
|
|
66
66
|
const wsServer = new ws_1.Server({ server: this.server });
|
|
67
67
|
wsServer.on('connection', this.handleClient.bind(this));
|
|
@@ -71,7 +71,7 @@ class SocApp extends webapp_1.WebApp {
|
|
|
71
71
|
var _a, _b;
|
|
72
72
|
const data = fs.readFileSync("www" + fn);
|
|
73
73
|
const type = mime.lookup(fn) || "application/text";
|
|
74
|
-
(0,
|
|
74
|
+
(0, logger_1.log)("proxy", "Serve - " + (((_b = (_a = context.req) === null || _a === void 0 ? void 0 : _a.socket) === null || _b === void 0 ? void 0 : _b.remoteAddress) || "no-remote") + ": " + fn + " - " + type);
|
|
75
75
|
return { status: 200, data, type };
|
|
76
76
|
}
|
|
77
77
|
renderLog() {
|
|
@@ -96,7 +96,7 @@ class SocApp extends webapp_1.WebApp {
|
|
|
96
96
|
handleClient(client, req) {
|
|
97
97
|
let target;
|
|
98
98
|
const clientAddr = client._socket.remoteAddress;
|
|
99
|
-
const logger = (msg) => (0,
|
|
99
|
+
const logger = (msg) => (0, logger_1.log)('proxy', clientAddr + ': ' + msg);
|
|
100
100
|
const info = (this.config.debug) ?
|
|
101
101
|
(msg) => logger('Proxy - ' + clientAddr + ': ' + msg) :
|
|
102
102
|
(msg) => null;
|
package/server/socapp.ts
CHANGED
|
@@ -3,7 +3,8 @@ import * as mime from 'mime-types';
|
|
|
3
3
|
import * as net from 'net';
|
|
4
4
|
import { Server as WSServer } from 'ws';
|
|
5
5
|
import { System } from '../duotecno/system';
|
|
6
|
-
import { LogFunction
|
|
6
|
+
import { LogFunction } from '../duotecno/types';
|
|
7
|
+
import { log, logSettings, LogLevel, err } from "../duotecno/logger";
|
|
7
8
|
import { Support } from './support';
|
|
8
9
|
import { Context, HttpResponse, WebApp } from "./webapp";
|
|
9
10
|
|