homebridge-smartsystem 6.7.1 → 6.7.3

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
@@ -296,11 +296,15 @@ Homebridge UI version
296
296
  ### v6.7.0 - 19/06/2025
297
297
  - 0: implemented "links" between HB accessories and Duotecno units.
298
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
299
301
 
300
302
  ## Hardware
301
303
  A Raspberry Pi that connects to a Duotecno IP Node
302
304
  and (if configured) to a Smappee Infinity (power and plugs)
303
305
  and to http switches (bi-directional)
306
+ and to openHAB http (from v5.1)
307
+ and linked to HB accessories (from v6.7)
304
308
 
305
309
 
306
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
  ]
@@ -41,13 +41,15 @@ var Cmd;
41
41
  Cmd[Cmd["Heartbeat"] = 215] = "Heartbeat";
42
42
  Cmd[Cmd["DatabaseInfo"] = 209] = "DatabaseInfo";
43
43
  Cmd[Cmd["SetSchedule"] = 217] = "SetSchedule";
44
+ Cmd[Cmd["reqSchedule"] = 218] = "reqSchedule";
45
+ Cmd[Cmd["reqNodeManagement"] = 220] = "reqNodeManagement";
44
46
  Cmd[Cmd["Register"] = 224] = "Register";
45
47
  })(Cmd || (Cmd = {}));
46
- // for Set Switch/Dimmer/Control
48
+ // for Set Switch/Dimmer/Control/Motor/Sensor = 162, 163, 168, 182, 136
47
49
  const reqDim = 3;
48
50
  const reqOff = 9;
49
51
  const reqOn = 10;
50
- // for Login
52
+ // for Login = 214
51
53
  const reqDisconnect = 0;
52
54
  const reqConnect = 3;
53
55
  // for DatabaseInfo = 209;
@@ -55,10 +57,7 @@ const reqDBInfo = 0;
55
57
  const reqNodeInfo = 1;
56
58
  const reqUnitInfo = 2;
57
59
  const reqUnitStatus = 3;
58
- // for Schedule
59
- const reqSchedule = 218;
60
- // for versions et al
61
- const reqNodeManagement = 220;
60
+ // for reqNodeMgt, versions et al
62
61
  var reqNodeAttributes;
63
62
  (function (reqNodeAttributes) {
64
63
  reqNodeAttributes[reqNodeAttributes["masterSupported"] = 0] = "masterSupported";
@@ -671,10 +670,10 @@ exports.Protocol = {
671
670
  return [Cmd.DatabaseInfo, reqUnitStatus, node.logicalAddress, unit.logicalAddress, unit.type];
672
671
  },
673
672
  buildRequestSchedule() {
674
- return [reqSchedule, 0];
673
+ return [Cmd.reqSchedule, 0];
675
674
  },
676
675
  buildRequestNodeMgt(method = reqNodeAttributes.nodeProtocol) {
677
- return [reqNodeManagement, method];
676
+ return [Cmd.reqNodeManagement, method];
678
677
  },
679
678
  getCmdAndMethod: function (unit, value) {
680
679
  switch (unit.type) {
@@ -38,15 +38,17 @@ enum Cmd {
38
38
  Heartbeat = 215,
39
39
  DatabaseInfo = 209,
40
40
  SetSchedule = 217,
41
+ reqSchedule = 218,
42
+ reqNodeManagement = 220,
41
43
  Register = 224
42
44
  }
43
45
 
44
- // for Set Switch/Dimmer/Control
46
+ // for Set Switch/Dimmer/Control/Motor/Sensor = 162, 163, 168, 182, 136
45
47
  const reqDim = 3;
46
48
  const reqOff = 9;
47
49
  const reqOn = 10;
48
50
 
49
- // for Login
51
+ // for Login = 214
50
52
  const reqDisconnect = 0;
51
53
  const reqConnect = 3;
52
54
 
@@ -56,11 +58,7 @@ const reqNodeInfo = 1;
56
58
  const reqUnitInfo = 2;
57
59
  const reqUnitStatus = 3;
58
60
 
59
- // for Schedule
60
- const reqSchedule = 218;
61
-
62
- // for versions et al
63
- const reqNodeManagement = 220;
61
+ // for reqNodeMgt, versions et al
64
62
  export enum reqNodeAttributes {
65
63
  masterSupported = 0,
66
64
  isMaster = 4,
@@ -782,10 +780,10 @@ export const Protocol = {
782
780
  },
783
781
 
784
782
  buildRequestSchedule(): Message {
785
- return [reqSchedule, 0];
783
+ return [Cmd.reqSchedule, 0];
786
784
  },
787
785
  buildRequestNodeMgt(method = reqNodeAttributes.nodeProtocol): Message {
788
- return [reqNodeManagement, method];
786
+ return [Cmd.reqNodeManagement, method];
789
787
  },
790
788
 
791
789
  getCmdAndMethod: function(unit: Unit, value: number | boolean): CmdSet {
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.1",
4
+ "version": "6.7.3",
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
+ ]
98
+ },
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
+ ]
81
119
  },
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" }];
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,84 @@ function hbLogin(server, username, password) {
120
167
  });
121
168
  }
