homebridge-smartsystem 6.8.5 → 6.8.8

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
@@ -302,13 +302,16 @@ Homebridge UI version
302
302
  - 4: better up-down values to and from HB
303
303
 
304
304
 
305
- ### v6.8.0 - 08/07/2025
305
+ ### v6.8.x - 08/07/2025
306
306
  - 0: first version including the proxy
307
307
  - 1: max/min values for hbValues
308
308
  - 2: HB up/down -> don't respond to motor unit's, only macro
309
309
  - 3: remember request before the login kicked in (and redirect to that one, once the login was succesful)
310
310
  - 4: now using only TCP sockets to the server
311
311
  - 5: making it more robust / inline with repo-service version
312
+ - 6: 26 aug / removed double error handlers on cloudSocket
313
+ - 7: 28 aug / restart if no free connections
314
+ - 8: 28 aug / less logging (through logConfig) for device set/get functions
312
315
 
313
316
  ## Hardware
314
317
  A Raspberry Pi that connects to a Duotecno IP Node
@@ -86,7 +86,7 @@ class Accessory {
86
86
  }
87
87
  updateState() {
88
88
  this.me.getCharacteristic(this.homebridge.Characteristic.On).updateValue(this.unit.status);
89
- // log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> on = " + this.unit.status);
89
+ (0, logger_1.log)("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> on = " + this.unit.status);
90
90
  }
91
91
  }
92
92
  exports.Accessory = Accessory;
@@ -107,7 +107,7 @@ export class Accessory {
107
107
 
108
108
  updateState() {
109
109
  this.me.getCharacteristic(this.homebridge.Characteristic.On).updateValue(this.unit.status);
110
- // log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> on = " + this.unit.status);
110
+ log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> on = " + this.unit.status);
111
111
  }
112
112
 
113
113
  }
@@ -69,7 +69,7 @@ class Dimmer extends accessory_1.Accessory {
69
69
  }
70
70
  updateState() {
71
71
  this.me.getCharacteristic(this.homebridge.Characteristic.Brightness).updateValue(this.unit.value);
72
- // log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> Brightness = " + this.unit.value);
72
+ (0, logger_1.log)("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> Brightness = " + this.unit.value);
73
73
  }
74
74
  }
75
75
  exports.Dimmer = Dimmer;
@@ -84,7 +84,7 @@ export class Dimmer extends Accessory {
84
84
 
85
85
  updateState() {
86
86
  this.me.getCharacteristic(this.homebridge.Characteristic.Brightness).updateValue(this.unit.value);
87
- // log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> Brightness = " + this.unit.value);
87
+ log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> Brightness = " + this.unit.value);
88
88
  }
89
89
 
90
90
  }
@@ -87,7 +87,7 @@ class GarageDoor extends accessory_1.Accessory {
87
87
  // in response to Duotecno status messages
88
88
  updateState() {
89
89
  const value = this.DT2HB(this.unit.status);
90
- // log("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing to HB: " + value);
90
+ (0, logger_1.log)("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing to HB: " + value);
91
91
  this.me.getCharacteristic(this.homebridge.Characteristic.CurrentDoorState).updateValue(value);
92
92
  }
93
93
  }
