homebridge-smartsystem 6.7.0 → 6.7.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 CHANGED
@@ -293,11 +293,18 @@ 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
299
+ - 2: use HB Accessory characteristics in set/get, ignore status for the moment
300
+ - 2: remember/show username in login screen + set focus to password
296
301
 
297
302
  ## Hardware
298
303
  A Raspberry Pi that connects to a Duotecno IP Node
299
304
  and (if configured) to a Smappee Infinity (power and plugs)
300
305
  and to http switches (bi-directional)
306
+ and to openHAB http (from v5.1)
307
+ and linked to HB accessories (from v6.7)
301
308
 
302
309
 
303
310
  ## Desired functions
package/config.json CHANGED
@@ -258,11 +258,33 @@
258
258
  "logicalAddress": 30,
259
259
  "masterAddress": "192.168.0.97",
260
260
  "masterPort": 5001,
261
- "name": "Blue LEDje",
262
- "displayName": "Blue LEDje",
261
+ "name": "unit",
262
+ "displayName": "unit",
263
263
  "extendedType": 0,
264
264
  "accId": "986b901a30d92cb7bd0205be90f65786c2d87c9a95d8a0a60cb4c048deda5ce6",
265
265
  "accName": "Tim Klein",
266
+ "accService": [
267
+ "CurrentPosition",
268
+ "PositionState",
269
+ "TargetPosition",
270
+ "ObstructionDetected"
271
+ ],
272
+ "type": "accessory"
273
+ },
274
+ {
275
+ "logicalNodeAddress": 3,
276
+ "logicalAddress": 30,
277
+ "masterAddress": "192.168.0.97",
278
+ "masterPort": 5001,
279
+ "name": "dimmers",
280
+ "displayName": "dimmers",
281
+ "extendedType": 0,
282
+ "accId": "da32bb657ad68d3fd63004015406e0cbde28355fc950204cec2fcd90d835e044",
283
+ "accName": "Somfy Dimmer",
284
+ "accService": [
285
+ "On",
286
+ "Brightness"
287
+ ],
266
288
  "type": "accessory"
267
289
  }
268
290
  ]
package/duotecno/types.js CHANGED
@@ -153,7 +153,7 @@ var LinkType;
153
153
  LinkType["kNoType"] = "";
154
154
  LinkType["kAccessory"] = "accessory";
155
155
  })(LinkType = exports.LinkType || (exports.LinkType = {}));
156
- exports.kEmptyLink = Object.assign(Object.assign({}, exports.kEmptyUnit), { type: LinkType.kAccessory, accName: "acc", accId: "" });
156
+ exports.kEmptyLink = Object.assign(Object.assign({}, exports.kEmptyUnit), { type: LinkType.kAccessory, accName: "acc", accId: "", accService: [] });
157
157
  exports.kEmptyBinding = {
158
158
  channel: "0", masterAddress: "0.0.0.0", masterPort: 5001, register: 0
159
159
  };
@@ -298,6 +298,7 @@ exports.Sanitizers = {
298
298
  const link = exports.Sanitizers.unitDef(aLink);
299
299
  link.accId = aLink.accId || "";
300
300
  link.accName = aLink.accName || "";
301
+ link.accService = aLink.accService || [];
301
302
  link.type = aLink.type || LinkType.kNoType;
302
303
  return link;
303
304
  },
@@ -329,7 +330,7 @@ exports.Sanitizers = {
329
330
  return exports.Sanitizers.linkConfig({ name: aLink.name, unitName: aLink.unitName,
330
331
  masterAddress: aLink.masterAddress, masterPort: aLink.masterPort,
331
332
  logicalAddress: aLink.logicalAddress, logicalNodeAddress: aLink.logicalNodeAddress,
332
- accName: aLink.accName, accId: aLink.accId, type: aLink.type });
333
+ accName: aLink.accName, accService: aLink.accService, accId: aLink.accId, type: aLink.type });
333
334
  },
334
335
  ruleConfig(rule) {
335
336
  rule = Object.assign({}, rule);
package/duotecno/types.ts CHANGED
@@ -266,6 +266,7 @@ export enum LinkType {
266
266
  export interface Link extends UnitDef{
267
267
  accName: string;
268
268
  accId: string;
269
+ accService: Array<string>; // e.g. ["On", "Brightness"], ["TargetPosition", "CurrentPosition", ...]
269
270
  type: LinkType;
270
271
  unit?: Unit;
271
272
  status?: number | boolean;
@@ -273,8 +274,8 @@ export interface Link extends UnitDef{
273
274
  }
274
275
 
275
276
  export const kEmptyLink: Link = {
276
- ...kEmptyUnit, type: LinkType.kAccessory, accName: "acc", accId: ""
277
- } as const;
277
+ ...kEmptyUnit, type: LinkType.kAccessory, accName: "acc", accId: "", accService: []
278
+ };
278
279
 
279
280
 
280
281
  ///////////
@@ -515,6 +516,7 @@ export const Sanitizers = {
515
516
  const link = <Link>Sanitizers.unitDef(aLink);
516
517
  link.accId = aLink.accId || "";
517
518
  link.accName = aLink.accName || "";
519
+ link.accService = aLink.accService || [];
518
520
  link.type = aLink.type || LinkType.kNoType;
519
521
  return link;
520
522
  },
@@ -550,7 +552,7 @@ export const Sanitizers = {
550
552
  return Sanitizers.linkConfig({ name: aLink.name, unitName: aLink.unitName,
551
553
  masterAddress: aLink.masterAddress, masterPort: aLink.masterPort,
552
554
  logicalAddress: aLink.logicalAddress, logicalNodeAddress: aLink.logicalNodeAddress,
553
- accName: aLink.accName, accId: aLink.accId, type: aLink.type });
555
+ accName: aLink.accName, accService: aLink.accService, accId: aLink.accId, type: aLink.type });
554
556
  },
555
557
 