122
169
  exports.hbLogin = hbLogin;
170
+ function hbLoad(value, service) {
171
+ function hbvalue(value) {
172
+ if (typeof value === "boolean") {
173
+ return (value) ? 100 : 0;
174
+ }
175
+ else if (typeof value === "number") {
176
+ if (value <= 2) {
177
+ return 0;
178
+ }
179
+ else if (value >= 98) {
180
+ return 100;
181
+ }
182
+ else {
183
+ return Math.round(value);
184
+ }
185
+ }
186
+ }
187
+ const data = {};
188
+ if (service.includes("On")) {
189
+ data["value"] = value;
190
+ data["characteristicType"] = "On";
191
+ }
192
+ if (service.includes("Brightness")) {
193
+ data["value"] = hbvalue(value);
194
+ data["characteristicType"] = "Brightness";
195
+ }
196
+ if (service.includes("TargetPosition")) {
197
+ data["value"] = hbvalue(value);
198
+ data["characteristicType"] = "TargetPosition";
199
+ }
200
+ ;
201
+ return data;
202
+ }
203
+ function hbValue(data, service) {
204
+ const values = {};
205
+ if (service.includes("Brightness")) {
206
+ values["value"] = parseInt(data["Brightness"]);
207
+ }
208
+ if (service.includes("On")) {
209
+ values["state"] = parseInt(data["On"]);
210
+ }
211
+ if (service.includes("TargetPosition")) {
212
+ values["value"] = parseInt(data["TargetPosition"]);
213
+ }
214
+ return values;
215
+ }
123
216
  // send a status change to HB Accessory
124
- function hbSetState(server, id, value, state, token) {
217
+ function hbSetState(server, id, service, value, state, token) {
125
218
  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);
219
+ const data = hbLoad(value, service);
220
+ (0, logger_1.log)("HB", "hbSetState: " + id + " -> " + JSON.stringify(data));
131
221
  const respBody = yield (0, HA_API_1.httpRequest)(server + "/api/accessories/" + id, 'PUT', JSON.stringify(data), HA_API_1.ContentType.json, {
132
222
  Authorization: 'Bearer ' + token
133
223
  });
224
+ if (respBody && respBody.error) {
225
+ (0, logger_1.log)("HB", "hbSetState: " + id + " -> error: " + JSON.stringify(respBody));
226
+ }
134
227
  });
135
228
  }
136
229
  exports.hbSetState = hbSetState;
137
230
  // send a status change to HB Accessory
138
- function hbGetState(server, id, token) {
231
+ function hbGetState(server, id, service, token) {
139
232
  return __awaiter(this, void 0, void 0, function* () {
140
- const data = {
141
- characteristicType: "TargetPosition",
142
- };
143
233
  const respBody = yield (0, HA_API_1.httpRequest)(server + "/api/accessories/" + id, 'GET', "", HA_API_1.ContentType.json, {
144
234
  Authorization: 'Bearer ' + token
145
235
  });
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 };
236
+ if (respBody && respBody.error) {
237
+ (0, logger_1.log)("HB", "hbGetState: " + id + " -> error: " + JSON.stringify(respBody));
238
+ return {};
239
+ }
240
+ else if (respBody && respBody.values) {
241
+ return hbValue(respBody.values, service);
242
+ }
243
+ else {
244
+ (0, logger_1.log)("HB", "hbGetState: " + id + " -> no values?? : " + JSON.stringify(respBody));
245
+ return {};
246
+ }
247
+ ;
150
248
  });
151
249
  }