@@ -117,7 +117,7 @@ export class GarageDoor extends Accessory {
117
117
  // in response to Duotecno status messages
118
118
  updateState() {
119
119
  const value = this.DT2HB(this.unit.status);
120
- // log("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing to HB: " + value);
120
+ log("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing to HB: " + value);
121
121
  this.me.getCharacteristic(this.homebridge.Characteristic.CurrentDoorState).updateValue(value);
122
122
  }
123
123
 
@@ -69,7 +69,7 @@ class Lock extends accessory_1.Accessory {
69
69
  }
70
70
  // in response to Duotecno status messages
71
71
  updateState() {
72
- // log("accessory", "Received updateState -> Homekit Lock for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
72
+ (0, logger_1.log)("accessory", "Received updateState -> Homekit Lock for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
73
73
  this.me.getCharacteristic(this.homebridge.Characteristic.LockCurrentState).updateValue(this.unit.status);
74
74
  }
75
75
  }
@@ -89,7 +89,7 @@ export class Lock extends Accessory {
89
89
 
90
90
  // in response to Duotecno status messages
91
91
  updateState() {
92
- // log("accessory", "Received updateState -> Homekit Lock for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
92
+ log("accessory", "Received updateState -> Homekit Lock for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
93
93
  this.me.getCharacteristic(this.homebridge.Characteristic.LockCurrentState).updateValue(this.unit.status);
94
94
  }
95
95
 
@@ -51,7 +51,7 @@ class Mood extends accessory_1.Accessory {
51
51
  }
52
52
  updateState() {
53
53
  this.me.getCharacteristic(this.homebridge.Characteristic.On).updateValue(!!this.unit.value);
54
- // log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> On = " + !!this.unit.value);
54
+ (0, logger_1.log)("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> On = " + !!this.unit.value);
55
55
  }
56
56
  }
57
57
  exports.Mood = Mood;
@@ -62,7 +62,7 @@ export class Mood extends Accessory {
62
62
  }
63
63
  updateState() {
64
64
  this.me.getCharacteristic(this.homebridge.Characteristic.On).updateValue(!!this.unit.value);
65
- // log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> On = " + !!this.unit.value);
65
+ log("accessory", "Received status change -> update accessory -> " + this.unit.getName() + " -> On = " + !!this.unit.value);
66
66
  }
67
67
 
68
68
  }
@@ -34,7 +34,7 @@ class Temperature extends accessory_1.Accessory {
34
34
  }
35
35
  updateState() {
36
36
  this.me.getCharacteristic(this.homebridge.Characteristic.CurrentTemperature).updateValue(this.unit.value / 10.0);
37
- // log("accessory", "Received updateState -> Homekit Temperature -> " + this.unit.getName() + " -> temp = " +(<number>this.unit.value) / 10.0);
37
+ (0, logger_1.log)("accessory", "Received updateState -> Homekit Temperature -> " + this.unit.getName() + " -> temp = " + this.unit.value / 10.0);
38
38
  }
39
39
  }
40
40
  exports.Temperature = Temperature;
@@ -42,7 +42,7 @@ export class Temperature extends Accessory {
42
42
 
43
43
  updateState() {
44
44
  this.me.getCharacteristic(this.homebridge.Characteristic.CurrentTemperature).updateValue((<number>this.unit.value) / 10.0);
45
- // log("accessory", "Received updateState -> Homekit Temperature -> " + this.unit.getName() + " -> temp = " +(<number>this.unit.value) / 10.0);
45
+ log("accessory", "Received updateState -> Homekit Temperature -> " + this.unit.getName() + " -> temp = " +(<number>this.unit.value) / 10.0);
46
46
  }
47
47
 
48
48
  }
