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.
Files changed (66) hide show
  1. package/README.md +14 -0
  2. package/accessories/accessory.js +4 -4
  3. package/accessories/accessory.ts +1 -2
  4. package/accessories/dimmer.js +6 -6
  5. package/accessories/dimmer.ts +1 -1
  6. package/accessories/door.js +2 -2
  7. package/accessories/door.ts +1 -1
  8. package/accessories/garagedoor.js +5 -4
  9. package/accessories/garagedoor.ts +2 -1
  10. package/accessories/lock.js +6 -5
  11. package/accessories/lock.ts +2 -1
  12. package/accessories/mood.js +2 -1
  13. package/accessories/mood.ts +2 -1
  14. package/accessories/temperature.js +2 -2
  15. package/accessories/temperature.ts +1 -1
  16. package/accessories/windowcovering.js +8 -7
  17. package/accessories/windowcovering.ts +2 -1
  18. package/config.json +169 -228
  19. package/config.smappee.json +178 -2
  20. package/config.smartapp.json +63 -0
  21. package/duotecno/Q.js +8 -8
  22. package/duotecno/Q.ts +1 -1
  23. package/duotecno/api.js +3 -3
  24. package/duotecno/api.ts +2 -2
  25. package/duotecno/config.js +24 -23
  26. package/duotecno/config.ts +23 -16
  27. package/duotecno/logger.js +51 -0
  28. package/duotecno/logger.ts +41 -0
  29. package/duotecno/master.js +4 -6
  30. package/duotecno/master.ts +2 -4
  31. package/duotecno/protocol.js +10 -9
  32. package/duotecno/protocol.ts +2 -1
  33. package/duotecno/system.js +17 -41
  34. package/duotecno/system.ts +7 -34
  35. package/duotecno/types.js +58 -131
  36. package/duotecno/types.ts +65 -125
  37. package/index.spec.js +22 -36
  38. package/index.spec.ts +20 -33
  39. package/package.json +2 -2
  40. package/server/base.js +10 -38
  41. package/server/base.ts +9 -42
  42. package/server/p1.js +9 -8
  43. package/server/p1.ts +6 -4
  44. package/server/platform.js +66 -36
  45. package/server/platform.ts +47 -12
  46. package/server/powerbase.js +15 -55
  47. package/server/powerbase.ts +4 -50
  48. package/server/shelly.js +4 -4
  49. package/server/shelly.ts +3 -2
  50. package/server/smappee.js +9 -8
  51. package/server/smappee.ts +4 -3
  52. package/server/smartapp.js +94 -39
  53. package/server/smartapp.ts +73 -20
  54. package/server/socapp.js +6 -6
  55. package/server/socapp.ts +2 -1
  56. package/server/somfy.js +12 -12
  57. package/server/somfy.ts +1 -1
  58. package/server/support.js +13 -13
  59. package/server/support.ts +4 -4
  60. package/server/views/style.ejs +1 -0
  61. package/server/views/switch-details.ejs +15 -5
  62. package/server/webapp.js +45 -47
  63. package/server/webapp.ts +29 -38
  64. package/config.groups.json +0 -8
  65. package/config.p1-dev.json +0 -59
  66. package/config.scenes.json +0 -15
@@ -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, dev = false) {
53
- super("smartapp" + (dev ? "-dev" : ""));
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, types_1.log)("smartapp", "Platform not ready -> " + context["notReadyMessage"]);
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 (pw === this.password);
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, types_1.log)("smartapp", "Init " + this.switches.length + " Switches -> add units");
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, types_1.log)("smartapp", "** error ** missing unit: " + (0, types_1.hex)(swtch.logicalNodeAddress) + "/" + (0, types_1.hex)(swtch.logicalAddress) + " **");
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, types_1.debug)("smartapp", "Received " + type + " switch status change: " + plugNr + " -> " + value);
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, types_1.debug)("smartapp", " -> Switch was attached to unit = " + swtch.unit.getDisplayName() + " -> setting state to " + value);
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, types_1.debug)("smartapp", "*** macro changed blocked by switch");
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, types_1.err)("smartapp", "Didn't find switch with inx: " + inx);
520
+ (0, logger_1.err)("smartapp", "Didn't find switch with inx: " + inx);
493
521
  }
494
522
  else if (!swtch.unit) {
495
- (0, types_1.err)("smartapp", "Don't have unit for switch: " + swtch.unitname);
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, types_1.err)("smartapp", "Don't know how to set a switch of type " + swtch.type);
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
- (0, types_1.log)("smartapp", "HTTP-Switch(" + !!swtch.unit.status + ") -> " + req);
569
- this.wrequest(req);
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, types_1.debug)("smartapp", "Blocked stop for HTTP-UpDown " + swtch.name);
619
+ (0, logger_1.debug)("smartapp", "Blocked stop for HTTP-UpDown " + swtch.name);
582
620
  }
583
621
  else {
584
- (0, types_1.log)("smartapp", "UpDown(" + val + ") -> " + base);
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, types_1.debug)("smartapp", "Dimmer(" + !!swtch.unit.status + "," + swtch.unit.value + ") -> " + req + " + " + data);
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["headers"] = {
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["headers"] = {
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, types_1.debug)("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + JSON.stringify(x));
685
+ (0, logger_1.debug)("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + JSON.stringify(x));
631
686
  }
632
687
  catch (e) {
633
- (0, types_1.debug)("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + resp);
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, types_1.log)("smartapp", "The request to " + url + ", error: " + e.message);
697
+ (0, logger_1.log)("smartapp", "The request to " + url + ", error: " + e.message);
643
698
  });
644
699
  req.on("timeout", (e) => {
645
- (0, types_1.log)("smartapp", "The request to " + url + ", timeout: " + e.message);
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, types_1.log)("smartapp", "** http error ** " + e.message + " **");
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, types_1.debug)("smartapp", "OH-Switch(" + !!swtch.unit.status + ") -> " + req);
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, types_1.debug)("smartapp", "OH-Dimmer(" + !!swtch.unit.status + ", " + val + ") -> " + req);
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, types_1.debug)("smartapp", "Blocked stop for OH-UpDown " + swtch.name);
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, types_1.debug)("smartapp", "OH-UpDown(" + swtch.unit.value + ", " + data + ") -> " + req);
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, types_1.log)("smartapp", "setState requested with node = " + nodeLogicalAddress + ", unit = " + unitLogicalAddress + " -> " + value);
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, types_1.log)("smartapp", "getState requested with node = " + nodeLogicalAddress + ", unit = " + unitLogicalAddress);
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 };
@@ -9,7 +9,8 @@
9
9
  // v2.0: mar/apr 2020
10
10
 
11
11
  import { Context, HttpResponse, WebApp } from "./webapp";
12
- import { log, Sanitizers, Rule, kEmptyRule, actionValue, Action, kEmptySwitch, Switch, SwitchType, hex, Binding, NetworkConfig, SettingsConfig, kEmptySettings, debug, err } from "../duotecno/types";
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, dev = false) {
61
- super("smartapp" + (dev ? "-dev" : ""));
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
- return (pw === this.password);
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?, contentType = ContentType.form) {
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["headers"] = {
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["headers"] = {
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 types_1 = require("../duotecno/types");
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
- types_1.logSettings["proxy"] = types_1.LogLevel.debug;
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, types_1.err)("proxy", "Error serving URL: " + context.req.url);
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, types_1.log)("proxy", "SocApp - Start http server on port " + this.port);
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, types_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);
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, types_1.log)('proxy', clientAddr + ': ' + msg);
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, log, logSettings, LogLevel, err } from '../duotecno/types';
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