152
250
  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
 
@@ -116,31 +171,86 @@ export async function hbLogin(server: string, username: string, password: string
116
171
  }
117
172
 
118
173
 
119
- // 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
174
+ function hbLoad(value: number | boolean, service: Array<string>): Record<string, number | boolean> {
175
+ function hbvalue(value: number | boolean): number {
176
+ if (typeof value === "boolean") {
177
+ return (value) ? 100 : 0;
178
+ } else if (typeof value === "number") {
179
+ if (value <= 2) {
180
+ return 0;
181
+ } else if (value >= 98) {
182
+ return 100;
183
+ } else {
184
+ return Math.round(value);
185
+ }
186
+ }
187
+ }
188
+
189
+ const data = {};
190
+
191
+ if (service.includes("On")) {
192
+ data["value"] = value;
193
+ data["characteristicType"] = "On";
194
+ }
195
+
196
+ if (service.includes("Brightness")) {
197
+ data["value"] = hbvalue(value);
198
+ data["characteristicType"] = "Brightness";
199
+ }
200
+
201
+ if (service.includes("TargetPosition")) {
202
+ data["value"] = hbvalue(value);
203
+ data["characteristicType"] = "TargetPosition";
124
204
  };
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
- });
205
+ return data;
206
+ }
207
+
208
+ function hbValue(data: Record<string, any>, service: Array<string>): {value?: number, state?: number} {
209
+ const values = {};
210
+ if (service.includes("Brightness")) {
211
+ values["value"] = parseInt(data["Brightness"]);
212
+ }
213
+
214
+ if (service.includes("On")) {
215
+ values["state"] = parseInt(data["On"])
216
+ }
217
+
218
+ if (service.includes("TargetPosition")) {
219
+ values["value"] = parseInt(data["TargetPosition"]);
220
+ }
221
+ return values
222
+ }
223
+
224
+ // send a status change to HB Accessory
225
+ export async function hbSetState(server: string, id: string, service: Array<string>, value: number | boolean, state: number, token: string) : Promise<void> {
226
+ const data = hbLoad(value, service);
227
+
228
+ log("HB", "hbSetState: " + id + " -> " + JSON.stringify(data));
229
+ const respBody = await httpRequest(server + "/api/accessories/"+id, 'PUT', JSON.stringify(data), ContentType.json, {
230
+ Authorization: 'Bearer ' + token
231
+ });
232
+
233
+ if (respBody && respBody.error) {
234
+ log("HB", "hbSetState: " + id + " -> error: " + JSON.stringify(respBody))
235
+ }
129
236
  }
130
237
 
131
238
  // 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
- };
239
+ export async function hbGetState(server: string, id: string, service: Array<string>, token: string) : Promise<{value?: number, state?: number}> {
136
240
  const respBody = await httpRequest(server + "/api/accessories/"+id, 'GET', "", ContentType.json, {
137
241
  Authorization: 'Bearer ' + token
138
242
  });
243
+ if (respBody && respBody.error) {
244
+ log("HB", "hbGetState: " + id + " -> error: " + JSON.stringify(respBody));
245
+ return {};
246
+
247
+ } else if (respBody && respBody.values) {
248
+ return hbValue(respBody.values, service);
139
249
 
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};
250
+ } else {
251
+ log("HB", "hbGetState: " + id + " -> no values?? : " + JSON.stringify(respBody));
252
+ return {};
253
+ };
144
254
  }
145
255
 
146
256
  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,16 +408,24 @@ 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
