homebridge-smartsystem 6.7.4 → 6.8.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
@@ -301,6 +301,13 @@ Homebridge UI version
301
301
  - 3: do dimmers/up-downs
302
302
  - 4: better up-down values to and from HB
303
303
 
304
+
305
+ ### v6.8.0 - 08/07/2025
306
+ - 0: first version including the proxy
307
+ - 1: max/min values for hbValues
308
+ - 2: HB up/down -> don't respond to motor unit's, only macro
309
+ - 3: remember request before the login kicked in (and redirect to that one, once the login was succesful)
310
+
304
311
  ## Hardware
305
312
  A Raspberry Pi that connects to a Duotecno IP Node
306
313
  and (if configured) to a Smappee Infinity (power and plugs)
package/config.json CHANGED
@@ -194,42 +194,6 @@
194
194
  "type": "httpdim",
195
195
  "plug": "http://192.168.0.96/light/0?brightness=$&turn=#"
196
196
  },
197
- {
198
- "logicalNodeAddress": 252,
199
- "logicalAddress": 105,
200
- "masterAddress": "192.168.0.97",
201
- "masterPort": 5001,
202
- "name": "Screen Salon Top",
203
- "displayName": "Screen Salon Top",
204
- "extendedType": 0,
205
- "nomacro": "N",
206
- "nostop": "N",
207
- "unitName": "Salon Top",
208
- "value": 0,
209
- "data": "",
210
- "header": "",
211
- "method": "GET",
212
- "type": "somfy",
213
- "plug": "1"
214
- },
215
- {
216
- "logicalNodeAddress": 252,
217
- "logicalAddress": 104,
218
- "masterAddress": "192.168.0.97",
219
- "masterPort": 5001,
220
- "name": "Screen Tim small",
221
- "displayName": "Screen Tim small",
222
- "extendedType": 0,
223
- "nomacro": "N",
224
- "nostop": "Y",
225
- "unitName": "Tim Small",
226
- "value": 0,
227
- "data": "",
228
- "header": "",
229
- "method": "GET",
230
- "type": "httpupdown",
231
- "plug": "2"
232
- },
233
197
  {
234
198
  "logicalNodeAddress": 16,
235
199
  "logicalAddress": 35,
@@ -254,8 +218,8 @@
254
218
  "password": "GMTecno",
255
219
  "links": [
256
220
  {
257
- "logicalNodeAddress": 3,
258
- "logicalAddress": 30,
221
+ "logicalNodeAddress": 252,
222
+ "logicalAddress": 104,
259
223
  "masterAddress": "192.168.0.97",
260
224
  "masterPort": 5001,
261
225
  "name": "unit",
@@ -269,7 +233,9 @@
269
233
  "TargetPosition",
270
234
  "ObstructionDetected"
271
235
  ],
272
- "type": "accessory"
236
+ "type": "accessory",
237
+ "max": 100,
238
+ "min": 0
273
239
  }
274
240
  ]
275
241
  },
@@ -304,7 +270,7 @@
304
270
  "masterPort": 5001,
305
271
  "name": "<Solar Panels",
306
272
  "displayName": "<Solar Panels",
307
- "extendedType": 4,
273
+ "extendedType": 0,
308
274
  "active": "N",
309
275
  "used": "Y",
310
276
  "group": 0
@@ -316,7 +282,7 @@
316
282
  "masterPort": 5001,
317
283
  "name": ">Cars",
318
284
  "displayName": ">Cars",
319
- "extendedType": 4,
285
+ "extendedType": 0,
320
286
  "active": "N",
321
287
  "used": "Y",
322
288
  "group": 0
@@ -328,7 +294,7 @@
328
294
  "masterPort": 5001,
329
295
  "name": ">Grid",
330
296
  "displayName": ">Grid",
331
- "extendedType": 4,
297
+ "extendedType": 0,
332
298
  "active": "N",
333
299
  "used": "Y",
334
300
  "group": 0
