homebridge-smartsystem 6.6.0 → 6.7.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 CHANGED
@@ -293,6 +293,9 @@ Homebridge UI version
293
293
  ### v6.6.0 - 21-05-2025 - OpenHAB
294
294
  - 0: Extra "Switch" type openHAB, uses username/password to ask for a token
295
295
 
296
+ ### v6.7.0 - 19/06/2025
297
+ - 0: implemented "links" between HB accessories and Duotecno units.
298
+ - 1: updated the readme file
296
299
 
297
300
  ## Hardware
298
301
  A Raspberry Pi that connects to a Duotecno IP Node
package/config.json CHANGED
@@ -134,7 +134,7 @@
134
134
  }
135
135
  ]
136
136
  },
137
- "p1": {
137
+ "p11": {
138
138
  "debug": false,
139
139
  "rules": [],
140
140
  "bindings": [
@@ -247,27 +247,25 @@
247
247
  "method": "GET",
248
248
  "type": "smappee",
249
249
  "plug": "1"
250
- },
250
+ }
251
+ ],
252
+ "debug": false,
253
+ "username": "pi",
254
+ "password": "GMTecno",
255
+ "links": [
251
256
  {
252
257
  "logicalNodeAddress": 3,
253
- "logicalAddress": 31,
258
+ "logicalAddress": 30,
254
259
  "masterAddress": "192.168.0.97",
255
260
  "masterPort": 5001,
256
- "name": "Somfy Windowcovering",
257
- "displayName": "Somfy Windowcovering",
261
+ "name": "Blue LEDje",
262
+ "displayName": "Blue LEDje",
258
263
  "extendedType": 0,
259
- "nomacro": "N",
260
- "nostop": "N",
261
- "unitName": "Somfy Dimmer",
262
- "value": 0,
263
- "data": "{CurrentPosition: $}",
264
- "header": "Pi:GMTecn",
265
- "method": "PUT",
266
- "type": "ohapi",
267
- "plug": "http://Pi:GMTecno@192.168.0.99:8581/api/accessory/06e5b66f78fe0136ea075e742a7fb388587329a4717393b21aaa180083305704"
264
+ "accId": "986b901a30d92cb7bd0205be90f65786c2d87c9a95d8a0a60cb4c048deda5ce6",
265
+ "accName": "Tim Klein",
266
+ "type": "accessory"
268
267
  }
269
- ],
270
- "debug": false
268
+ ]
271
269
  },
272
270
  "system": {
273
271
  "language": "EN",
@@ -290,21 +288,6 @@
290
288
  "252": " 20211003"
291
289
  },
292
290
  "debug": false
293
- },
294
- {
295
- "address": "192.168.0.5",
296
- "port": 5005,
297
- "password": "duotecno",
298
- "name": "GM",
299
- "active": true,
300
- "nodenames": {
301
- "3": "IP Server",
302
- "4": "Sonos",
303
- "16": "Smartbox Living",
304
- "17": "Switch Living",
305
- "252": " 20220925"
306
- },
307
- "debug": false
308
291
  }
309
292
  ],
310
293
  "cunits": [
@@ -392,6 +375,18 @@
392
375
  "used": "Y",
393
376
  "group": 0
394
377
  },
378
+ {
379
+ "logicalNodeAddress": 3,
380
+ "logicalAddress": 29,
381
+ "masterAddress": "192.168.0.97",
382
+ "masterPort": 5001,
383
+ "name": "Red LED",
384
+ "displayName": "Red LED",
385
+ "extendedType": 0,
386
+ "active": "N",
387
+ "used": "Y",
388
+ "group": 0
389
+ },
395
390
  {
396
391
  "logicalNodeAddress": 3,
397
392
  "logicalAddress": 34,
@@ -466,5 +461,6 @@
466
461
  "ip2": "",
467
462
  "gateway2": ""
468
463
  }
469
- }
464
+ },
465
+ "p1": false
470
466
  }