556
558
  ruleConfig(rule): Rule {
package/index.spec.ts CHANGED
@@ -35,7 +35,7 @@ try {
35
35
  debug("spec", config);
36
36
  config.debug = true;
37
37
 
38
- const platform = new Platform(console, config, <API>{registerPlatformAccessories: adder});
38
+ const platform = new Platform(console as any, config, <API>{registerPlatformAccessories: adder});
39
39
 
40
40
  } catch(err) {
41
41
  log("spec", err);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-smartsystem",
3
3
  "displayname": "Duotecno Bridge",
4
- "version": "6.7.0",
4
+ "version": "6.7.2",
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/HB.js CHANGED
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.hbUnsubscribe = exports.hbSubscribe = exports.hbGetState = exports.hbSetState = exports.hbLogin = exports.hbAccessories = exports.hbName = void 0;
12
+ exports.hbUnsubscribe = exports.hbSubscribe = exports.hbGetState = exports.hbSetState = exports.hbLogin = exports.hbAccessories = exports.hbService = exports.hbName = void 0;
13
13
  const logger_1 = require("../duotecno/logger");
14
14
  const HA_API_1 = require("./HA-API");
15
15
  let gAccessories = [];
@@ -19,13 +19,17 @@ function hbName(id) {
19
19
  return ((_a = gAccessories.find(item => item.id === id)) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
20
20
  }
21
21
  exports.hbName = hbName;
22
+ function hbService(id) {
23
+ var _a;
24
+ return ((_a = gAccessories.find(item => item.id === id)) === null || _a === void 0 ? void 0 : _a.service) || [];
25
+ }
26
+ exports.hbService = hbService;
22
27
  function hbAccessories(server, token) {
23
28
  return __awaiter(this, void 0, void 0, function* () {
24
29
  if (gAccessories.length === 0) {
25
30
  const list = yield (0, HA_API_1.httpRequest)(server + '/api/accessories', 'GET', "", HA_API_1.ContentType.json, {
26
31
  Authorization: 'Bearer ' + token
27
32
  });
28
- // console.log("getAccessories: " + JSON.stringify(list));
29
33
  gAccessories = list
30
34
  .filter(item => item.accessoryInformation.Manufacturer != "homebridge.io")
31
35
  .map(item => ({
@@ -33,7 +37,7 @@ function hbAccessories(server, token) {
33
37
  manufacturer: item.accessoryInformation.Manufacturer,
34
38
  id: item.uniqueId,
35
39
  type: item.type,
36
- service: ["On", "Brightness"] // TargetPosition
40
+ service: Object.keys(item.values) // ["On", "Brightness"] // TargetPosition
37
41
  }));
38
42
  }
39
43
  return gAccessories;
@@ -47,58 +51,101 @@ function hbAccessories(server, token) {
47
51
  }
48
52
  exports.hbAccessories = hbAccessories;
49
53
  /*
50
- const xxxAccessories = [
51
- { "aid": 1, "iid": 2000000008, "uuid": "000000A2-0000-1000-8000-0026BB765291", "type": "ProtocolInformation", "humanType": "Protocol Information", "serviceName": "Homebridge Valies 9839",
52
- "serviceCharacteristics": [
53
- { "aid": 1, "iid": 9, "uuid": "00000037-0000-1000-8000-0026BB765291", "type": "Version", "serviceType": "ProtocolInformation", "serviceName": "Homebridge Valies 9839", "description": "Version", "value": "1.1.0", "format": "string", "perms": ["pr"], "canRead": true, "canWrite": false, "ev": false }
54
- ],
55
- "accessoryInformation": { "Manufacturer": "homebridge.io", "Model": "homebridge", "Name": "Homebridge Valies 9839", "Serial Number": "CC:22:3D:E3:A1:01", "Firmware Revision": "2.0.0-beta.27" },
56
- "values": { "Version": "1.1.0" },
57
- "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" },
58
- "uniqueId": "2d3969b57735a9af89c45c344cce8bc5784e78530943977ce2ab757a8bea30e6" },
59
-
60
- { "aid": 41, "iid": 8, "uuid": "0000008C-0000-1000-8000-0026BB765291", "type": "WindowCovering", "humanType": "Window Covering", "serviceName": "Living",
61
- "serviceCharacteristics": [
62
- { "aid": 41, "iid": 9, "uuid": "0000006D-0000-1000-8000-0026BB765291", "type": "CurrentPosition", "serviceType": "WindowCovering", "serviceName": "Living", "description": "Current Position", "value": 0, "format": "uint8", "perms": ["ev", "pr"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
63
- { "aid": 41, "iid": 10, "uuid": "00000072-0000-1000-8000-0026BB765291", "type": "PositionState", "serviceType": "WindowCovering", "serviceName": "Living", "description": "Position State", "value": 2, "format": "uint8", "perms": ["ev", "pr"], "maxValue": 2, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
64
- { "aid": 41, "iid": 11, "uuid": "0000007C-0000-1000-8000-0026BB765291", "type": "TargetPosition", "serviceType": "WindowCovering", "serviceName": "Living", "description": "Target Position", "value": 0, "format": "uint8", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true }
65
- ],
66
- "accessoryInformation": { "Manufacturer": "Somfy", "Model": "Awning", "Name": "Living", "Serial Number": "ogp:10503758", "Firmware Revision": "0" },
67
- "values": { "CurrentPosition": 0, "PositionState": 2, "TargetPosition": 0 },
68
- "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" },
69
- "uniqueId": "06e5b66f78fe0136ea075e742a7fb388587329a4717393b21aaa180083305704"
70
- },
71
- { "aid": 42, "iid": 8, "uuid": "0000008C-0000-1000-8000-0026BB765291", "type": "WindowCovering", "humanType": "Window Covering", "serviceName": "Tim Klein",
72
- "serviceCharacteristics": [
73
- { "aid": 42, "iid": 9, "uuid": "0000006D-0000-1000-8000-0026BB765291", "type": "CurrentPosition", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Current Position", "value": 100, "format": "uint8", "perms": ["ev", "pr"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
74
- { "aid": 42, "iid": 10, "uuid": "00000072-0000-1000-8000-0026BB765291", "type": "PositionState", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Position State", "value": 2, "format": "uint8", "perms": ["ev", "pr"], "maxValue": 2, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
75
- { "aid": 42, "iid": 11, "uuid": "0000007C-0000-1000-8000-0026BB765291", "type": "TargetPosition", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Target Position", "value": 100, "format": "uint8", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true },
76
- { "aid": 42, "iid": 13, "uuid": "00000024-0000-1000-8000-0026BB765291", "type": "ObstructionDetected", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Obstruction Detected", "value": 0, "format": "bool", "perms": ["ev", "pr"], "canRead": true, "canWrite": false, "ev": true }],
77
- "accessoryInformation": { "Manufacturer": "Somfy", "Model": "Screen", "Name": "Tim Klein", "Serial Number": "ogp:10841203", "Firmware Revision": "0" },
78
- "values": { "CurrentPosition": 100, "PositionState": 2, "TargetPosition": 100, "ObstructionDetected": 0 },
79
- "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" },
80
- "uniqueId": "986b901a30d92cb7bd0205be90f65786c2d87c9a95d8a0a60cb4c048deda5ce6"
54
+ const getAccessories: [
55
+ {
56
+ "name": "Living",
57
+ "manufacturer": "Somfy",
58
+ "id": "06e5b66f78fe0136ea075e742a7fb388587329a4717393b21aaa180083305704",
59
+ "type": "WindowCovering",
60
+ "service": [
61
+ "CurrentPosition",
62
+ "PositionState",
63
+ "TargetPosition"
64
+ ]
65
+ },
66
+ {
67
+ "name": "Tim Klein",
68
+ "manufacturer": "Somfy",
69
+ "id": "986b901a30d92cb7bd0205be90f65786c2d87c9a95d8a0a60cb4c048deda5ce6",
70
+ "type": "WindowCovering",
71
+ "service": [
72
+ "CurrentPosition",
73
+ "PositionState",
74
+ "TargetPosition",
75
+ "ObstructionDetected"
76
+ ]
77
+ },
78
+ {
79
+ "name": "Keuken",
80
+ "manufacturer": "Somfy",
81
+ "id": "f18e175bdc561a8e2318662ebd225c21338623668152b23bf383849b085f340b",
82
+ "type": "WindowCovering",
83
+ "service": [
84
+ "CurrentPosition",
85
+ "PositionState",
86
+ "TargetPosition"
87
+ ]
88
+ },
89
+ {
90
+ "name": "Connectivity kit-ledBox",
91
+ "manufacturer": "Somfy",
92
+ "id": "04b9348dda8ef417605a8661c2850721975cfcbd1398c6425ab9824ec238bc96",
93
+ "type": "Lightbulb",
94
+ "service": [
95
+ "On",
96
+ "Brightness"
97
+ ]
81
98
  },
82
- { "aid": 43, "iid": 8, "uuid": "0000008C-0000-1000-8000-0026BB765291", "type": "WindowCovering", "humanType": "Window Covering", "serviceName": "Keuken",
83
- "serviceCharacteristics": [
84
- { "aid": 43, "iid": 9, "uuid": "0000006D-0000-1000-8000-0026BB765291", "type": "CurrentPosition", "serviceType": "WindowCovering", "serviceName": "Keuken", "description": "Current Position", "value": 0, "format": "uint8", "perms": ["ev", "pr"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
85
- { "aid": 43, "iid": 10, "uuid": "00000072-0000-1000-8000-0026BB765291", "type": "PositionState", "serviceType": "WindowCovering", "serviceName": "Tim Groot", "description": "Position State", "value": 2, "format": "uint8", "perms": ["ev", "pr"], "maxValue": 2, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
86
- { "aid": 45, "iid": 11, "uuid": "0000007C-0000-1000-8000-0026BB765291", "type": "TargetPosition", "serviceType": "WindowCovering", "serviceName": "Tim Groot", "description": "Target Position", "value": 100, "format": "uint8", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true },
87
- { "aid": 45, "iid": 13, "uuid": "00000024-0000-1000-8000-0026BB765291", "type": "ObstructionDetected", "serviceType": "WindowCovering", "serviceName": "Tim Groot", "description": "Obstruction Detected", "value": 0, "format": "bool", "perms": ["ev", "pr"], "canRead": true, "canWrite": false, "ev": true }],
88
- "accessoryInformation": { "Manufacturer": "Somfy", "Model": "Screen", "Name": "Tim Groot", "Serial Number": "ogp:7447159", "Firmware Revision": "0" }, "values": { "CurrentPosition": 100, "PositionState": 2, "TargetPosition": 100, "ObstructionDetected": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "81892c3728bb9e401c915ec4be9cb6cff2de98a27b0573f8396b94d386db99b0" },
89
- { "aid": 24, "iid": 8, "uuid": "00000049-0000-1000-8000-0026BB765291", "type": "Switch", "humanType": "Switch", "serviceName": "Bottom-Left",
90
- "serviceCharacteristics": [{ "aid": 24, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Switch", "serviceName": "Bottom-Left", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true }],
91
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Bottom-Left", "Name": "Bottom-Left", "Serial Number": " 20211003-N252-U7", "Firmware Revision": "0.0.0" }, "values": { "On": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "5f7746ab9fdcab159ee4806750bfcac5539df233e9dafe7ed1a568deecceb8ca" },
92
- { "aid": 46, "iid": 8, "uuid": "00000043-0000-1000-8000-0026BB765291", "type": "Lightbulb", "humanType": "Lightbulb", "serviceName": "Somfy Dimmer",
93
- "serviceCharacteristics": [{ "aid": 46, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Lightbulb", "serviceName": "Somfy Dimmer", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true },
94
- { "aid": 46, "iid": 11, "uuid": "00000008-0000-1000-8000-0026BB765291", "type": "Brightness", "serviceType": "Lightbulb", "serviceName": "Somfy Dimmer", "description": "Brightness", "value": 61, "format": "int", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true }],
95
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Somfy Dimmer", "Name": "Somfy Dimmer", "Serial Number": "SB 1-N3-U31", "Firmware Revision": "0.0.0" }, "values": { "On": 0, "Brightness": 61 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "da32bb657ad68d3fd63004015406e0cbde28355fc950204cec2fcd90d835e044" },
96
- { "aid": 22, "iid": 8, "uuid": "00000049-0000-1000-8000-0026BB765291", "type": "Switch", "humanType": "Switch", "serviceName": "Alles uitje",
97
- "serviceCharacteristics": [{ "aid": 22, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Switch", "serviceName": "Alles uitje", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true }],
98
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Alles uitje", "Name": "Alles uitje", "Serial Number": "SB 1-N3-U4", "Firmware Revision": "0.0.0" }, "values": { "On": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "f0645ea91ad7730f9c9115364ad8c3a2bd58d3fdce7bfcf00e84a7cc1017c217" },
99
- { "aid": 23, "iid": 8, "uuid": "00000049-0000-1000-8000-0026BB765291", "type": "Switch", "humanType": "Switch", "serviceName": "Alles aantje",
100
- "serviceCharacteristics": [{ "aid": 23, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Switch", "serviceName": "Alles aantje", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true }],
101
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Alles aantje", "Name": "Alles aantje", "Serial Number": "SB 1-N3-U5", "Firmware Revision": "0.0.0" }, "values": { "On": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "cdcab0c257fcd190e31d3cc736419f9fc32c4ff6e406e2d4d2cc51727d323fe9" }];
99
+ {
100
+ "name": "Tim Groot",
101
+ "manufacturer": "Somfy",
102
+ "id": "81892c3728bb9e401c915ec4be9cb6cff2de98a27b0573f8396b94d386db99b0",
103
+ "type": "WindowCovering",
104
+ "service": [
105
+ "CurrentPosition",
106
+ "PositionState",
107
+ "TargetPosition",
108
+ "ObstructionDetected"
109
+ ]
110
+ },
111
+ {
112
+ "name": "Bottom-Left",
113
+ "manufacturer": "Duotecno-Coppieters",
114
+ "id": "5f7746ab9fdcab159ee4806750bfcac5539df233e9dafe7ed1a568deecceb8ca",
115
+ "type": "Switch",
116
+ "service": [
117
+ "On"
118
+ ]
119
+ },
120
+ {
121
+ "name": "Somfy Dimmer",
122
+ "manufacturer": "Duotecno-Coppieters",
123
+ "id": "da32bb657ad68d3fd63004015406e0cbde28355fc950204cec2fcd90d835e044",
124
+ "type": "Lightbulb",
125
+ "service": [
126
+ "On",
127
+ "Brightness"
128
+ ]
129
+ },
130
+ {
131
+ "name": "Alles uitje",
132
+ "manufacturer": "Duotecno-Coppieters",
133
+ "id": "f0645ea91ad7730f9c9115364ad8c3a2bd58d3fdce7bfcf00e84a7cc1017c217",
134
+ "type": "Switch",
135
+ "service": [
136
+ "On"
137
+ ]
138
+ },
139
+ {
140
+ "name": "Alles aantje",
141
+ "manufacturer": "Duotecno-Coppieters",
142
+ "id": "cdcab0c257fcd190e31d3cc736419f9fc32c4ff6e406e2d4d2cc51727d323fe9",
143
+ "type": "Switch",
144
+ "service": [
145
+ "On"
146
+ ]
147
+ }
148
+ ]
102
149
  */
103
150
  //
104
151
  // Get a token for the Homebridge API
@@ -120,33 +167,61 @@ function hbLogin(server, username, password) {
120
167
  });
121
168
  }
122
169
  exports.hbLogin = hbLogin;
170
+ function hbLoad(value, service) {
171
+ const data = {};
172
+ if (service.includes("On")) {
173
+ data["value"] = value;
174
+ data["characteristicType"] = "On";
175
+ }
176
+ if (service.includes("Brightness")) {
177
+ data["value"] = value;
178
+ data["characteristicType"] = "Brightness";
179
+ }
180
+ if (service.includes("TargetPosition")) {
181
+ data["value"] = value;
182
+ data["characteristicType"] = "TargetPosition";
183
+ }
184
+ ;
185
+ return data;
186
+ }
187
+ function hbValue(data, service) {
188
+ const values = {};
189
+ if (service.includes("Brightness")) {
190
+ values["value"] = parseInt(data["Brightness"]);
191
+ }
192
+ if (service.includes("On")) {
193
+ values["state"] = parseInt(data["On"]);
194
+ }
195
+ if (service.includes("TargetPosition")) {
196
+ values["value"] = parseInt(data["TargetPosition"]);
197
+ }
198
+ return values;
199
+ }
123
200
  // send a status change to HB Accessory
124
- function hbSetState(server, id, value, state, token) {
201
+ function hbSetState(server, id, service, value, state, token) {
125
202
  return __awaiter(this, void 0, void 0, function* () {
126
- const data = {
127
- characteristicType: "TargetPosition",
128
- value
129
- };
130
- (0, logger_1.log)("HB", "hbSetState: " + id + " -> " + value + ", state: " + state);
203
+ const data = hbLoad(value, service);
204
+ (0, logger_1.log)("HB", "hbSetState: " + id + " -> " + JSON.stringify(data));
131
205
  const respBody = yield (0, HA_API_1.httpRequest)(server + "/api/accessories/" + id, 'PUT', JSON.stringify(data), HA_API_1.ContentType.json, {
132
206
  Authorization: 'Bearer ' + token
133
207
  });
208
+ if (respBody && respBody.error) {
209
+ (0, logger_1.log)("HB", "hbSetState: " + id + " -> error: " + JSON.stringify(respBody));
210
+ }
134
211
  });
135
212
  }
136
213
  exports.hbSetState = hbSetState;
137
214
  // send a status change to HB Accessory
138
- function hbGetState(server, id, token) {
215
+ function hbGetState(server, id, service, token) {
139
216
  return __awaiter(this, void 0, void 0, function* () {
140
- const data = {
141
- characteristicType: "TargetPosition",
142
- };
143
217
  const respBody = yield (0, HA_API_1.httpRequest)(server + "/api/accessories/" + id, 'GET', "", HA_API_1.ContentType.json, {
144
218
  Authorization: 'Bearer ' + token
145
219
  });
146
- // get the value strings from the gAccessories !! TODO
147
- const value = parseInt(respBody.values.TargetPosition);
148
- const state = parseInt(respBody.values.PositionState);
149
- return { value, state };
220
+ if (respBody && respBody.error) {
221
+ (0, logger_1.log)("HB", "hbGetState: " + id + " -> error: " + JSON.stringify(respBody));
222
+ return {};
223
+ }
224
+ return hbValue(respBody.values, service);
150
225
  });
151
226
  }
152
227
  exports.hbGetState = hbGetState;
package/server/HB.ts CHANGED
@@ -1,20 +1,32 @@
1
1
  import { err, log } from "../duotecno/logger";
2
2
  import { ContentType, httpRequest } from "./HA-API";
3
3
 
4
- let gAccessories = [];
4
+ export interface HBAccessory {
5
+ name: string,
6
+ manufacturer: string,
7
+ id: string,
8
+ type: string,
9
+ service: Array<string>
10
+ // Dimmers: [On, Brightness],
11
+ // Switch: [On],
12
+ // WindowCovering: [TargetPosition, PositionState, ObstructionDetected, CurrentPosition]
13
+ }
14
+ let gAccessories: Array<HBAccessory> = [];
5
15
  let timer = null;
6
16
 
7
17
  export function hbName(id: string): string {
8
18
  return gAccessories.find(item => item.id === id)?.name || "Unknown";
9
19
  }
20
+ export function hbService(id: string): Array<string> {
21
+ return gAccessories.find(item => item.id === id)?.service || [];
22
+ }
10
23
 
11
- export async function hbAccessories(server: string, token: string): Promise<Array<{name: string, manufacturer: string, id: string, type: string}>> {
24
+ export async function hbAccessories(server: string, token: string): Promise<Array<HBAccessory>> {
12
25
  if (gAccessories.length === 0) {
13
26
 
14
27
  const list = await httpRequest(server+'/api/accessories', 'GET', "", ContentType.json, {
15
28
  Authorization: 'Bearer ' + token
16
29
  });
17
- // console.log("getAccessories: " + JSON.stringify(list));
18
30
 
19
31
  gAccessories = list
20
32
  .filter(item =>
@@ -25,7 +37,7 @@ export async function hbAccessories(server: string, token: string): Promise<Arra
25
37
  manufacturer: item.accessoryInformation.Manufacturer,
26
38
  id: item.uniqueId,
27
39
  type: item.type,
28
- service: ["On", "Brightness"] // TargetPosition
40
+ service: Object.keys(item.values) // ["On", "Brightness"] // TargetPosition
29
41
  })
30
42
  );
31
43
  }
@@ -41,58 +53,101 @@ export async function hbAccessories(server: string, token: string): Promise<Arra
41
53
  }
42
54
 
43
55
  /*
44
- const xxxAccessories = [
45
- { "aid": 1, "iid": 2000000008, "uuid": "000000A2-0000-1000-8000-0026BB765291", "type": "ProtocolInformation", "humanType": "Protocol Information", "serviceName": "Homebridge Valies 9839",
46
- "serviceCharacteristics": [
47
- { "aid": 1, "iid": 9, "uuid": "00000037-0000-1000-8000-0026BB765291", "type": "Version", "serviceType": "ProtocolInformation", "serviceName": "Homebridge Valies 9839", "description": "Version", "value": "1.1.0", "format": "string", "perms": ["pr"], "canRead": true, "canWrite": false, "ev": false }
48
- ],
49
- "accessoryInformation": { "Manufacturer": "homebridge.io", "Model": "homebridge", "Name": "Homebridge Valies 9839", "Serial Number": "CC:22:3D:E3:A1:01", "Firmware Revision": "2.0.0-beta.27" },
50
- "values": { "Version": "1.1.0" },
51
- "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" },
52
- "uniqueId": "2d3969b57735a9af89c45c344cce8bc5784e78530943977ce2ab757a8bea30e6" },
53
-
54
- { "aid": 41, "iid": 8, "uuid": "0000008C-0000-1000-8000-0026BB765291", "type": "WindowCovering", "humanType": "Window Covering", "serviceName": "Living",
55
- "serviceCharacteristics": [
56
- { "aid": 41, "iid": 9, "uuid": "0000006D-0000-1000-8000-0026BB765291", "type": "CurrentPosition", "serviceType": "WindowCovering", "serviceName": "Living", "description": "Current Position", "value": 0, "format": "uint8", "perms": ["ev", "pr"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
57
- { "aid": 41, "iid": 10, "uuid": "00000072-0000-1000-8000-0026BB765291", "type": "PositionState", "serviceType": "WindowCovering", "serviceName": "Living", "description": "Position State", "value": 2, "format": "uint8", "perms": ["ev", "pr"], "maxValue": 2, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
58
- { "aid": 41, "iid": 11, "uuid": "0000007C-0000-1000-8000-0026BB765291", "type": "TargetPosition", "serviceType": "WindowCovering", "serviceName": "Living", "description": "Target Position", "value": 0, "format": "uint8", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true }
59
- ],
60
- "accessoryInformation": { "Manufacturer": "Somfy", "Model": "Awning", "Name": "Living", "Serial Number": "ogp:10503758", "Firmware Revision": "0" },
61
- "values": { "CurrentPosition": 0, "PositionState": 2, "TargetPosition": 0 },
62
- "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" },
63
- "uniqueId": "06e5b66f78fe0136ea075e742a7fb388587329a4717393b21aaa180083305704"
64
- },
65
- { "aid": 42, "iid": 8, "uuid": "0000008C-0000-1000-8000-0026BB765291", "type": "WindowCovering", "humanType": "Window Covering", "serviceName": "Tim Klein",
66
- "serviceCharacteristics": [
67
- { "aid": 42, "iid": 9, "uuid": "0000006D-0000-1000-8000-0026BB765291", "type": "CurrentPosition", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Current Position", "value": 100, "format": "uint8", "perms": ["ev", "pr"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
68
- { "aid": 42, "iid": 10, "uuid": "00000072-0000-1000-8000-0026BB765291", "type": "PositionState", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Position State", "value": 2, "format": "uint8", "perms": ["ev", "pr"], "maxValue": 2, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
69
- { "aid": 42, "iid": 11, "uuid": "0000007C-0000-1000-8000-0026BB765291", "type": "TargetPosition", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Target Position", "value": 100, "format": "uint8", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true },
70
- { "aid": 42, "iid": 13, "uuid": "00000024-0000-1000-8000-0026BB765291", "type": "ObstructionDetected", "serviceType": "WindowCovering", "serviceName": "Tim Klein", "description": "Obstruction Detected", "value": 0, "format": "bool", "perms": ["ev", "pr"], "canRead": true, "canWrite": false, "ev": true }],
71
- "accessoryInformation": { "Manufacturer": "Somfy", "Model": "Screen", "Name": "Tim Klein", "Serial Number": "ogp:10841203", "Firmware Revision": "0" },
72
- "values": { "CurrentPosition": 100, "PositionState": 2, "TargetPosition": 100, "ObstructionDetected": 0 },
73
- "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" },
74
- "uniqueId": "986b901a30d92cb7bd0205be90f65786c2d87c9a95d8a0a60cb4c048deda5ce6"
75
- },
76
- { "aid": 43, "iid": 8, "uuid": "0000008C-0000-1000-8000-0026BB765291", "type": "WindowCovering", "humanType": "Window Covering", "serviceName": "Keuken",
77
- "serviceCharacteristics": [
78
- { "aid": 43, "iid": 9, "uuid": "0000006D-0000-1000-8000-0026BB765291", "type": "CurrentPosition", "serviceType": "WindowCovering", "serviceName": "Keuken", "description": "Current Position", "value": 0, "format": "uint8", "perms": ["ev", "pr"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
79
- { "aid": 43, "iid": 10, "uuid": "00000072-0000-1000-8000-0026BB765291", "type": "PositionState", "serviceType": "WindowCovering", "serviceName": "Tim Groot", "description": "Position State", "value": 2, "format": "uint8", "perms": ["ev", "pr"], "maxValue": 2, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": false, "ev": true },
80
- { "aid": 45, "iid": 11, "uuid": "0000007C-0000-1000-8000-0026BB765291", "type": "TargetPosition", "serviceType": "WindowCovering", "serviceName": "Tim Groot", "description": "Target Position", "value": 100, "format": "uint8", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true },
81
- { "aid": 45, "iid": 13, "uuid": "00000024-0000-1000-8000-0026BB765291", "type": "ObstructionDetected", "serviceType": "WindowCovering", "serviceName": "Tim Groot", "description": "Obstruction Detected", "value": 0, "format": "bool", "perms": ["ev", "pr"], "canRead": true, "canWrite": false, "ev": true }],
82
- "accessoryInformation": { "Manufacturer": "Somfy", "Model": "Screen", "Name": "Tim Groot", "Serial Number": "ogp:7447159", "Firmware Revision": "0" }, "values": { "CurrentPosition": 100, "PositionState": 2, "TargetPosition": 100, "ObstructionDetected": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "81892c3728bb9e401c915ec4be9cb6cff2de98a27b0573f8396b94d386db99b0" },
83
- { "aid": 24, "iid": 8, "uuid": "00000049-0000-1000-8000-0026BB765291", "type": "Switch", "humanType": "Switch", "serviceName": "Bottom-Left",
84
- "serviceCharacteristics": [{ "aid": 24, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Switch", "serviceName": "Bottom-Left", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true }],
85
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Bottom-Left", "Name": "Bottom-Left", "Serial Number": " 20211003-N252-U7", "Firmware Revision": "0.0.0" }, "values": { "On": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "5f7746ab9fdcab159ee4806750bfcac5539df233e9dafe7ed1a568deecceb8ca" },
86
- { "aid": 46, "iid": 8, "uuid": "00000043-0000-1000-8000-0026BB765291", "type": "Lightbulb", "humanType": "Lightbulb", "serviceName": "Somfy Dimmer",
87
- "serviceCharacteristics": [{ "aid": 46, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Lightbulb", "serviceName": "Somfy Dimmer", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true },
88
- { "aid": 46, "iid": 11, "uuid": "00000008-0000-1000-8000-0026BB765291", "type": "Brightness", "serviceType": "Lightbulb", "serviceName": "Somfy Dimmer", "description": "Brightness", "value": 61, "format": "int", "perms": ["ev", "pr", "pw"], "unit": "percentage", "maxValue": 100, "minValue": 0, "minStep": 1, "canRead": true, "canWrite": true, "ev": true }],
89
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Somfy Dimmer", "Name": "Somfy Dimmer", "Serial Number": "SB 1-N3-U31", "Firmware Revision": "0.0.0" }, "values": { "On": 0, "Brightness": 61 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "da32bb657ad68d3fd63004015406e0cbde28355fc950204cec2fcd90d835e044" },
90
- { "aid": 22, "iid": 8, "uuid": "00000049-0000-1000-8000-0026BB765291", "type": "Switch", "humanType": "Switch", "serviceName": "Alles uitje",
91
- "serviceCharacteristics": [{ "aid": 22, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Switch", "serviceName": "Alles uitje", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true }],
92
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Alles uitje", "Name": "Alles uitje", "Serial Number": "SB 1-N3-U4", "Firmware Revision": "0.0.0" }, "values": { "On": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "f0645ea91ad7730f9c9115364ad8c3a2bd58d3fdce7bfcf00e84a7cc1017c217" },
93
- { "aid": 23, "iid": 8, "uuid": "00000049-0000-1000-8000-0026BB765291", "type": "Switch", "humanType": "Switch", "serviceName": "Alles aantje",
94
- "serviceCharacteristics": [{ "aid": 23, "iid": 10, "uuid": "00000025-0000-1000-8000-0026BB765291", "type": "On", "serviceType": "Switch", "serviceName": "Alles aantje", "description": "On", "value": 0, "format": "bool", "perms": ["ev", "pr", "pw"], "canRead": true, "canWrite": true, "ev": true }],
95
- "accessoryInformation": { "Manufacturer": "Duotecno-Coppieters", "Model": "Alles aantje", "Name": "Alles aantje", "Serial Number": "SB 1-N3-U5", "Firmware Revision": "0.0.0" }, "values": { "On": 0 }, "instance": { "name": "homebridge", "username": "CC:22:3D:E3:A1:01", "ipAddress": "127.0.0.1", "port": 51827, "services": [], "connectionFailedCount": 0, "configurationNumber": "15" }, "uniqueId": "cdcab0c257fcd190e31d3cc736419f9fc32c4ff6e406e2d4d2cc51727d323fe9" }];
56
+ const getAccessories: [
57
+ {
58
+ "name": "Living",
59
+ "manufacturer": "Somfy",
60
+ "id": "06e5b66f78fe0136ea075e742a7fb388587329a4717393b21aaa180083305704",
61
+ "type": "WindowCovering",
62
+ "service": [
63
+ "CurrentPosition",
64
+ "PositionState",
65
+ "TargetPosition"
66
+ ]
67
+ },
68
+ {
69
+ "name": "Tim Klein",
70
+ "manufacturer": "Somfy",
71
+ "id": "986b901a30d92cb7bd0205be90f65786c2d87c9a95d8a0a60cb4c048deda5ce6",
72
+ "type": "WindowCovering",
73
+ "service": [
74
+ "CurrentPosition",
75
+ "PositionState",
76
+ "TargetPosition",
77
+ "ObstructionDetected"
78
+ ]
79
+ },
80
+ {
81
+ "name": "Keuken",
82
+ "manufacturer": "Somfy",
83
+ "id": "f18e175bdc561a8e2318662ebd225c21338623668152b23bf383849b085f340b",
84
+ "type": "WindowCovering",
85
+ "service": [
86
+ "CurrentPosition",
87
+ "PositionState",
88
+ "TargetPosition"
89
+ ]
90
+ },
91
+ {
92
+ "name": "Connectivity kit-ledBox",
93
+ "manufacturer": "Somfy",
94
+ "id": "04b9348dda8ef417605a8661c2850721975cfcbd1398c6425ab9824ec238bc96",
95
+ "type": "Lightbulb",
96
+ "service": [
97
+ "On",
98
+ "Brightness"
99
+ ]
100
+ },
101
+ {
102
+ "name": "Tim Groot",
103
+ "manufacturer": "Somfy",
104
+ "id": "81892c3728bb9e401c915ec4be9cb6cff2de98a27b0573f8396b94d386db99b0",
105
+ "type": "WindowCovering",
106
+ "service": [
107
+ "CurrentPosition",
108
+ "PositionState",
109
+ "TargetPosition",
110
+ "ObstructionDetected"
111
+ ]
112
+ },
113
+ {
114
+ "name": "Bottom-Left",
115
+ "manufacturer": "Duotecno-Coppieters",
116
+ "id": "5f7746ab9fdcab159ee4806750bfcac5539df233e9dafe7ed1a568deecceb8ca",
117
+ "type": "Switch",
118
+ "service": [
119
+ "On"
120
+ ]
121
+ },
122
+ {
123
+ "name": "Somfy Dimmer",
124
+ "manufacturer": "Duotecno-Coppieters",
125
+ "id": "da32bb657ad68d3fd63004015406e0cbde28355fc950204cec2fcd90d835e044",
126
+ "type": "Lightbulb",
127
+ "service": [
128
+ "On",
129
+ "Brightness"
130
+ ]
131
+ },
132
+ {
133
+ "name": "Alles uitje",
134
+ "manufacturer": "Duotecno-Coppieters",
135
+ "id": "f0645ea91ad7730f9c9115364ad8c3a2bd58d3fdce7bfcf00e84a7cc1017c217",
136
+ "type": "Switch",
137
+ "service": [
138
+ "On"
139
+ ]
140
+ },
141
+ {
142
+ "name": "Alles aantje",
143
+ "manufacturer": "Duotecno-Coppieters",
144
+ "id": "cdcab0c257fcd190e31d3cc736419f9fc32c4ff6e406e2d4d2cc51727d323fe9",
145
+ "type": "Switch",
146
+ "service": [
147
+ "On"
148
+ ]
149
+ }
150
+ ]
96
151
  */
97
152
 
98
153
 
@@ -115,32 +170,65 @@ export async function hbLogin(server: string, username: string, password: string
115
170
  }
116
171
  }
117
172
 
173
+ function hbLoad(value: number | boolean, service: Array<string>): Record<string, number | boolean> {
174
+ const data = {};
175
+
176
+ if (service.includes("On")) {
177
+ data["value"] = value;
178
+ data["characteristicType"] = "On";
179
+ }
180
+ if (service.includes("Brightness")) {
181
+ data["value"] = value;
182
+ data["characteristicType"] = "Brightness";
183
+ }
184
+ if (service.includes("TargetPosition")) {
185
+ data["value"] = value;
186
+ data["characteristicType"] = "TargetPosition";
187
+ };
188
+ return data;
189
+ }
190
+
191
+ function hbValue(data: Record<string, any>, service: Array<string>): {value?: number, state?: number} {
192
+ const values = {};
193
+ if (service.includes("Brightness")) {
194
+ values["value"] = parseInt(data["Brightness"]);
195
+ }
196
+
197
+ if (service.includes("On")) {
198
+ values["state"] = parseInt(data["On"])
199
+ }
200
+
201
+ if (service.includes("TargetPosition")) {
202
+ values["value"] = parseInt(data["TargetPosition"]);
203
+ }
204
+ return values
205
+ }
118
206
 
119
207
  // send a status change to HB Accessory
120
- export async function hbSetState(server: string, id: string, value: number | boolean, state: number, token: string) : Promise<void> {
121
- const data = {
122
- characteristicType: "TargetPosition",
123
- value
124
- };
125
- log("HB", "hbSetState: " + id + " -> " + value + ", state: " + state);
126
- const respBody = await httpRequest(server + "/api/accessories/"+id, 'PUT', JSON.stringify(data), ContentType.json, {
127
- Authorization: 'Bearer ' + token
128
- });
208
+ export async function hbSetState(server: string, id: string, service: Array<string>, value: number | boolean, state: number, token: string) : Promise<void> {
209
+ const data = hbLoad(value, service);
210
+
211
+ log("HB", "hbSetState: " + id + " -> " + JSON.stringify(data));
212
+ const respBody = await httpRequest(server + "/api/accessories/"+id, 'PUT', JSON.stringify(data), ContentType.json, {
213
+ Authorization: 'Bearer ' + token
214
+ });
215
+
216
+ if (respBody && respBody.error) {
217
+ log("HB", "hbSetState: " + id + " -> error: " + JSON.stringify(respBody))
218
+ }
129
219
  }
130
220
 
131
221
  // send a status change to HB Accessory
132
- export async function hbGetState(server: string, id: string, token: string) : Promise<{value: number, state: number}> {
133
- const data = {
134
- characteristicType: "TargetPosition",
135
- };
222
+ export async function hbGetState(server: string, id: string, service: Array<string>, token: string) : Promise<{value?: number, state?: number}> {
136
223
  const respBody = await httpRequest(server + "/api/accessories/"+id, 'GET', "", ContentType.json, {
137
224
  Authorization: 'Bearer ' + token
138
225
  });
226
+ if (respBody && respBody.error) {
227
+ log("HB", "hbGetState: " + id + " -> error: " + JSON.stringify(respBody));
228
+ return {};
229
+ }
139
230
 
140
- // get the value strings from the gAccessories !! TODO
141
- const value = parseInt(respBody.values.TargetPosition);
142
- const state = parseInt(respBody.values.PositionState);
143
- return {value, state};
231
+ return hbValue(respBody.values, service);
144
232
  }
145
233
 
146
234
  export function hbSubscribe(callback) {
@@ -56,7 +56,8 @@ class SmartApp extends webapp_1.WebApp {
56
56
  this.system.emitter.on('plug', this.alertSwitch.bind(this));
57
57
  // get some configurated params
58
58
  this.port = this.config.port || this.port || 80;
59
- this.password = platform.config.password;
59
+ this.password = platform.config.password || this.config.password || "";
60
+ this.user = platform.config.user || this.config.user || "pi";
60
61
  this.system = system;
61
62
  this.power = power;
62
63
  this.platform = platform;
@@ -118,6 +119,10 @@ class SmartApp extends webapp_1.WebApp {
118
119
  try {
119
120
  const access_token = yield this.checkLogin(this.config.apiHost + ":" + this.config.apiPort, user, pw);
120
121
  if (access_token) {
122
+ // save username / password in config
123
+ this.config.username = user;
124
+ this.config.password = pw;
125
+ this.writeConfig();
121
126
  this.token = access_token;
122
127
  (0, config_1.setToken)(this.token);
123
128
  return true;
@@ -403,12 +408,12 @@ class SmartApp extends webapp_1.WebApp {
403
408
  if (this.links.length && (yield this.checkToken())) {
404
409
  for (const link of this.links) {
405
410
  if ((link.type === types_1.LinkType.kAccessory) && link.unit) {
406
- const { value, state } = yield (0, HB_1.hbGetState)(this.config.apiHost + ":" + this.config.apiPort, link.accId, this.token);
411
+ const { value, state } = yield (0, HB_1.hbGetState)(this.config.apiHost + ":" + this.config.apiPort, link.accId, link.accService, this.token);
407
412
  (0, logger_1.debug)("smartapp", "Received HB value for accessory: " + link.accName + " -> " + value + " / " + state + " from accessory");
408
- if (value !== link.value) { // || state !== link.status
413
+ if ((typeof value != "undefined") && (value !== link.value)) { // || state !== link.status
409
414
  (0, logger_1.log)("smartapp", " -> Setting value of Duotecno unit: " + link.unit.getDisplayName() + " to " + value + " of HB accessory: " + link.accName);
410
415
  link.value = value;
411
- link.status = state;
416
+ // link.status = state;
412
417
  link.unit.setState(value);
413
418
  }
414
419
  else {
@@ -428,7 +433,7 @@ class SmartApp extends webapp_1.WebApp {
428
433
  (0, logger_1.log)("smartapp", " -> Setting HB value for accessory: " + link.accName + " to " + u.value + " / " + u.status + " of Duotecno unit: " + u.getDisplayName());
429
434
  link.value = u.value;
430
435
  link.status = u.status;
431
- yield (0, HB_1.hbSetState)(this.config.apiHost + ":" + this.config.apiPort, link.accId, u.value, u.status, this.token);
436
+ yield (0, HB_1.hbSetState)(this.config.apiHost + ":" + this.config.apiPort, link.accId, link.accService, u.value, u.status, this.token);
432
437
  }
433
438
  else {
434
439
  (0, logger_1.debug)("smartapp", " -> No change for accessory: " + link.accName + " to " + u.value + " / " + u.status);
@@ -503,11 +508,12 @@ class SmartApp extends webapp_1.WebApp {
503
508
  // uniqiue id for the accessory
504
509
  const accId = context.getParam({ name: "accId", type: "string", default: "" });
505
510
  const accName = (0, HB_1.hbName)(accId);
506
- return { accId, accName, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
511
+ const accService = (0, HB_1.hbService)(accId);
512
+ return { accId, accName, accService, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
507
513
  }
508
- updateLink(inx, swtch) {
514
+ updateLink(inx, link) {
509
515
  if ((inx >= 0) && (inx < this.links.length)) {
510
- this.links[inx] = swtch;
516
+ this.links[inx] = link;
511
517
  this.initLinks();
512
518
  this.writeConfig();
513
519
  }
@@ -548,7 +554,7 @@ class SmartApp extends webapp_1.WebApp {
548
554
  else {
549
555
  if (link.type == types_1.LinkType.kAccessory) {
550
556
  if (yield this.checkToken())
551
- yield (0, HB_1.hbSetState)(this.config.apiHost + ":" + this.config.apiPort, link.id, link.unit.value, link.unit.status, this.token);
557
+ yield (0, HB_1.hbSetState)(this.config.apiHost + ":" + this.config.apiPort, link.id, link.service, link.unit.value, link.unit.status, this.token);
552
558
  }
553
559
  else {
554
560
  (0, logger_1.err)("smartapp", "Don't know how to set a link of type " + link.type);
@@ -22,7 +22,7 @@ import { Shelly } from './shelly';
22
22
  import { P1 } from './p1';
23
23
  import { setToken } from "../duotecno/config";
24
24
  import { PowerBase } from "./powerbase";
25
- import { hbAccessories, hbGetState, hbLogin, hbName, hbSetState, hbSubscribe } from "./HB";
25
+ import { hbAccessories, hbGetState, hbLogin, hbName, hbService, hbSetState, hbSubscribe } from "./HB";
26
26
  import { httpRequest, ContentType } from "./HA-API";
27
27
 
28
28
  const kMaster = {name: "master", type: "string", default: "0.0.0.0:5001"} as const;
@@ -72,7 +72,8 @@ export class SmartApp extends WebApp {
72
72
 
73
73
  // get some configurated params
74
74
  this.port = this.config.port || this.port || 80;
75
- this.password = platform.config.password;
75
+ this.password = platform.config.password || this.config.password || "";
76
+ this.user = platform.config.user || this.config.user || "pi";
76
77
 
77
78
  this.system = system;
78
79
  this.power = power;
@@ -145,6 +146,11 @@ export class SmartApp extends WebApp {
145
146
  try {
146
147
  const access_token = await this.checkLogin(this.config.apiHost +":"+ this.config.apiPort, user, pw);
147
148
  if (access_token) {
149
+ // save username / password in config
150
+ this.config.username = user;
151
+ this.config.password = pw;
152
+ this.writeConfig();
153
+
148
154
  this.token = access_token;
149
155
  setToken(this.token);
150
156
  return true;
@@ -453,12 +459,12 @@ export class SmartApp extends WebApp {
453
459
  if (this.links.length && await this.checkToken()) {
454
460
  for (const link of this.links) {
455
461
  if ((link.type === LinkType.kAccessory) && link.unit) {
456
- const {value, state} = await hbGetState(this.config.apiHost +":"+ this.config.apiPort, link.accId, this.token);
462
+ const {value, state} = await hbGetState(this.config.apiHost +":"+ this.config.apiPort, link.accId, link.accService, this.token);
457
463
  debug("smartapp", "Received HB value for accessory: " + link.accName + " -> " + value + " / " + state + " from accessory");
458
- if (value !== link.value) { // || state !== link.status
464
+ if ((typeof value != "undefined") && (value !== link.value)) { // || state !== link.status
459
465
  log("smartapp", " -> Setting value of Duotecno unit: " + link.unit.getDisplayName() + " to " + value + " of HB accessory: " + link.accName);
460
466
  link.value = value;
461
- link.status = state;
467
+ // link.status = state;
462
468
  link.unit.setState(value);
463
469
  } else {
464
470
  debug("smartapp", " -> No change for unit: " + link.unit.getDisplayName() + " to " + value);
@@ -478,7 +484,7 @@ export class SmartApp extends WebApp {
478
484
  log("smartapp", " -> Setting HB value for accessory: " + link.accName + " to " + u.value + " / " + u.status + " of Duotecno unit: " + u.getDisplayName());
479
485
  link.value = u.value;
480
486
  link.status = u.status;
481
- await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.accId, u.value, u.status, this.token);
487
+ await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.accId, link.accService, u.value, u.status, this.token);
482
488
 
483
489
  } else {
484
490
  debug("smartapp", " -> No change for accessory: " + link.accName + " to " + u.value + " / " + u.status);
@@ -555,12 +561,13 @@ export class SmartApp extends WebApp {
555
561
  // uniqiue id for the accessory
556
562
  const accId = context.getParam({name: "accId", type: "string", default: "" });
557
563
  const accName = hbName(accId);
558
- return { accId, accName, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
564
+ const accService = hbService(accId);
565
+ return { accId, accName, accService, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
559
566
  }
560
567
 
561
- updateLink(inx: number, swtch: Link) {
568
+ updateLink(inx: number, link: Link) {
562
569
  if ((inx >= 0) && (inx < this.links.length)) {
563
- this.links[inx] = swtch;
570
+ this.links[inx] = link;
564
571
  this.initLinks();
565
572
  this.writeConfig();
566
573
  }
@@ -608,7 +615,7 @@ export class SmartApp extends WebApp {
608
615
  } else {
609
616
  if (link.type == LinkType.kAccessory) {
610
617
  if (await this.checkToken())
611
- await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.id, link.unit.value, link.unit.status, this.token)
618
+ await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.id, link.service, link.unit.value, link.unit.status, this.token)
612
619
 
613
620
  } else {
614
621
  err("smartapp", "Don't know how to set a link of type " + link.type);
@@ -33,7 +33,7 @@
33
33
 
34
34
  <div class="row">
35
35
  <div class="input-field col s12">
36
- <input type="text" name="user" id="user" value="" />
36
+ <input type="text" name="user" id="user" value="<%= user %>" />
37
37
  <label for="user">Username</label>
38
38
  </div>
39
39
  </div>
@@ -54,4 +54,16 @@
54
54
 
55
55
  </body>
56
56
  <%- include('/views/scripts'); -%>
57
+ <script>
58
+ // select password field if username is filled
59
+ document.addEventListener('DOMContentLoaded', function() {
60
+ var user = document.getElementById('user');
61
+ var password = document.getElementById('password');
62
+ if (user.value) {
63
+ setTimeout(password.focus.bind(password), 100);
64
+ } else {
65
+ user.focus();
66
+ }
67
+ });
68
+ </script>
57
69
  </html>
package/server/webapp.js CHANGED
@@ -384,7 +384,7 @@ class WebApp extends base_1.Base {
384
384
  return this.doTryLogin(context);
385
385
  }
386
386
  else {
387
- return this.ejs("login", context, {});
387
+ return this.ejs("login", context, { user: this.user });
388
388
  }
389
389
  });
390
390
  }
package/server/webapp.ts CHANGED
@@ -444,7 +444,7 @@ export class WebApp extends Base {
444
444
  return this.doTryLogin(context);
445
445
 
446
446
  } else {
447
- return this.ejs("login", context, {});
447
+ return this.ejs("login", context, {user: this.user});
448
448
  }
449
449
  }
450
450
  async doTryLogin(context: Context): Promise<HttpResponse> {