@@ -340,11 +306,23 @@
340
306
  "masterPort": 5001,
341
307
  "name": ">Swimmingpool",
342
308
  "displayName": ">Swimmingpool",
343
- "extendedType": 4,
309
+ "extendedType": 0,
344
310
  "active": "N",
345
311
  "used": "Y",
346
312
  "group": 0
347
313
  },
314
+ {
315
+ "logicalNodeAddress": 252,
316
+ "logicalAddress": 104,
317
+ "masterAddress": "192.168.0.97",
318
+ "masterPort": 5001,
319
+ "name": "Tim Small",
320
+ "displayName": "Tim Small",
321
+ "extendedType": 8,
322
+ "active": "Y",
323
+ "used": "Y",
324
+ "group": 0
325
+ },
348
326
  {
349
327
  "logicalNodeAddress": 252,
350
328
  "logicalAddress": 7,
@@ -365,7 +343,7 @@
365
343
  "name": "Garage $",
366
344
  "displayName": "Garage ",
367
345
  "extendedType": 8,
368
- "active": "N",
346
+ "active": "Y",
369
347
  "used": "Y",
370
348
  "group": 0
371
349
  },
@@ -376,7 +354,7 @@
376
354
  "masterPort": 5001,
377
355
  "name": "2|Dimmer",
378
356
  "displayName": "Somfy Dimmer",
379
- "extendedType": 0,
357
+ "extendedType": 1,
380
358
  "active": "Y",
381
359
  "used": "Y",
382
360
  "group": 0
@@ -388,7 +366,7 @@
388
366
  "masterPort": 5001,
389
367
  "name": "Blue LED",
390
368
  "displayName": "Blue LED",
391
- "extendedType": 0,
369
+ "extendedType": 1,
392
370
  "active": "N",
393
371
  "used": "Y",
394
372
  "group": 0
@@ -400,7 +378,7 @@
400
378
  "masterPort": 5001,
401
379
  "name": "Red LED",
402
380
  "displayName": "Red LED",
403
- "extendedType": 0,
381
+ "extendedType": 1,
404
382
  "active": "N",
405
383
  "used": "Y",
406
384
  "group": 0
@@ -480,5 +458,14 @@
480
458
  "gateway2": ""
481
459
  }
482
460
  },
483
- "p1": false
461
+ "p1": false,
462
+ "proxy": {
463
+ "cloudServer": "ws.duotecno.eu",
464
+ "cloudPort": 5098,
465
+ "masterAddress": "192.168.0.97",
466
+ "masterPort": 5001,
467
+ "uniqueId": "",
468
+ "debug": false,
469
+ "kind": "solo"
470
+ }
484
471
  }