package/duotecno/types.js CHANGED
@@ -14,7 +14,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
14
14
  });
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.makeInt = exports.now = exports.datetime = exports.time = exports.date = exports.watt = exports.hex = exports.two = exports.char = exports.ascii = exports.wait = exports.Sanitizers = exports.actionValueStr = exports.actionValue = exports.kEmptySwitch = exports.kEmptyRule = exports.kEmptyAction = exports.kEmptyBinding = exports.SwitchType = exports.RuleType = exports.Boundaries = exports.kEmptyCommRecord = exports.WriteError = exports.kEmptySettings = exports.kEmptyNetworkConfig = exports.kEmptyGroup = exports.kEmptyScene = exports.kEmptyUnitScene = exports.kEmptyUnit = exports.UnitExtendedType = exports.UnitType = exports.UnitMotorCmd = exports.UnitState = exports.NodeType = void 0;
17
+ exports.makeInt = exports.now = exports.datetime = exports.time = exports.date = exports.watt = exports.hex = exports.two = exports.char = exports.ascii = exports.wait = exports.Sanitizers = exports.actionValueStr = exports.actionValue = exports.kEmptyBinding = exports.kEmptyLink = exports.LinkType = exports.kEmptySwitch = exports.SwitchType = exports.kEmptyRule = exports.kEmptyAction = exports.RuleType = exports.Boundaries = exports.kEmptyCommRecord = exports.WriteError = exports.kEmptySettings = exports.kEmptyNetworkConfig = exports.kEmptyGroup = exports.kEmptyScene = exports.kEmptyUnitScene = exports.kEmptyUnit = exports.UnitExtendedType = exports.UnitType = exports.UnitMotorCmd = exports.UnitState = exports.NodeType = void 0;
18
18
  // Node types
19
19
  var NodeType;
20
20
  (function (NodeType) {
@@ -126,6 +126,13 @@ var RuleType;
126
126
  RuleType["kWater"] = "water";
127
127
  })(RuleType = exports.RuleType || (exports.RuleType = {}));
128
128
  ;
129
+ exports.kEmptyAction = Object.assign(Object.assign({}, exports.kEmptyUnit), { value: false });
130
+ ;
131
+ exports.kEmptyRule = {
132
+ type: "power", channel: "0", low: 30, high: 900,
133
+ actions: [Object.assign(Object.assign({}, exports.kEmptyUnit), { value: false }), Object.assign(Object.assign({}, exports.kEmptyUnit), { value: 50 }), Object.assign(Object.assign({}, exports.kEmptyUnit), { value: true })]
134
+ };
135
+ // Switches
129
136
  var SwitchType;
130
137
  (function (SwitchType) {
131
138
  SwitchType["kNoType"] = "";
@@ -137,21 +144,19 @@ var SwitchType;
137
144
  SwitchType["kOhSwitch"] = "oswitch";
138
145
  SwitchType["kOhDimmer"] = "odimmer";
139
146
  SwitchType["kOhUpDown"] = "oupdown";
140
- SwitchType["kohAPI"] = "ohapi";
141
147
  SwitchType["kSomfy"] = "somfy";
142
148
  })(SwitchType = exports.SwitchType || (exports.SwitchType = {}));
143
- ;
149
+ exports.kEmptySwitch = Object.assign(Object.assign({}, exports.kEmptyUnit), { plug: 0, type: SwitchType.kNoType, unitName: "", name: "", data: "", method: "GET", header: "", username: "", password: "", nomacro: "N", nostop: "N" });
150
+ // Links
151
+ var LinkType;
152
+ (function (LinkType) {
153
+ LinkType["kNoType"] = "";
154
+ LinkType["kAccessory"] = "accessory";
155
+ })(LinkType = exports.LinkType || (exports.LinkType = {}));
156
+ exports.kEmptyLink = Object.assign(Object.assign({}, exports.kEmptyUnit), { type: LinkType.kAccessory, accName: "acc", accId: "" });
144
157
  exports.kEmptyBinding = {
145
158
  channel: "0", masterAddress: "0.0.0.0", masterPort: 5001, register: 0
146
159
  };