+ let { 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
409
- (0, logger_1.log)("smartapp", " -> Setting value of Duotecno unit: " + link.unit.getDisplayName() + " to " + value + " of HB accessory: " + link.accName);
410
- link.value = value;
411
- link.status = state;
412
- link.unit.setState(value);
413
- }
414
- else {
415
- (0, logger_1.debug)("smartapp", " -> No change for unit: " + link.unit.getDisplayName() + " to " + value);
413
+ if (typeof value != "undefined") {
414
+ if (link.unit.isUpDown())
415
+ value = (value >= 50) ? 4 : 5;
416
+ if (link.unit.isDimmer)
417
+ value = (value <= 1) ? 1 : (value >= 99) ? 100 : value;
418
+ if (link.unit.isSwitch() || link.unit.isMood())
419
+ value = (value > 50) ? 1 : 0;
420
+ if (value !== link.value) { // || state !== link.status
421
+ (0, logger_1.log)("smartapp", " -> Setting value of Duotecno unit: " + link.unit.getDisplayName() + " to " + value + " of HB accessory: " + link.accName);
422
+ link.value = value;
423
+ // link.status = state;
424
+ link.unit.setState(value);
425
+ }
426
+ else {
427
+ (0, logger_1.debug)("smartapp", " -> No change for unit: " + link.unit.getDisplayName() + " to " + value);
428
+ }
416
429
  }
417
430
  }
418
431
  }