@@ -15,7 +15,7 @@ var LogLevel;
15
15
  exports.logSettings = {
16
16
  "*": LogLevel.log,
17
17
  "master": LogLevel.log,
18
- "protocol": LogLevel.debug,
18
+ "protocol": LogLevel.noLog,
19
19
  "webapp": LogLevel.log,
20
20
  "p1": LogLevel.log,
21
21
  "shelly": LogLevel.log,
@@ -10,7 +10,7 @@ export enum LogLevel { noLog = 0, log = 1, debug = 2 };
10
10
  export const logSettings = {
11
11
  "*": LogLevel.log,
12
12
  "master": LogLevel.log,
13
- "protocol": LogLevel.debug,
13
+ "protocol": LogLevel.noLog,
14
14
  "webapp": LogLevel.log,
15
15
  "p1": LogLevel.log,
16
16
  "shelly": LogLevel.log,
@@ -790,6 +790,7 @@ exports.Protocol = {
790
790
  }
791
791
  else if (next.cmd === Rec.Motor) {
792
792
  // motor -> boolean/status
793
+ // [38,0,252,104,8,0,0]
793
794
  // 0 = stopped, 1 stopped/down, 2 = stopped/up, 3 = busy/down, 4 = busy/up
794
795
  unit.status = next.message[6];
795
796
  unit.value = next.message[6];
@@ -800,7 +801,8 @@ exports.Protocol = {
800
801
  // = EV_UNITMACROCOMMANDO
801
802
  // examples: On 50%: [69,0,NodeAddress,UnitAddress,6,1,0,50]
802
803
  // Off: [69,0,NodeAddress,UnitAddress,6,0,0,0] -> don't touch dimmer value
803
- // STATUS
804
+ // unit_type_duoswitch -> Event = 32, state 0, 1, 2
805
+ // STATUS (Event: 7 = dimmer, 5 = switch, on = 1, off = 0)
804
806
  if (((next.message[4] == 7) || (next.message[4] == 5)) && (next.message[5] == 1)) {
805
807
  // PIR ON -- override status value with "2" (our PIR ON)
806
808
  unit.status = 2;
@@ -815,6 +817,13 @@ exports.Protocol = {
815
817
  (next.message[4] === 11)) {
816
818
  unit.value = this.makeWord(next.message, 6);
817
819
  }
820
+ else if (next.message[4] === 32) {
821
+ // unit_type_duoswitch -> Event = 32
822
+ // [69,0,252,104,32,2,0,0]
823
+ // state = 0 -> stop, state = 1 -> up, state = 2 -> down
824
+ unit.value = 10 + next.message[5];
825
+ unit.status = next.message[5];
826
+ }
818
827
  (0, logger_1.debug)("protocol", "received status - macro -> value=" + unit.value + " / status=" + unit.status);
819
828
  kind = "M";
820
829
  }
@@ -913,6 +913,7 @@ export const Protocol = {
913
913
 
914
914
  } else if (next.cmd === Rec.Motor) {
915
915
  // motor -> boolean/status
916
+ // [38,0,252,104,8,0,0]
916
917
  // 0 = stopped, 1 stopped/down, 2 = stopped/up, 3 = busy/down, 4 = busy/up
917
918
  unit.status = next.message[6];
918
919
  unit.value = next.message[6];
@@ -923,8 +924,9 @@ export const Protocol = {
923
924
  // = EV_UNITMACROCOMMANDO
924
925
  // examples: On 50%: [69,0,NodeAddress,UnitAddress,6,1,0,50]
925
926
  // Off: [69,0,NodeAddress,UnitAddress,6,0,0,0] -> don't touch dimmer value
927
+ // unit_type_duoswitch -> Event = 32, state 0, 1, 2
926
928
 
927
- // STATUS
929
+ // STATUS (Event: 7 = dimmer, 5 = switch, on = 1, off = 0)
928
930
  if (((next.message[4] == 7) || (next.message[4] == 5)) && (next.message[5] == 1)) {
929
931
  // PIR ON -- override status value with "2" (our PIR ON)
930
932
  unit.status = 2;
@@ -939,6 +941,13 @@ export const Protocol = {
939
941
  if ((((next.message[4] === 1) || (next.message[4] === 6) || (next.message[4] === 7)) && (next.message[5] === 1)) ||
940
942
  (next.message[4] === 11)) {
941
943
  unit.value = this.makeWord(next.message, 6);
944
+
945
+ } else if (next.message[4] === 32) {
946
+ // unit_type_duoswitch -> Event = 32
947
+ // [69,0,252,104,32,2,0,0]
948
+ // state = 0 -> stop, state = 1 -> up, state = 2 -> down
949
+ unit.value = 10 + next.message[5];
950
+ unit.status = next.message[5];
942
951
  }
943
952
 
944
953
  debug("protocol", "received status - macro -> value=" + unit.value + " / status=" + unit.status);
package/duotecno/types.js CHANGED
@@ -15,6 +15,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.makeInt = exports.now = exports.datetime = exports.time = exports.date = exports.watt = exports.hex = exports.two = exports.char = exports.ascii = exports.wait = exports.Sanitizers = exports.actionValueStr = exports.actionValue = exports.kEmptyBinding = exports.kEmptyLink = exports.LinkType = exports.kEmptySwitch = exports.SwitchType = exports.kEmptyRule = exports.kEmptyAction = exports.RuleType = exports.Boundaries = exports.kEmptyCommRecord = exports.WriteError = exports.kEmptySettings = exports.kEmptyNetworkConfig = exports.kEmptyGroup = exports.kEmptyScene = exports.kEmptyUnitScene = exports.kEmptyUnit = exports.UnitExtendedType = exports.UnitType = exports.UnitMotorCmd = exports.UnitState = exports.NodeType = void 0;
18
+ const proxy_1 = require("../server/proxy");
18
19
  // Node types
19
20
  var NodeType;
20
21
  (function (NodeType) {
@@ -153,7 +154,7 @@ var LinkType;
153
154
  LinkType["kNoType"] = "";
154
155
  LinkType["kAccessory"] = "accessory";
155
156
  })(LinkType = exports.LinkType || (exports.LinkType = {}));
156
- exports.kEmptyLink = Object.assign(Object.assign({}, exports.kEmptyUnit), { type: LinkType.kAccessory, accName: "acc", accId: "", accService: [] });
157
+ exports.kEmptyLink = Object.assign(Object.assign({}, exports.kEmptyUnit), { type: LinkType.kNoType, max: 100, min: 0, accName: "acc", accId: "", accService: [] });
157
158
  exports.kEmptyBinding = {
158
159
  channel: "0", masterAddress: "0.0.0.0", masterPort: 5001, register: 0
159
160
  };
@@ -204,6 +205,7 @@ exports.Sanitizers = {
204
205
  config.shelly = config.shelly || false;
205
206
  config.smartapp = config.smartapp || null;
206
207
  config.system = exports.Sanitizers.system(config.system);
208
+ config.proxy = exports.Sanitizers.proxy(config.proxy, config.system);
207
209
  return config;
208
210
  },
209
211
  homebridge: function (config) {
@@ -299,7 +301,9 @@ exports.Sanitizers = {
299
301
  link.accId = aLink.accId || "";
300
302
  link.accName = aLink.accName || "";
301
303
  link.accService = aLink.accService || [];
302
- link.type = aLink.type || LinkType.kNoType;
304
+ link.type = aLink.type || exports.kEmptyLink.type;
305
+ link.max = aLink.max || exports.kEmptyLink.max;
306
+ link.min = aLink.min || exports.kEmptyLink.min;
303
307
  return link;
304
308
  },
305
309
  switchConfig: function (aSwitch) {
@@ -358,6 +362,19 @@ exports.Sanitizers = {
358
362
  b.channel = binding.channel || "0";
359
363
  return b;
360
364
  },
365
+ ///////////
366
+ // Proxy //
367
+ ///////////
368
+ proxy: function (config, system) {
369
+ var _a, _b;
370
+ const c = proxy_1.kEmptyProxy;
371
+ c.cloudServer = (config === null || config === void 0 ? void 0 : config.cloudServer) || proxy_1.kEmptyProxy.cloudServer;
372
+ c.cloudPort = (config === null || config === void 0 ? void 0 : config.cloudPort) || proxy_1.kEmptyProxy.cloudPort;
373
+ c.masterAddress = (config === null || config === void 0 ? void 0 : config.masterAddress) || ((_a = system === null || system === void 0 ? void 0 : system.cmasters[0]) === null || _a === void 0 ? void 0 : _a.address) || "";
374
+ c.masterPort = (config === null || config === void 0 ? void 0 : config.masterPort) || ((_b = system === null || system === void 0 ? void 0 : system.cmasters[0]) === null || _b === void 0 ? void 0 : _b.port) || 5001;
375
+ c.uniqueId = (config === null || config === void 0 ? void 0 : config.uniqueId) || proxy_1.kEmptyProxy.uniqueId;
376
+ return c;
377
+ },
361
378
  //////////////
362
379
  // Duotecno //
363
380
  //////////////
package/duotecno/types.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  // v3 - smart server version, Mar 2020
5
5
  // v3.1 - added scenes from app version, May 2020
6
6
 
7
+ import { kEmptyProxy, ProxyConfig } from "../server/proxy";
7
8
  import { Unit } from "./protocol";
8
9
 
9
10
  // for Active=Y/N or New=X
@@ -268,13 +269,15 @@ export interface Link extends UnitDef{
268
269
  accId: string;
269
270
  accService: Array<string>; // e.g. ["On", "Brightness"], ["TargetPosition", "CurrentPosition", ...]
270
271
  type: LinkType;
272
+ max?: number;
273
+ min?: number;
271
274
  unit?: Unit;
272
275
  dtValue?: number | boolean | string;
273
276
  hbValue?: number | boolean | string
274
277
  }
275
278
 
276
279
  export const kEmptyLink: Link = {
277
- ...kEmptyUnit, type: LinkType.kAccessory, accName: "acc", accId: "", accService: []
280
+ ...kEmptyUnit, type: LinkType.kNoType, max: 100, min: 0, accName: "acc", accId: "", accService: []
278
281
  };
279
282
 
280
283
 
@@ -412,6 +415,7 @@ export const Sanitizers = {
412
415
  config.shelly = config.shelly || false;
413
416
  config.smartapp = config.smartapp || null;
414
417
  config.system = Sanitizers.system(config.system);
418
+ config.proxy = Sanitizers.proxy(config.proxy, config.system);
415
419
 
416
420
  return config;
417
421
  },
@@ -517,7 +521,9 @@ export const Sanitizers = {
517
521
  link.accId = aLink.accId || "";
518
522
  link.accName = aLink.accName || "";
519
523
  link.accService = aLink.accService || [];
520
- link.type = aLink.type || LinkType.kNoType;
524
+ link.type = aLink.type || kEmptyLink.type;
525
+ link.max = aLink.max || kEmptyLink.max;
526
+ link.min = aLink.min || kEmptyLink.min;
521
527
  return link;
522
528
  },
523
529
 
@@ -581,6 +587,19 @@ export const Sanitizers = {
581
587
  return b;
582
588
  },
583
589
 
590
+ ///////////
591
+ // Proxy //
592
+ ///////////
593
+ proxy: function(config?: ProxyConfig, system?: SystemConfig): ProxyConfig {
594
+ const c = kEmptyProxy as ProxyConfig;
595
+ c.cloudServer = config?.cloudServer || kEmptyProxy.cloudServer;
596
+ c.cloudPort = config?.cloudPort || kEmptyProxy.cloudPort;
597
+ c.masterAddress = config?.masterAddress || system?.cmasters[0]?.address || "";
598
+ c.masterPort = config?.masterPort || system?.cmasters[0]?.port || 5001;
599
+ c.uniqueId = config?.uniqueId || kEmptyProxy.uniqueId;
600
+ return c;
601
+ },
602
+
584
603
  //////////////
585
604
  // Duotecno //
586
605
  //////////////
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-smartsystem",
3
3
  "displayname": "Duotecno Bridge",
4
- "version": "6.7.4",
4
+ "version": "6.8.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.hbGetValue = exports.hbSetState = exports.dt2hbvalue = exports.hb2dtvalue = exports.hbLogin = exports.hbAccessories = exports.hbService = exports.hbName = void 0;
12
+ exports.hbUnsubscribe = exports.hbSubscribe = exports.hbStop = exports.hbGetValue = exports.hbSetState = exports.dt2hbvalue = exports.hb2dtvalue = 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 = [];
@@ -173,14 +173,15 @@ function hbLoad(value, service) {
173
173
  return (value) ? 100 : 0;
174
174
  }
175
175
  else if (typeof value === "number") {
176
- if (value <= 2) {
176
+ value = Math.round(value);
177
+ if (value <= 1) {
177
178
  return 0;
178
179
  }
179
- else if (value >= 98) {
180
+ else if (value >= 99) {
180
181
  return 100;
181
182
  }
182
183
  else {
183
- return Math.round(value);
184
+ return value;
184
185
  }
185
186
  }
186
187
  }
@@ -217,16 +218,31 @@ function hb2dtvalue(unit, value) {
217
218
  return { dtTarget, dtValue };
218
219
  }
219
220
  exports.hb2dtvalue = hb2dtvalue;
220
- function dt2hbvalue(unit, dontChangeValue) {
221
- let value = (typeof unit.value === "boolean") ? (unit.value ? 100 : 0) : Math.round(unit.value);
222
- // status Motor: 0 = stopped, 1 stopped/down, 2 = stopped/up, 3 = busy/down, 4 = busy/up
223
- if (unit.isUpDown())
224
- return ((value === 1) || (value === 3)) ? 0 : ((value === 2) || (value === 4)) ? 100 : dontChangeValue;
225
- if (unit.isDimmer())
226
- return (value <= 2) ? 0 : (value >= 99) ? 100 : value;
227
- if (unit.isSwitch() || unit.isMood())
228
- return (value) ? 1 : 0;
229
- return value;
221
+ function dt2hbvalue(unit, dontChangeValue, min, max) {
222
+ // tunr into (rounded) number
223
+ let value = (typeof unit.value === "boolean") ? (unit.value ? max : min) : Math.round(unit.value);
224
+ // Virtual motor: state = 0 -> stop, state = 1 -> up, state = 2 -> down
225
+ // Real motor: 0 = stopped, 1 stopped/down, 2 = stopped/up, 3 = busy/down, 4 = busy/up
226
+ if (unit.isUpDown()) {
227
+ // don't send status of real motor
228
+ return (value == 10) ? -1 : // stop [ virtual ]
229
+ (value == 11) ? max : // up [ virtual ]
230
+ (value == 12) ? min : undefined; // down [ virtual ]
231
+ // return ((value === 1) || (value === 3)) ? min : // down [ motor ]
232
+ // ((value === 2) || (value === 4)) ? max : // up [ motor ]
233
+ // (value == 0) ? dontChangeValue : // no change [ motor ]
234
+ // (value == 10) ? -1 : // stop [ virtual ]
235
+ // (value == 11) ? max : min; // up/down [ virtual ]
236
+ }
237
+ else if (unit.isDimmer()) {
238
+ return (value <= 1) ? 0 : (value >= 99) ? 100 : value;
239
+ }
240
+ else if (unit.isSwitch() || unit.isMood()) {
241
+ return (value) ? max : min;
242
+ }
243
+ else {
244
+ return value;
245
+ }
230
246
  }
231
247
  exports.dt2hbvalue = dt2hbvalue;
232
248
  function hbValue(data, service) {
@@ -276,6 +292,28 @@ function hbGetValue(server, id, service, token) {
276
292
  });
277
293
  }
278
294
  exports.hbGetValue = hbGetValue;
295
+ function hbStop(server, id, service, token) {
296
+ return __awaiter(this, void 0, void 0, function* () {
297
+ // does not exist, but we'll do a getCurrentPosition + setTargetPosition to this value
298
+ const respBody = yield (0, HA_API_1.httpRequest)(server + "/api/accessories/" + id, 'GET', "", HA_API_1.ContentType.json, {
299
+ Authorization: 'Bearer ' + token
300
+ });
301
+ if (respBody && respBody.error) {
302
+ (0, logger_1.log)("HB", "hbGetState: " + id + " -> error: " + JSON.stringify(respBody));
303
+ return undefined;
304
+ }
305
+ else if (respBody && respBody.values) {
306
+ (0, logger_1.log)("HB", "hbStop GetState: " + id + " -> " + JSON.stringify(respBody.values));
307
+ const data = { value: respBody.values.CurrentPosition, characteristicType: "TargetPosition" };
308
+ (0, logger_1.log)("HB", "hbStop SetState: " + id + " -> " + JSON.stringify(data));
309
+ yield (0, HA_API_1.httpRequest)(server + "/api/accessories/" + id, 'PUT', JSON.stringify(data), HA_API_1.ContentType.json, {
310
+ Authorization: 'Bearer ' + token
311
+ });
312
+ return hbValue(respBody.values, service);
313
+ }
314
+ });
315
+ }
316
+ exports.hbStop = hbStop;
279
317
  function hbSubscribe(callback) {
280
318
  if (timer) {
281
319
  clearInterval(timer);
package/server/HB.ts CHANGED
@@ -177,12 +177,13 @@ function hbLoad(value: number | boolean, service: Array<string>): Record<string,
177
177
  if (typeof value === "boolean") {
178
178
  return (value) ? 100 : 0;
179
179
  } else if (typeof value === "number") {
180
- if (value <= 2) {
180
+ value = Math.round(value);
181
+ if (value <= 1) {
181
182
  return 0;
182
- } else if (value >= 98) {
183
+ } else if (value >= 99) {
183
184
  return 100;
184
185
  } else {
185
- return Math.round(value);
186
+ return value;
186
187
  }
187
188
  }
188
189
  }
@@ -223,16 +224,32 @@ export function hb2dtvalue(unit: Unit, value: number | boolean): {dtValue: numbe
223
224
  return {dtTarget, dtValue};
224
225
  }
225
226
 
226
- export function dt2hbvalue(unit: Unit, dontChangeValue: number): number {
227
- let value = (typeof unit.value === "boolean") ? (unit.value ? 100 : 0) : Math.round(unit.value);
227
+ export function dt2hbvalue(unit: Unit, dontChangeValue: number, min: number, max: number): number {
228
+ // tunr into (rounded) number
229
+ let value = (typeof unit.value === "boolean") ? (unit.value ? max : min) : Math.round(unit.value);
228
230
 
229
- // status Motor: 0 = stopped, 1 stopped/down, 2 = stopped/up, 3 = busy/down, 4 = busy/up
230
- if (unit.isUpDown()) return ((value === 1) || (value === 3)) ? 0 : ((value === 2) || (value === 4)) ? 100 : dontChangeValue;
231
-
232
- if (unit.isDimmer()) return (value <= 2) ? 0 : (value >= 99) ? 100 : value;
233
- if (unit.isSwitch() || unit.isMood()) return (value) ? 1 : 0;
231
+ // Virtual motor: state = 0 -> stop, state = 1 -> up, state = 2 -> down
232
+ // Real motor: 0 = stopped, 1 stopped/down, 2 = stopped/up, 3 = busy/down, 4 = busy/up
233
+ if (unit.isUpDown()) {
234
+ // don't send status of real motor
235
+ return (value == 10) ? -1 : // stop [ virtual ]
236
+ (value == 11) ? max : // up [ virtual ]
237
+ (value == 12) ? min : undefined; // down [ virtual ]
238
+ // return ((value === 1) || (value === 3)) ? min : // down [ motor ]
239
+ // ((value === 2) || (value === 4)) ? max : // up [ motor ]
240
+ // (value == 0) ? dontChangeValue : // no change [ motor ]
241
+ // (value == 10) ? -1 : // stop [ virtual ]
242
+ // (value == 11) ? max : min; // up/down [ virtual ]
243
+
244
+ } else if (unit.isDimmer()) {
245
+ return (value <= 1) ? 0 : (value >= 99) ? 100 : value;
246
+
247
+ } else if (unit.isSwitch() || unit.isMood()) {
248
+ return (value) ? max : min;
234
249
 
235
- return value;
250
+ } else {
251
+ return value;
252
+ }
236
253
  }
237
254
 
238
255
  function hbValue(data: Record<string, any>, service: Array<string>): number {
@@ -282,6 +299,28 @@ export async function hbGetValue(server: string, id: string, service: Array<stri
282
299
  };
283
300
  }
284
301
 
302
+ export async function hbStop(server: string, id: string, service: Array<string>, token: string) : Promise<number | undefined> {
303
+ // does not exist, but we'll do a getCurrentPosition + setTargetPosition to this value
304
+ const respBody = await httpRequest(server + "/api/accessories/"+id, 'GET', "", ContentType.json, {
305
+ Authorization: 'Bearer ' + token
306
+ });
307
+ if (respBody && respBody.error) {
308
+ log("HB", "hbGetState: " + id + " -> error: " + JSON.stringify(respBody));
309
+ return undefined;
310
+
311
+ } else if (respBody && respBody.values) {
312
+ log("HB", "hbStop GetState: " + id + " -> " + JSON.stringify(respBody.values));
313
+
314
+ const data = {value: respBody.values.CurrentPosition, characteristicType: "TargetPosition"};
315
+ log("HB", "hbStop SetState: " + id + " -> " + JSON.stringify(data));
316
+
317
+ await httpRequest(server + "/api/accessories/"+id, 'PUT', JSON.stringify(data), ContentType.json, {
318
+ Authorization: 'Bearer ' + token
319
+ });
320
+ return hbValue(respBody.values, service);
321
+ }
322
+ }
323
+
285
324
  export function hbSubscribe(callback) {
286
325
  if (timer) {
287
326
  clearInterval(timer);
@@ -30,8 +30,10 @@ const p1_1 = require("./p1");
30
30
  const shelly_1 = require("./shelly");
31
31
  const config_1 = require("../duotecno/config");
32
32
  const fs_1 = require("fs");
33
+ const proxy_1 = require("./proxy");
33
34
  class Platform extends base_1.Base {
34
35
  constructor(logger, pConfig, api) {
36
+ var _a;
35
37
  super("homebridge");
36
38
  this.accessoryList = [];
37
39
  this.ready = false;
@@ -109,6 +111,15 @@ class Platform extends base_1.Base {
109
111
  else {
110
112
  (0, logger_1.log)("homebridge", "platform - No SocApp configured.");
111
113
  }
114
+ if (this.config.proxy && ((_a = this.config.proxy) === null || _a === void 0 ? void 0 : _a.uniqueId)) {
115
+ this.config.proxy.kind = "gw";
116
+ (0, proxy_1.setProxyConfig)(this.config.proxy);
117
+ (0, proxy_1.cleanStart)(true);
118
+ (0, logger_1.log)("homebridge", "platform - Proxy configured: " + JSON.stringify(this.config.proxy, null, 2));
119
+ }
120
+ else {
121
+ (0, logger_1.log)("homebridge", "platform - No Proxy configured.");
122
+ }
112
123
  }
113
124
  startPower(type, PowerMgr) {
114
125
  // startup a power handler, if configured
@@ -20,7 +20,8 @@ import { Lock } from "../accessories/lock";
20
20
  import { P1 } from "./p1";
21
21
  import { Shelly } from "./shelly";
22
22
  import { kConfigFiles, setConfig } from "../duotecno/config";
23
- import { fstat, readFileSync } from "fs";
23
+ import { readFileSync } from "fs";
24
+ import { cleanStart, setProxyConfig } from "./proxy";
24
25
 
25
26
 
26
27
  export class Platform extends Base {
@@ -115,6 +116,15 @@ export class Platform extends Base {
115
116
  } else {
116
117
  log("homebridge", "platform - No SocApp configured.");
117
118
  }
119
+
120
+ if (this.config.proxy && this.config.proxy?.uniqueId) {
121
+ this.config.proxy.kind = "gw";
122
+ setProxyConfig(this.config.proxy);
123
+ cleanStart(true);
124
+ log("homebridge", "platform - Proxy configured: " + JSON.stringify(this.config.proxy, null, 2));
125
+ } else {
126
+ log("homebridge", "platform - No Proxy configured.");
127
+ }
118
128
  }
119
129
 
120
130
  startPower(type: string, PowerMgr: any) {