147
- exports.kEmptyAction = Object.assign(Object.assign({}, exports.kEmptyUnit), { value: false });
148
- ;
149
- exports.kEmptyRule = {
150
- type: "power", channel: "0", low: 30, high: 900,
151
- actions: [Object.assign(Object.assign({}, exports.kEmptyUnit), { value: false }), Object.assign(Object.assign({}, exports.kEmptyUnit), { value: 50 }), Object.assign(Object.assign({}, exports.kEmptyUnit), { value: true })]
152
- };
153
- ;
154
- exports.kEmptySwitch = Object.assign(Object.assign({}, exports.kEmptyUnit), { plug: 0, type: SwitchType.kNoType, unitName: "", name: "", data: "", method: "GET", header: "", username: "", password: "", nomacro: "N", nostop: "N" });
155
160
  ;
156
161
  ;
157
162
  ;
@@ -249,9 +254,13 @@ exports.Sanitizers = {
249
254
  config.port = config.port || 5002;
250
255
  config.switches = config.switches || [];
251
256
  config.switches = config.switches.map(sw => exports.Sanitizers.switchConfig(sw));
257
+ config.links = config.links || [];
258
+ config.links = config.links.map(sw => exports.Sanitizers.linkConfig(sw));
252
259
  config.debug = config.debug || false;
253
260
  config.apiHost = config.apiHost || "localhost";
254
261
  config.apiPort = config.apiPort || 8581;
262
+ config.username = config.username || "admin";
263
+ config.password = config.password || "";
255
264
  return config;
256
265
  },
257
266
  powerbase: function (config) {
@@ -285,6 +294,13 @@ exports.Sanitizers = {
285
294
  cfg.addresses = (config === null || config === void 0 ? void 0 : config.addresses) || "";
286
295
  return cfg;
287
296
  },
297
+ linkConfig: function (aLink) {
298
+ const link = exports.Sanitizers.unitDef(aLink);
299
+ link.accId = aLink.accId || "";
300
+ link.accName = aLink.accName || "";
301
+ link.type = aLink.type || LinkType.kNoType;
302
+ return link;
303
+ },
288
304
  switchConfig: function (aSwitch) {
289
305
  const sw = exports.Sanitizers.unitDef(aSwitch);
290
306
  sw.nomacro = aSwitch.nomacro || "N";
@@ -309,6 +325,12 @@ exports.Sanitizers = {
309
325
  type: aSwitch.type, plug: aSwitch.plug,
310
326
  data: aSwitch.data, method: aSwitch.method, header: aSwitch.header });
311
327
  },