@@ -424,11 +437,18 @@ class SmartApp extends webapp_1.WebApp {
424
437
  (0, logger_1.debug)("smartapp", "Received DT value from unit: " + u.getDisplayName() + " -> " + u.value + " / " + u.status + " from unit");
425
438
  for (const link of this.links) {
426
439
  if (u.isUnit(link.masterAddress, link.masterPort, link.logicalNodeAddress, link.logicalAddress)) {
427
- if (link.value !== u.value) { // || link.status !== u.status
428
- (0, logger_1.log)("smartapp", " -> Setting HB value for accessory: " + link.accName + " to " + u.value + " / " + u.status + " of Duotecno unit: " + u.getDisplayName());
429
- link.value = u.value;
440
+ let value = (typeof u.value === "boolean") ? (u.value ? 100 : 0) : u.value;
441
+ if (u.isUpDown())
442
+ value = ((value === 1) || (value === 3)) ? 100 : 0;
443
+ if (u.isDimmer)
444
+ value = (value <= 2) ? 0 : (value >= 99) ? 100 : value;
445
+ if (u.isSwitch() || link.unit.isMood())
446
+ value = (value) ? 1 : 0;
447
+ if (link.value !== value) { // || link.status !== u.status
448
+ (0, logger_1.log)("smartapp", " -> Setting HB value for accessory: " + link.accName + " to " + value + " / " + u.status + " of Duotecno unit: " + u.getDisplayName());
449
+ link.value = value;
430
450
  link.status = u.status;
431
- yield (0, HB_1.hbSetState)(this.config.apiHost + ":" + this.config.apiPort, link.accId, u.value, u.status, this.token);
451
+ yield (0, HB_1.hbSetState)(this.config.apiHost + ":" + this.config.apiPort, link.accId, link.accService, u.value, u.status, this.token);
432
452
  }
433
453
  else {
434
454
  (0, logger_1.debug)("smartapp", " -> No change for accessory: " + link.accName + " to " + u.value + " / " + u.status);
@@ -503,11 +523,12 @@ class SmartApp extends webapp_1.WebApp {
503
523
  // uniqiue id for the accessory
504
524
  const accId = context.getParam({ name: "accId", type: "string", default: "" });
505
525
  const accName = (0, HB_1.hbName)(accId);
506
- return { accId, accName, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
526
+ const accService = (0, HB_1.hbService)(accId);
527
+ return { accId, accName, accService, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
507
528
  }
508
- updateLink(inx, swtch) {
529
+ updateLink(inx, link) {
509
530
  if ((inx >= 0) && (inx < this.links.length)) {
510
- this.links[inx] = swtch;
531
+ this.links[inx] = link;
511
532
  this.initLinks();
512
533
  this.writeConfig();
513
534
  }
@@ -548,7 +569,7 @@ class SmartApp extends webapp_1.WebApp {
548
569
  else {
549
570
  if (link.type == types_1.LinkType.kAccessory) {
550
571
  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);
572
+ yield (0, HB_1.hbSetState)(this.config.apiHost + ":" + this.config.apiPort, link.id, link.service, link.unit.value, link.unit.status, this.token);
552
573
  }
553
574
  else {
554
575
  (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,15 +459,21 @@ 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
+ let {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
459
- log("smartapp", " -> Setting value of Duotecno unit: " + link.unit.getDisplayName() + " to " + value + " of HB accessory: " + link.accName);
460
- link.value = value;
461
- link.status = state;
462
- link.unit.setState(value);
463
- } else {
464
- debug("smartapp", " -> No change for unit: " + link.unit.getDisplayName() + " to " + value);
464
+ if (typeof value != "undefined") {
465
+ if (link.unit.isUpDown()) value = (value >= 50) ? 4 : 5;
466
+ if (link.unit.isDimmer) value = (value <= 1) ? 1 : (value >= 99) ? 100 : value;
467
+ if (link.unit.isSwitch() || link.unit.isMood()) value = (value > 50) ? 1 : 0;
468
+
469
+ if (value !== link.value) { // || state !== link.status
470
+ log("smartapp", " -> Setting value of Duotecno unit: " + link.unit.getDisplayName() + " to " + value + " of HB accessory: " + link.accName);
471
+ link.value = value;
472
+ // link.status = state;
473
+ link.unit.setState(value);
474
+ } else {
475
+ debug("smartapp", " -> No change for unit: " + link.unit.getDisplayName() + " to " + value);
476
+ }
465
477
  }
466
478
  }
467
479
  }
@@ -474,11 +486,16 @@ export class SmartApp extends WebApp {
474
486
  debug("smartapp", "Received DT value from unit: " + u.getDisplayName() + " -> " + u.value + " / " + u.status + " from unit");
475
487
  for (const link of this.links) {
476
488
  if (u.isUnit(link.masterAddress, link.masterPort, link.logicalNodeAddress, link.logicalAddress)) {
477
- if (link.value !== u.value) { // || link.status !== u.status
478
- log("smartapp", " -> Setting HB value for accessory: " + link.accName + " to " + u.value + " / " + u.status + " of Duotecno unit: " + u.getDisplayName());
479
- link.value = u.value;
489
+ let value = (typeof u.value === "boolean") ? (u.value ? 100 : 0) : u.value;
490
+ if (u.isUpDown()) value = ((value === 1) || (value === 3)) ? 100 : 0;
491
+ if (u.isDimmer) value = (value <= 2) ? 0 : (value >= 99) ? 100 : value;
492
+ if (u.isSwitch() || link.unit.isMood()) value = (value) ? 1 : 0;
493
+
494
+ if (link.value !== value) { // || link.status !== u.status
495
+ log("smartapp", " -> Setting HB value for accessory: " + link.accName + " to " + value + " / " + u.status + " of Duotecno unit: " + u.getDisplayName());
496
+ link.value = value;
480
497
  link.status = u.status;
481
- await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.accId, u.value, u.status, this.token);
498
+ await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.accId, link.accService, u.value, u.status, this.token);
482
499
 
483
500
  } else {
484
501
  debug("smartapp", " -> No change for accessory: " + link.accName + " to " + u.value + " / " + u.status);
@@ -555,12 +572,13 @@ export class SmartApp extends WebApp {
555
572
  // uniqiue id for the accessory
556
573
  const accId = context.getParam({name: "accId", type: "string", default: "" });
557
574
  const accName = hbName(accId);
558
- return { accId, accName, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
575
+ const accService = hbService(accId);
576
+ return { accId, accName, accService, name, masterAddress, masterPort, logicalAddress, logicalNodeAddress, type: stype };
559
577
  }
560
578
 
561
- updateLink(inx: number, swtch: Link) {
579
+ updateLink(inx: number, link: Link) {
562
580
  if ((inx >= 0) && (inx < this.links.length)) {
563
- this.links[inx] = swtch;
581
+ this.links[inx] = link;
564
582
  this.initLinks();
565
583
  this.writeConfig();
566
584
  }
@@ -608,7 +626,7 @@ export class SmartApp extends WebApp {
608
626
  } else {
609
627
  if (link.type == LinkType.kAccessory) {
610
628
  if (await this.checkToken())
611
- await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.id, link.unit.value, link.unit.status, this.token)
629
+ await hbSetState(this.config.apiHost +":"+ this.config.apiPort, link.id, link.service, link.unit.value, link.unit.status, this.token)
612
630
 
613
631
  } else {
614
632
  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> {