homebridge-smartsystem 7.1.22 → 7.1.23

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.
@@ -49,7 +49,7 @@ class Accessory {
49
49
  getState(next) {
50
50
  if (this.unit) {
51
51
  (0, logger_1.log)("accessory", "getState was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
52
- next(null, this.unit.status);
52
+ next(null, !!this.unit.status);
53
53
  }
54
54
  else {
55
55
  next(new Error("accessory - getState needs to be overridden if no unit is provided."));
@@ -81,7 +81,7 @@ class Accessory {
81
81
  next(new Error("accessory - setState needs to be overridden if no unit is provided."));
82
82
  }
83
83
  updateState() {
84
- this.me.getCharacteristic(this.homebridge.Characteristic.On).updateValue(this.unit.status);
84
+ this.me.getCharacteristic(this.homebridge.Characteristic.On).updateValue(!!this.unit.status);
85
85
  (0, logger_1.log)("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> on = " + this.unit.status);
86
86
  }
87
87
  }
@@ -28,9 +28,10 @@ class Dimmer extends accessory_1.Accessory {
28
28
  return [lightbulbService];
29
29
  }
30
30
  getBrightness(next) {
31
+ var _a;
31
32
  if (this.unit) {
32
33
  (0, logger_1.log)("accessory", "getBrightness was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
33
- next(null, this.unit.value);
34
+ next(null, (_a = this.unit.value) !== null && _a !== void 0 ? _a : 0);
34
35
  }
35
36
  else {
36
37
  next(new Error("accessory - getBrightness needs to be overridden if no unit is provided."));
@@ -21,9 +21,10 @@ class Temperature extends accessory_1.Accessory {
21
21
  return [temperatureService];
22
22
  }
23
23
  getTemperature(next) {
24
+ var _a;
24
25
  if (this.unit) {
25
26
  (0, logger_1.log)("accessory", "getTemperature was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
26
- const limited = Math.max(-100, Math.min(100, this.unit.value / 10.0));
27
+ const limited = Math.max(-100, Math.min(100, ((_a = this.unit.value) !== null && _a !== void 0 ? _a : 0) / 10.0));
27
28
  next(null, limited);
28
29
  }
29
30
  else {
package/duotecno/types.js CHANGED
@@ -428,6 +428,7 @@ exports.Sanitizers = {
428
428
  c.installationId = (config === null || config === void 0 ? void 0 : config.installationId) || proxy_1.kEmptyProxy.installationId;
429
429
  c.nodeNumber = (typeof (config === null || config === void 0 ? void 0 : config.nodeNumber) === "number") ? config.nodeNumber : proxy_1.kEmptyProxy.nodeNumber;
430
430
  c.httpEnabled = !!(config === null || config === void 0 ? void 0 : config.httpEnabled);
431
+ c.gatewayPhysicalId = (config === null || config === void 0 ? void 0 : config.gatewayPhysicalId) || proxy_1.kEmptyProxy.gatewayPhysicalId;
431
432
  c.kind = "gw";
432
433
  return c;
433
434
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-smartsystem",
3
3
  "displayname": "Duotecno Bridge",
4
- "version": "7.1.22",
4
+ "version": "7.1.23",
5
5
  "description": "SmartServer (Proxy TCP sockets to the cloud, Smappee MQTT, Duotecno IP Nodes, Homekit interface)",
6
6
  "main": "index.js",
7
7
  "author": "Johan Coppieters",
@@ -36,7 +36,7 @@
36
36
  ],
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/Duotecno-developer/smartsocket.git"
39
+ "url": "git+https://github.com/Duotecno-developer/smartsocket.git"
40
40
  },
41
41
  "dependencies": {
42
42
  "bonjour-service": "^1.3.0",
package/server/proxy.js CHANGED
@@ -11,8 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Context = exports.cleanStart = exports.makeNewCloudConnection = exports.gCloudConnections = exports.setProxyConfig = exports.setWebApp = exports.kEmptyProxy = exports.gatewayHttpPort = exports.makeDeviceId = exports.kMasterNodeNumber = void 0;
13
13
  const net = require("net");
14
+ const os = require("os");
14
15
  const child_process_1 = require("child_process");
15
16
  const process_1 = require("process");
17
+ const config_1 = require("../duotecno/config");
16
18
  // Fixed, non-configurable logical node number every Duotecno master identifies itself with
17
19
  // on the bus -- used for both of the master's own registrations (raw protocol + Config API).
18
20
  exports.kMasterNodeNumber = 0xfc;
@@ -31,6 +33,47 @@ function gatewayHttpPort() {
31
33
  return port || 80;
32
34
  }
33
35
  exports.gatewayHttpPort = gatewayHttpPort;
36
+ // The cloud server can tell apart two different physical devices that were accidentally
37
+ // registered under the same unique id (instead of assuming a reconnect and evicting the
38
+ // other one), given a "physical address" -- 1-8 hex digits, formatted "0x..." -- alongside
39
+ // the unique id at registration. Masters have a real one: the Duotecno bus physical address
40
+ // of their own logical node (kMasterNodeNumber). Returns '' if not known yet (e.g. the master
41
+ // hasn't finished its initial discovery), in which case no physical address is sent for this
42
+ // registration, same as old firmware.
43
+ function masterPhysicalAddress() {
44
+ const system = gWebApp === null || gWebApp === void 0 ? void 0 : gWebApp.system;
45
+ const master = (system === null || system === void 0 ? void 0 : system.findMaster) ? system.findMaster(config.masterAddress, config.masterPort) : null;
46
+ const node = (master === null || master === void 0 ? void 0 : master.findNode) ? master.findNode(exports.kMasterNodeNumber) : null;
47
+ const phys = node === null || node === void 0 ? void 0 : node.physicalAddress;
48
+ return (typeof phys === "number" && phys > 0) ? "0x" + phys.toString(16) : '';
49
+ }
50
+ // The gateway itself has no Duotecno bus address of its own. Prefer the last two groups of a
51
+ // non-internal IPv6 address (8 hex digits total, e.g. "2afa:23fa:b95:9ebf" -> last two groups
52
+ // "b95:9ebf" -> "0x0b959ebf") if one is available on this machine; otherwise fall back to a
53
+ // random id generated once and persisted in config.json, so it stays stable across restarts
54
+ // and lets the cloud tell two different physical gateways apart even if they were accidentally
55
+ // given the same installation id.
56
+ function gatewayPhysicalAddress() {
57
+ const interfaces = os.networkInterfaces();
58
+ for (const name in interfaces) {
59
+ for (const iface of interfaces[name] || []) {
60
+ if ((iface.family === "IPv6" || iface.family === 6) && !iface.internal) {
61
+ const groups = iface.address.split(":").filter(g => g.length > 0);
62
+ if (groups.length >= 2) {
63
+ const last2 = groups.slice(-2).map(g => g.padStart(4, '0')).join('');
64
+ return "0x" + last2;
65
+ }
66
+ }
67
+ }
68
+ }
69
+ if (!config.gatewayPhysicalId) {
70
+ config.gatewayPhysicalId = "0x" + Math.floor(Math.random() * 0x100000000).toString(16).padStart(8, '0');
71
+ (0, config_1.setSubConfig)("proxy", config);
72
+ (0, config_1.setAPIConfig)();
73
+ log(`[PROXY] No IPv6 address available, generated and stored a random physical id for this gateway: ${config.gatewayPhysicalId}`);
74
+ }
75
+ return config.gatewayPhysicalId;
76
+ }
34
77
  exports.kEmptyProxy = {
35
78
  cloudServer: "masters.duotecno.eu",
36
79
  cloudPort: 5097,
@@ -40,6 +83,7 @@ exports.kEmptyProxy = {
40
83
  installationId: "",
41
84
  nodeNumber: 0xff,
42
85
  httpEnabled: false,
86
+ gatewayPhysicalId: "",
43
87
  debug: false,
44
88
  kind: "gw" // default running under the gateway or other process manager -> don't restart yourself
45
89
  };
@@ -61,6 +105,7 @@ function setProxyConfig(c) {
61
105
  config.installationId = c.installationId || exports.kEmptyProxy.installationId;
62
106
  config.nodeNumber = (typeof c.nodeNumber === "number") ? c.nodeNumber : exports.kEmptyProxy.nodeNumber;
63
107
  config.httpEnabled = !!c.httpEnabled;
108
+ config.gatewayPhysicalId = c.gatewayPhysicalId || exports.kEmptyProxy.gatewayPhysicalId;
64
109
  config.debug = !!c.debug;
65
110
  config.kind = c.kind || exports.kEmptyProxy.kind;
66
111
  }
@@ -114,6 +159,9 @@ function makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId
114
159
  // we check every 16 seconds for a free connection, if none found -> restart
115
160
  }
116
161
  }
162
+ // lets the cloud tell two genuinely different physical devices apart if they were ever
163
+ // accidentally configured with the same unique id, instead of assuming a reconnect
164
+ const physicalAddress = isGateway ? gatewayPhysicalAddress() : masterPhysicalAddress();
117
165
  const cloudSocket = new net.Socket();
118
166
  cloudSocket.once('error', reconnect);
119
167
  // net.Socket.connect() has no default timeout: if the TCP handshake to the cloud gets
@@ -130,9 +178,9 @@ function makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId
130
178
  log(`[PROXY] → [CLOUD] Connected to Cloud at ${server}:${serverPort}`);
131
179
  cloudSocket.setTimeout(0); // connected -- no longer subject to the connect-attempt timeout
132
180
  cloudSocket.removeListener('error', reconnect);
133
- // Send unique ID as the first message to identify this proxy/device
134
- cloudSocket.write(`[${uniqueId}]`);
135
- debug(`[PROXY] → [CLOUD] Sent unique ID: ${uniqueId}`);
181
+ // Send unique ID (and physical address, if known) as the first message to identify this proxy/device
182
+ cloudSocket.write(physicalAddress ? `[${uniqueId}][${physicalAddress}]` : `[${uniqueId}]`);
183
+ debug(`[PROXY] → [CLOUD] Sent unique ID: ${uniqueId}` + (physicalAddress ? ` physical address: ${physicalAddress}` : ''));
136
184
  const context = new Context(cloudSocket, master, masterPort, server, serverPort, uniqueId, isGateway);
137
185
  exports.gCloudConnections.push(context);
138
186
  log(`[PROXY] → [CLOUD] New free connection #${exports.gCloudConnections.length} to the Cloud at ${server}:${serverPort}`);