328
+ makeLinkConfig: function (aLink) {
329
+ return exports.Sanitizers.linkConfig({ name: aLink.name, unitName: aLink.unitName,
330
+ masterAddress: aLink.masterAddress, masterPort: aLink.masterPort,
331
+ logicalAddress: aLink.logicalAddress, logicalNodeAddress: aLink.logicalNodeAddress,
332
+ accName: aLink.accName, accId: aLink.accId, type: aLink.type });
333
+ },
312
334
  ruleConfig(rule) {
313
335
  rule = Object.assign({}, rule);
314
336
  rule.channel = rule.channel || "0";
package/duotecno/types.ts CHANGED
@@ -193,38 +193,17 @@ export const kEmptyCommRecord = { status: false, cmd: -1, message: [-1,0,0], res
193
193
  export enum Boundaries {kLow = 0, kMid = 1, kHigh = 2};
194
194
  export enum RuleType {kPower = "power", kCurrent = "current", kWater = "water"};
195
195
 
196
- export enum SwitchType {
197
- kNoType = "",
198
- kSmappee = "smappee",
199
- kRF = "RF",
200
- kHTTPSwitch = "http",
201
- kHTTPDimmer = "httpdim",
202
- kHTTPUpDown = "httpupdown",
203
- kOhSwitch = "oswitch",
204
- kOhDimmer = "odimmer",
205
- kOhUpDown = "oupdown",
206
- kohAPI = "ohapi",
207
- kSomfy = "somfy"
208
- };
209
-
210
-
211
- export interface Binding {
212
- masterAddress: string;
213
- masterPort: number;
214
- register: number;
215
- channel: string;
216
- value?: number;
217
- }
218
- export const kEmptyBinding: Binding = {
219
- channel: "0", masterAddress: "0.0.0.0", masterPort: 5001, register: 0
220
- } as const;
221
196
 
222
197
 
198
+ ///////////
199
+ // Rules //
200
+ ///////////
223
201
  export interface Action extends UnitDef {
224
202
  value: number | boolean;
225
203
  }
226
- export const kEmptyAction: Action = { ...kEmptyUnit, value: false } as const;
227
-
204
+ export const kEmptyAction: Action = {
205
+ ...kEmptyUnit, value: false
206
+ } as const;
228
207
 
229
208
  export interface Rule {
230
209
  type: string;
@@ -240,44 +219,78 @@ export const kEmptyRule: Rule = {
240
219
  actions: [{...kEmptyUnit, value: false}, {...kEmptyUnit, value: 50}, {...kEmptyUnit, value: true}]
241
220
  };
242
221
 
222
+
223
+ // Switches
224
+
225
+ export enum SwitchType {
226
+ kNoType = "",
227
+ kSmappee = "smappee",
228
+ kRF = "RF",
229
+ kHTTPSwitch = "http",
230
+ kHTTPDimmer = "httpdim",
231
+ kHTTPUpDown = "httpupdown",
232
+ kOhSwitch = "oswitch",
233
+ kOhDimmer = "odimmer",
234
+ kOhUpDown = "oupdown",
235
+ kSomfy = "somfy"
236
+ }
237
+
243
238
  export interface Switch extends UnitDef {
244
- name: string,
245
- unitName: string,
246
- type: SwitchType,
247
- plug: number | string,
248
- data: string,
249
- header: string,
250
- method: string,
239
+ unitName: string;
240
+ type: SwitchType;
241
+ plug: number | string;
242
+ data: string;
243
+ header: string;
244
+ method: string;
251
245
  username?: string;
252
246
  password?: string;
253
247
  unit?: Unit;
254
- status?: number | boolean,
255
- value?: number | boolean | string,
248
+ status?: number | boolean;
249
+ value?: number | boolean | string;
256
250
  nomacro: string;
257
251
  nostop: string;
258
- };
259
- export const kEmptySwitch: Switch =
260
- { ...kEmptyUnit, plug: 0, type: SwitchType.kNoType, unitName: "", name: "", data: "", method: "GET",
261
- header: "", username: "", password: "", nomacro: "N", nostop: "N"} as const;
252
+ }
253
+
254
+ export const kEmptySwitch: Switch = {
255
+ ...kEmptyUnit, plug: 0, type: SwitchType.kNoType, unitName: "", name: "", data: "", method: "GET",
256
+ header: "", username: "", password: "", nomacro: "N", nostop: "N"
257
+ } as const;
262
258
 
263
259
 
264
- export interface SmartAppConfig extends BaseConfig {
265
- port: number;
266
- apiHost: string;
267
- apiPort: number;
268
- switches: Array<Switch>;
260
+ // Links
261
+ export enum LinkType {
262
+ kNoType = "",
263
+ kAccessory = "accessory"
269
264
  }
270
265
 
271
- /////////////////////
272
- // Smartapp config //
273
- /////////////////////
274
- export interface BaseConfig {
275
- debug?: boolean;
266
+ export interface Link extends UnitDef{
267
+ accName: string;
268
+ accId: string;
269
+ type: LinkType;
270
+ unit?: Unit;
271
+ status?: number | boolean;
272
+ value?: number | boolean | string
276
273
  }
277
274
 
275
+ export const kEmptyLink: Link = {
276
+ ...kEmptyUnit, type: LinkType.kAccessory, accName: "acc", accId: ""
277
+ } as const;
278
+
279
+
278
280
  ///////////
279
281
  // Power //
280
282
  ///////////
283
+ export interface Binding {
284
+ masterAddress: string;
285
+ masterPort: number;
286
+ register: number;
287
+ channel: string;
288
+ value?: number;
289
+ }
290
+ export const kEmptyBinding: Binding = {
291
+ channel: "0", masterAddress: "0.0.0.0", masterPort: 5001, register: 0
292
+ } as const;
293
+
281
294
  export interface PowerBaseConfig extends BaseConfig {
282
295
  address: string;
283
296
  rules: Array<Rule>;
@@ -292,6 +305,24 @@ export interface ShellyConfig extends PowerBaseConfig {
292
305
  addresses: string;
293
306
  }
294
307
 
308
+
309
+ /////////////////////
310
+ // Smartapp config //
311
+ /////////////////////
312
+ export interface BaseConfig {
313
+ debug?: boolean;
314
+ }
315
+
316
+ export interface SmartAppConfig extends BaseConfig {
317
+ port: number;
318
+ apiHost: string;
319
+ apiPort: number;
320
+ username: string; password: string;
321
+ switches: Array<Switch>;
322
+ links: Array<Link>;
323
+ }
324
+
325
+
295
326
  ////////////////
296
327
  // Homebridge //
297
328
  ////////////////
@@ -434,9 +465,13 @@ export const Sanitizers = {
434
465
  config.port = config.port || 5002;
435
466
  config.switches = config.switches || [];
436
467
  config.switches = config.switches.map(sw => Sanitizers.switchConfig(sw));
468
+ config.links = config.links || [];
469
+ config.links = config.links.map(sw => Sanitizers.linkConfig(sw));
437
470
  config.debug = config.debug || false;
438
471
  config.apiHost = config.apiHost || "localhost";
439
472
  config.apiPort = config.apiPort || 8581;
473
+ config.username = config.username || "admin";
474
+ config.password = config.password || "";
440
475
 
441
476
  return config;
442
477
  },
@@ -476,6 +511,14 @@ export const Sanitizers = {
476
511
  return cfg;
477
512
  },
478
513
 
514
+ linkConfig: function(aLink): Link {
515
+ const link = <Link>Sanitizers.unitDef(aLink);
516
+ link.accId = aLink.accId || "";
517
+ link.accName = aLink.accName || "";
518
+ link.type = aLink.type || LinkType.kNoType;
519
+ return link;
520
+ },
521
+
479
522
  switchConfig: function(aSwitch): Switch {
480
523
  const sw = <Switch>Sanitizers.unitDef(aSwitch);
481
524
  sw.nomacro = aSwitch.nomacro || "N";
@@ -503,6 +546,13 @@ export const Sanitizers = {
503
546
  data: aSwitch.data, method: aSwitch.method, header: aSwitch.header });
504
547
  },
505
548
 
549
+ makeLinkConfig: function(aLink): Link {
550
+ return Sanitizers.linkConfig({ name: aLink.name, unitName: aLink.unitName,
551
+ masterAddress: aLink.masterAddress, masterPort: aLink.masterPort,
552
+ logicalAddress: aLink.logicalAddress, logicalNodeAddress: aLink.logicalNodeAddress,
553
+ accName: aLink.accName, accId: aLink.accId, type: aLink.type });
554
+ },
555
+
506
556
  ruleConfig(rule): Rule {
507
557
  rule = {... rule};
508
558
  rule.channel = rule.channel || "0";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-smartsystem",
3
3
  "displayname": "Duotecno Bridge",
4
- "version": "6.6.0",
4
+ "version": "6.7.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",
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.httpRequest = exports.ContentType = void 0;
13
+ const logger_1 = require("../duotecno/logger");
14
+ const http = require("http");
15
+ //////////////////////////////////
16
+ // Home Assistant API //
17
+ // and other http based APIs //
18
+ //////////////////////////////////
19
+ var ContentType;
20
+ (function (ContentType) {
21
+ ContentType[ContentType["form"] = 0] = "form";
22
+ ContentType[ContentType["plain"] = 1] = "plain";
23
+ ContentType[ContentType["none"] = 2] = "none";
24
+ ContentType[ContentType["json"] = 3] = "json";
25
+ })(ContentType = exports.ContentType || (exports.ContentType = {}));
26
+ ;
27
+ function httpRequest(url, method = "GET", formdata = "", contentType = ContentType.form, header = "") {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ if (url.indexOf("://") === -1)
30
+ url = "http://" + url;
31
+ return new Promise((resolve, reject) => {
32
+ try {
33
+ // const data = querystring.stringify(formdata);
34
+ const options = { method };
35
+ if (formdata) {
36
+ if (contentType === ContentType.form) {
37
+ options.headers = {
38
+ 'Content-Type': 'application/x-www-form-urlencoded',
39
+ 'Content-Length': Buffer.byteLength(formdata)
40
+ };
41
+ }
42
+ else if (contentType === ContentType.plain) {
43
+ options.headers = {
44
+ 'Content-Type': 'text/plain',
45
+ 'Accept': 'application/json'
46
+ };
47
+ }
48
+ else if (contentType === ContentType.json) {
49
+ options.headers = {
50
+ 'Content-Type': 'application/json',
51
+ 'Accept': '*/*'
52
+ };
53
+ }
54
+ }
55
+ ;
56
+ // header provide as string or object
57
+ if (header) {
58
+ if (!options.headers)
59
+ options.headers = {};
60
+ if (typeof header === "string") {
61
+ if (header.substring(0, 1) === "{") {
62
+ try {
63
+ options.headers = Object.assign(Object.assign({}, options.headers), JSON.parse(header));
64
+ }
65
+ catch (e) {
66
+ (0, logger_1.err)("smartapp", "error parsing :" + header + " -> " + JSON.stringify(e));
67
+ }
68
+ }
69
+ else {
70
+ const s = header.split(":");
71
+ options.headers[s[0]] = s[1];
72
+ }
73
+ }
74
+ else {
75
+ // header is an object
76
+ options.headers = Object.assign(Object.assign({}, options.headers), header);
77
+ }
78
+ }
79
+ (0, logger_1.debug)("smartapp", "Sending to " + url + " with options: " + JSON.stringify(options));
80
+ const req = http.request(url, options, res => {
81
+ let resp = "";
82
+ // res.setEncoding('utf8');
83
+ res.on('error', error => {
84
+ (0, logger_1.log)("smartapp", "The request to " + url + ", error: " + (error ? error.message : "unknown"));
85
+ reject(error);
86
+ });
87
+ res.on('data', chunk => {
88
+ resp += chunk;
89
+ });
90
+ res.on('end', () => {
91
+ // try to make something out of it...
92
+ try {
93
+ const x = JSON.parse(resp);
94
+ if ((x.type === "Buffer") && x.data)
95
+ x.data = x.data.toString();
96
+ (0, logger_1.debug)("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + JSON.stringify(x).substring(0, 200) + "...");
97
+ resolve(x);
98
+ }
99
+ catch (e) {
100
+ (0, logger_1.err)("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + resp);
101
+ resolve({ status: "NOK", result: resp });
102
+ }
103
+ ;
104
+ });
105
+ });
106
+ if (formdata) {
107
+ req.write(formdata);
108
+ }
109
+ req.on("error", (e) => {
110
+ (0, logger_1.log)("smartapp", "The request to " + url + ", error: " + (e ? e.message : "unknown"));
111
+ });
112
+ req.on("timeout", (e) => {
113
+ (0, logger_1.log)("smartapp", "The request to " + url + ", timeout: " + (e ? e.message : "unknown"));
114
+ });
115
+ req.end();
116
+ }
117
+ catch (e) {
118
+ (0, logger_1.log)("smartapp", "** http error ** " + (e ? e.message : "unknown") + " **");
119
+ reject(e);
120
+ }
121
+ });
122
+ });
123
+ }
124
+ exports.httpRequest = httpRequest;
125
+ //# sourceMappingURL=HA-API.js.map
@@ -0,0 +1,99 @@
1
+ import { debug, err, log } from "../duotecno/logger";
2
+ import * as http from "http";
3
+
4
+ //////////////////////////////////
5
+ // Home Assistant API //
6
+ // and other http based APIs //
7
+ //////////////////////////////////
8
+ export enum ContentType {form, plain, none, json};
9
+
10
+
11
+ export async function httpRequest(url: string, method = "GET", formdata = "",
12
+ contentType = ContentType.form, header: string | object = ""): Promise<Record<string, any>> {
13
+ if (url.indexOf("://") === -1) url = "http://" + url;
14
+
15
+ return new Promise((resolve, reject) => {
16
+ try {
17
+ // const data = querystring.stringify(formdata);
18
+ const options: {method: string, [key: string]: object | string} = { method };
19
+
20
+ if (formdata) {
21
+ if (contentType === ContentType.form) {
22
+ options.headers = {
23
+ 'Content-Type': 'application/x-www-form-urlencoded',
24
+ 'Content-Length': Buffer.byteLength(formdata)
25
+ }
26
+ } else if (contentType === ContentType.plain) {
27
+ options.headers = {
28
+ 'Content-Type': 'text/plain',
29
+ 'Accept': 'application/json'
30
+ };
31
+ } else if (contentType === ContentType.json) {
32
+ options.headers = {
33
+ 'Content-Type': 'application/json',
34
+ 'Accept': '*/*'
35
+ };
36
+ }
37
+ };
38
+
39
+ // header provide as string or object
40
+ if (header) {
41
+ if (!options.headers) options.headers = {};
42
+ if (typeof header === "string") {
43
+ if (header.substring(0,1) === "{") {
44
+ try {
45
+ options.headers = { ...<object>options.headers, ...JSON.parse(header)};
46
+ } catch(e) {
47
+ err("smartapp", "error parsing :" + header + " -> " + JSON.stringify(e));
48
+ }
49
+ } else {
50
+ const s = header.split(":");
51
+ options.headers[s[0]] = s[1];
52
+ }
53
+ } else {
54
+ // header is an object
55
+ options.headers = { ...<object>options.headers, ...header };
56
+ }
57
+ }
58
+
59
+ debug("smartapp", "Sending to " + url + " with options: " + JSON.stringify(options));
60
+ const req = http.request(url, options, res => {
61
+ let resp = "";
62
+ // res.setEncoding('utf8');
63
+ res.on('error', error => {
64
+ log("smartapp", "The request to " + url + ", error: " + (error ? error.message : "unknown"));
65
+ reject(error);
66
+ });
67
+ res.on('data', chunk => {
68
+ resp += chunk;
69
+ });
70
+ res.on('end', () => {
71
+ // try to make something out of it...
72
+ try {
73
+ const x = JSON.parse(resp);
74
+ if ((x.type === "Buffer") && x.data) x.data = x.data.toString();
75
+ debug("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + JSON.stringify(x).substring(0, 200) + "...");
76
+ resolve(x)
77
+ } catch(e) {
78
+ err("smartapp", "http " + method + " -> " + res.statusCode + ": " + url + " = " + resp);
79
+ resolve({status: "NOK", result: resp});
80
+ };
81
+ });
82
+ });
83
+ if (formdata) {
84
+ req.write(formdata);
85
+ }
86
+ req.on("error", (e) => {
87
+ log("smartapp", "The request to " + url + ", error: " + (e ? e.message : "unknown"));
88
+ });
89
+ req.on("timeout", (e) => {
90
+ log("smartapp", "The request to " + url + ", timeout: " + (e ? e.message : "unknown"));
91
+ });
92
+ req.end();
93
+
94
+ } catch(e) {
95
+ log("smartapp", "** http error ** " + (e ? e.message : "unknown") + " **");
96
+ reject(e);
97
+ }
98
+ });
99
+ }