@@ -134,7 +134,7 @@ class WindowCovering extends accessory_1.Accessory {
134
134
  // called in response to Duotecno status update
135
135
  updateState() {
136
136
  const value = this.DT2HB(this.unit.status);
137
- // log("accessory", "Received updateState -> Homekit WindowCovering for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing: " + value);
137
+ (0, logger_1.log)("accessory", "Received updateState -> Homekit WindowCovering for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing: " + value);
138
138
  this.me.getCharacteristic(this.homebridge.Characteristic.CurrentPosition).updateValue(value);
139
139
  if (this.unit.status == types_1.UnitState.kClosing) {
140
140
  this.me.getCharacteristic(this.homebridge.Characteristic.TargetPosition).updateValue(0);
@@ -161,7 +161,7 @@ export class WindowCovering extends Accessory {
161
161
  // called in response to Duotecno status update
162
162
  updateState() {
163
163
  const value = this.DT2HB(this.unit.status);
164
- // log("accessory", "Received updateState -> Homekit WindowCovering for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing: " + value);
164
+ log("accessory", "Received updateState -> Homekit WindowCovering for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing: " + value);
165
165
 
166
166
  this.me.getCharacteristic(this.homebridge.Characteristic.CurrentPosition).updateValue(value);
167
167
 
@@ -16,6 +16,7 @@ exports.logSettings = {
16
16
  "*": LogLevel.log,
17
17
  "master": LogLevel.log,
18
18
  "protocol": LogLevel.noLog,
19
+ "accessory": LogLevel.noLog,
19
20
  "webapp": LogLevel.log,
20
21
  "p1": LogLevel.log,
21
22
  "shelly": LogLevel.log,
@@ -11,6 +11,7 @@ export const logSettings = {
11
11
  "*": LogLevel.log,
12
12
  "master": LogLevel.log,
13
13
  "protocol": LogLevel.noLog,
14
+ "accessory": LogLevel.noLog,
14
15
  "webapp": LogLevel.log,
15
16
  "p1": LogLevel.log,
16
17
  "shelly": LogLevel.log,
package/duotecno/types.ts CHANGED
@@ -17,7 +17,7 @@ export enum NodeType { kNoNode = 0, kStandardNode = 1, kGatewayNode = 4, kModemN
17
17
 
18
18
  // States
19
19
  export enum UnitState { kOpening = 4, kClosing = 3, kOpen = 2, kClosed = 1, kStopped = 0 };
20
- export enum UnitMotorCmd { kClose = 5, kOpen = 4, kStop = 3};
20
+ export enum UnitMotorCmd { kClose = 5, kOpen = 4, kStop = 3 };
21
21
 
22
22
  export enum UnitType {
23
23
  kNoType = 0, kDimmer = 1, kSwitch = 2, kInput = 3,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-smartsystem",
3
3
  "displayname": "Duotecno Bridge",
4
- "version": "6.8.5",
4
+ "version": "6.8.8",
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",
package/server/proxy.js CHANGED
@@ -38,6 +38,7 @@ catch (e) {
38
38
  setProxyConfig();
39
39
  console.log("No config-proxy.json found, using default values from homebridge or other.");
40
40
  }
41
+ let connectionChecker = null;
41
42
  const gCloudConnections = [];
42
43
  const kDebug = config.debug || false; // enable debug mode from config
43
44
  /////////////
@@ -60,13 +61,23 @@ function error(str) {
60
61
  ///////////////////////////////
61
62
  // Start up the proxy server //
62
63
  ///////////////////////////////
63
- if (config.uniqueId) {
64
- makeNewCloudConnection(config.masterAddress, config.masterPort, config.cloudServer, config.cloudPort, config.uniqueId);
65
- }
64
+ cleanStart(true);
66
65
  function makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId, count = 0) {
66
+ // retry making a cloud connection
67
+ function reconnect(err) {
68
+ error(`[PROXY] → [CLOUD] Failed to connect ${count + 1} times to the Cloud server: ${err.message}`);
69
+ if (count < 3) {
70
+ setTimeout(() => {
71
+ makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId, count + 1);
72
+ }, 1000);
73
+ }
74
+ }
67
75
  const cloudSocket = new net.Socket();
76
+ cloudSocket.once('error', reconnect);
77
+ log(`[PROXY] → [CLOUD] Attempt ${count + 1} to start a new free connection for ${config.uniqueId} to the Cloud at ${server}:${serverPort}`);
68
78
  cloudSocket.connect(serverPort, server, () => {
69
79
  log(`[PROXY] → [CLOUD] Connected to Cloud at ${server}:${serverPort}`);
80
+ cloudSocket.removeListener('error', reconnect);
70
81
  // Send unique ID as the first message to identify this proxy/device
71
82
  cloudSocket.write(`[${uniqueId}]`);
72
83
  debug(`[PROXY] → [CLOUD] Sent unique ID: ${uniqueId}`);
@@ -74,27 +85,35 @@ function makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId
74
85
  gCloudConnections.push(context);
75
86
  log(`[PROXY] → [CLOUD] New free connection #${gCloudConnections.length} to the Cloud at ${server}:${serverPort}`);
76
87
  });
77
- cloudSocket.on('error', (err) => {
78
- error(`[PROXY] → [CLOUD] Failed to connect ${count + 1} times to the Cloud server: ${err.message}`);
79
- if (count < 3) {
80
- setTimeout(() => {
81
- makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId, count + 1);
82
- }, 1000);
83
- }
84
- });
85
- log(`[PROXY] → [CLOUD] Attempt ${count + 1} to start a new free connection for ${config.uniqueId} to the Cloud at ${server}:${serverPort}`);
86
88
  }
87
89
  exports.makeNewCloudConnection = makeNewCloudConnection;
88
90
  ////////////////
89
91
  // Restarting //
90
92
  ////////////////
91
93
  function cleanStart(restart = false) {
92
- gCloudConnections.forEach(ctx => ctx.cleanupSockets());
93
- log(`[PROXY] → [CLOUD] Cleaned up all ${gCloudConnections.length} cloud connections.`);
94
- gCloudConnections.splice(0, gCloudConnections.length); // clear the array
94
+ if (gCloudConnections.length) {
95
+ gCloudConnections.forEach(ctx => ctx.cleanupSockets());
96
+ log(`[PROXY] → [CLOUD] Cleaned up all ${gCloudConnections.length} cloud connections.`);
97
+ gCloudConnections.splice(0, gCloudConnections.length); // clear the array
98
+ }
99
+ if (connectionChecker) {
100
+ clearInterval(connectionChecker);
101
+ connectionChecker = null;
102
+ }
95
103
  if (restart) {
96
104
  if (config.uniqueId) {
97
105
  makeNewCloudConnection(config.masterAddress, config.masterPort, config.cloudServer, config.cloudPort, config.uniqueId);
106
+ // check every 5 second for a free connection
107
+ connectionChecker = setInterval(() => {
108
+ const freeConnection = gCloudConnections.find((ctx) => (!ctx.deviceSocket) && ctx.cloudSocket && (ctx.cloudSocket.readyState === 'open'));
109
+ if (freeConnection) {
110
+ log(`[PROXY] → [CLOUD] Found free connection #${freeConnection.uniqueId} to the Cloud -> we're still ok.`);
111
+ }
112
+ else {
113
+ error(`[PROXY] → [CLOUD] No free connections available, restarting proxy. ***************************`);
114
+ cleanStart(true);
115
+ }
116
+ }, 5000);
98
117
  }
99
118
  else {
100
119
  log(`[PROXY] → [CLOUD] Not restarting, no unique ID configured, not starting the proxy.`);
@@ -293,6 +312,8 @@ function restart() {
293
312
  // Graceful shutdown handler
294
313
  function handleShutdown(signal) {
295
314
  log(`[PROXY] → [SYSTEM] Received ${signal}, shutting down gracefully...`);
315
+ if (connectionChecker)
316
+ clearInterval(connectionChecker);
296
317
  // Close all cloud connections
297
318
  gCloudConnections.forEach(ctx => ctx.cleanupSockets());
298
319
  gCloudConnections.splice(0, gCloudConnections.length);
package/server/proxy.ts CHANGED
@@ -51,6 +51,7 @@ try {
51
51
  }
52
52
 
53
53
 
54
+ let connectionChecker: NodeJS.Timer | null = null;
54
55
 
55
56
  const gCloudConnections: Array<Context> = [];
56
57
  const kDebug = config.debug || false; // enable debug mode from config
@@ -77,16 +78,28 @@ function error(str: string) {
77
78
  ///////////////////////////////
78
79
  // Start up the proxy server //
79
80
  ///////////////////////////////
80
- if (config.uniqueId) {
81
- makeNewCloudConnection(config.masterAddress, config.masterPort, config.cloudServer, config.cloudPort, config.uniqueId);
82
- }
81
+ cleanStart(true);
83
82
 
84
83
 
85
84
  export function makeNewCloudConnection(master: string, masterPort: number, server: string, serverPort: number, uniqueId: string, count = 0) {
85
+ // retry making a cloud connection
86
+ function reconnect(err: Error) {
87
+ error(`[PROXY] → [CLOUD] Failed to connect ${count+1} times to the Cloud server: ${err.message}`);
88
+ if (count < 3) {
89
+ setTimeout(() => {
90
+ makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId, count + 1);
91
+ }, 1000);
92
+ }
93
+ }
94
+
86
95
  const cloudSocket = new net.Socket();
87
-
96
+ cloudSocket.once('error', reconnect);
97
+
98
+ log(`[PROXY] → [CLOUD] Attempt ${count+1} to start a new free connection for ${config.uniqueId} to the Cloud at ${server}:${serverPort}`);
99
+
88
100
  cloudSocket.connect(serverPort, server, () => {
89
101
  log(`[PROXY] → [CLOUD] Connected to Cloud at ${server}:${serverPort}`);
102
+ cloudSocket.removeListener('error', reconnect);
90
103
 
91
104
  // Send unique ID as the first message to identify this proxy/device
92
105
  cloudSocket.write(`[${uniqueId}]`);
@@ -96,17 +109,6 @@ export function makeNewCloudConnection(master: string, masterPort: number, serve
96
109
  gCloudConnections.push(context);
97
110
  log(`[PROXY] → [CLOUD] New free connection #${gCloudConnections.length} to the Cloud at ${server}:${serverPort}`);
98
111
  });
99
-
100
- cloudSocket.on('error', (err) => {
101
- error(`[PROXY] → [CLOUD] Failed to connect ${count+1} times to the Cloud server: ${err.message}`);
102
- if (count < 3) {
103
- setTimeout(() => {
104
- makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId, count + 1);
105
- }, 1000);
106
- }
107
- });
108
-
109
- log(`[PROXY] → [CLOUD] Attempt ${count+1} to start a new free connection for ${config.uniqueId} to the Cloud at ${server}:${serverPort}`);
110
112
  }
111
113
 
112
114
 
@@ -114,14 +116,35 @@ export function makeNewCloudConnection(master: string, masterPort: number, serve
114
116
  // Restarting //
115
117
  ////////////////
116
118
  export function cleanStart(restart: boolean = false) {
117
- gCloudConnections.forEach(ctx => ctx.cleanupSockets());
118
- log(`[PROXY] → [CLOUD] Cleaned up all ${gCloudConnections.length} cloud connections.`);
119
+ if (gCloudConnections.length) {
120
+ gCloudConnections.forEach(ctx => ctx.cleanupSockets());
121
+ log(`[PROXY] → [CLOUD] Cleaned up all ${gCloudConnections.length} cloud connections.`);
119
122
 
120
- gCloudConnections.splice(0, gCloudConnections.length); // clear the array
123
+ gCloudConnections.splice(0, gCloudConnections.length); // clear the array
124
+ }
125
+
126
+ if (connectionChecker) {
127
+ clearInterval(connectionChecker);
128
+ connectionChecker = null;
129
+ }
121
130
 
122
131
  if (restart) {
123
132
  if (config.uniqueId) {
124
133
  makeNewCloudConnection(config.masterAddress, config.masterPort, config.cloudServer, config.cloudPort, config.uniqueId);
134
+
135
+ // check every 5 second for a free connection
136
+ connectionChecker = setInterval(() => {
137
+ const freeConnection = gCloudConnections.find((ctx: Context) =>
138
+ (!ctx.deviceSocket) && ctx.cloudSocket && (ctx.cloudSocket.readyState === 'open')
139
+ );
140
+ if (freeConnection) {
141
+ log(`[PROXY] → [CLOUD] Found free connection #${freeConnection.uniqueId} to the Cloud -> we're still ok.`);
142
+ } else {
143
+ error(`[PROXY] → [CLOUD] No free connections available, restarting proxy. ***************************`);
144
+ cleanStart(true);
145
+ }
146
+ }, 5000);
147
+
125
148
  } else {
126
149
  log(`[PROXY] → [CLOUD] Not restarting, no unique ID configured, not starting the proxy.`);
127
150
  }
@@ -364,6 +387,8 @@ function restart() {
364
387
  function handleShutdown(signal: string) {
365
388
  log(`[PROXY] → [SYSTEM] Received ${signal}, shutting down gracefully...`);
366
389
 
390
+ if (connectionChecker)clearInterval(connectionChecker);
391
+
367
392
  // Close all cloud connections
368
393
  gCloudConnections.forEach(ctx => ctx.cleanupSockets());
369
394
  gCloudConnections.splice(0, gCloudConnections.length);