homebridge-smartsystem 6.8.3 → 6.8.4
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/config.json +2 -2
- package/package.json +1 -1
- package/server/proxy.js +60 -34
- package/server/proxy.ts +73 -40
package/config.json
CHANGED
|
@@ -461,11 +461,11 @@
|
|
|
461
461
|
"p1": false,
|
|
462
462
|
"proxy": {
|
|
463
463
|
"cloudServer": "ws.duotecno.eu",
|
|
464
|
-
"cloudPort":
|
|
464
|
+
"cloudPort": 5097,
|
|
465
465
|
"masterAddress": "192.168.0.97",
|
|
466
466
|
"masterPort": 5001,
|
|
467
467
|
"uniqueId": "",
|
|
468
468
|
"debug": false,
|
|
469
|
-
"kind": "
|
|
469
|
+
"kind": "gw"
|
|
470
470
|
}
|
|
471
471
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-smartsystem",
|
|
3
3
|
"displayname": "Duotecno Bridge",
|
|
4
|
-
"version": "6.8.
|
|
4
|
+
"version": "6.8.4",
|
|
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/proxy.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Context = exports.cleanStart = exports.makeNewCloudConnection = exports.setProxyConfig = exports.kEmptyProxy = void 0;
|
|
4
4
|
const net = require("net");
|
|
5
|
-
const WebSocket = require("ws");
|
|
6
5
|
const child_process_1 = require("child_process");
|
|
7
6
|
const process_1 = require("process");
|
|
8
7
|
exports.kEmptyProxy = {
|
|
@@ -61,17 +60,23 @@ function error(str) {
|
|
|
61
60
|
///////////////////////////////
|
|
62
61
|
// Start up the proxy server //
|
|
63
62
|
///////////////////////////////
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
if (config.uniqueId) {
|
|
64
|
+
makeNewCloudConnection(config.masterAddress, config.masterPort, config.cloudServer, config.cloudPort, config.uniqueId);
|
|
65
|
+
}
|
|
67
66
|
function makeNewCloudConnection(master, masterPort, server, serverPort, uniqueId) {
|
|
68
|
-
const cloudSocket = new
|
|
69
|
-
cloudSocket.
|
|
67
|
+
const cloudSocket = new net.Socket();
|
|
68
|
+
cloudSocket.connect(serverPort, server, () => {
|
|
70
69
|
log(`[PROXY] → [CLOUD] Connected to Cloud at ${server}:${serverPort}`);
|
|
70
|
+
// Send unique ID as the first message to identify this proxy/device
|
|
71
|
+
cloudSocket.write(`[${uniqueId}]`);
|
|
72
|
+
debug(`[PROXY] → [CLOUD] Sent unique ID: ${uniqueId}`);
|
|
71
73
|
const context = new Context(cloudSocket, master, masterPort, server, serverPort, uniqueId);
|
|
72
74
|
gCloudConnections.push(context);
|
|
73
75
|
log(`[PROXY] → [CLOUD] New free connection #${gCloudConnections.length} to the Cloud at ${server}:${serverPort}`);
|
|
74
76
|
});
|
|
77
|
+
cloudSocket.on('error', (err) => {
|
|
78
|
+
error(`[PROXY] → [CLOUD] Failed to connect to Cloud: ${err.message}`);
|
|
79
|
+
});
|
|
75
80
|
log(`[PROXY] → [CLOUD] Attempting to start a new free connection for ${config.uniqueId} to the Cloud at ${server}:${serverPort}`);
|
|
76
81
|
}
|
|
77
82
|
exports.makeNewCloudConnection = makeNewCloudConnection;
|
|
@@ -85,7 +90,7 @@ function cleanStart(restart = false) {
|
|
|
85
90
|
context.deviceSocket.end();
|
|
86
91
|
context.deviceSocket = null;
|
|
87
92
|
}
|
|
88
|
-
context.cloudSocket.
|
|
93
|
+
context.cloudSocket.end();
|
|
89
94
|
context.cloudSocket = null;
|
|
90
95
|
});
|
|
91
96
|
gCloudConnections.splice(0, gCloudConnections.length); // clear the array
|
|
@@ -117,11 +122,11 @@ class Context {
|
|
|
117
122
|
}
|
|
118
123
|
setupCloudSocket() {
|
|
119
124
|
// set up handlers for the cloud socket
|
|
120
|
-
this.cloudSocket.on('
|
|
121
|
-
this.handleDataFromCloud(
|
|
125
|
+
this.cloudSocket.on('data', data => {
|
|
126
|
+
this.handleDataFromCloud(data);
|
|
122
127
|
});
|
|
123
|
-
this.cloudSocket.on('close', (
|
|
124
|
-
warning('[CLOUD] Connection closed
|
|
128
|
+
this.cloudSocket.on('close', () => {
|
|
129
|
+
warning('[CLOUD] Connection closed');
|
|
125
130
|
if (this.deviceSocket) {
|
|
126
131
|
this.deviceSocket.end();
|
|
127
132
|
this.deviceSocket = null;
|
|
@@ -136,26 +141,31 @@ class Context {
|
|
|
136
141
|
});
|
|
137
142
|
}
|
|
138
143
|
handleDataFromCloud(data) {
|
|
139
|
-
debug(`[CLOUD] → [PROXY]: Data from Cloud: ${data.toString()
|
|
144
|
+
debug(`[CLOUD] → [PROXY]: Data from Cloud: ${data.toString()}`);
|
|
140
145
|
if (!this.deviceSocket) {
|
|
141
146
|
// we either have a real client that wants to connect to the device,
|
|
142
147
|
// or we receive a heartbeat from the cloud server
|
|
148
|
+
// or we receive a connection response [OK], [OK-=2], [ERROR-...]
|
|
143
149
|
if (this.isHeartbeatRequest(data)) {
|
|
144
150
|
debug(`[CLOUD] → [PROXY]: Received heartbeat from Cloud, returning response.`);
|
|
145
151
|
// respond to the heartbeat of the cloud server
|
|
146
|
-
this.cloudSocket.
|
|
152
|
+
this.cloudSocket.write("[72,3]"); // command([Command.HeartbeatStatus, CommandMethod.Heartbeat.server])
|
|
153
|
+
}
|
|
154
|
+
else if (this.isConnectionResponse(data)) {
|
|
155
|
+
debug(`[CLOUD] → [PROXY]: Received connection response from Cloud: ${data.toString()}`);
|
|
156
|
+
// Handle connection response from the cloud server
|
|
147
157
|
}
|
|
148
158
|
else {
|
|
149
|
-
// There is incomming data, it's a fresh connection, so: a new client wants to connect to the device
|
|
159
|
+
// There is real incomming data, it's a fresh connection, so: a new client wants to connect to the device
|
|
150
160
|
// 1) create a new (free) connection to the cloud server for the next client
|
|
151
161
|
// 2) connect to the device and send the initial data
|
|
152
|
-
log(`[PROXY] → [CLOUD] New client connection, creating new free connection for this proxy/device: ${
|
|
162
|
+
log(`[PROXY] → [CLOUD] New client connection, creating new free connection for this proxy/device: ${config.uniqueId}.`);
|
|
153
163
|
makeNewCloudConnection(this.master, this.masterPort, this.server, this.serverPort, this.uniqueId);
|
|
154
164
|
this.makeDeviceConnection(this.master, this.masterPort, data);
|
|
155
165
|
}
|
|
156
166
|
}
|
|
157
167
|
else if (this.deviceSocket.readyState === 'open') {
|
|
158
|
-
this.deviceSocket.write(data);
|
|
168
|
+
this.deviceSocket.write(new Uint8Array(data));
|
|
159
169
|
debug(`[PROXY] → [DEVICE] forwarding data to device: ${data.toString().slice(0, -1)}`);
|
|
160
170
|
}
|
|
161
171
|
else {
|
|
@@ -168,25 +178,34 @@ class Context {
|
|
|
168
178
|
this.deviceSocket = new net.Socket();
|
|
169
179
|
// Connect to local device and send the data that came in from the cloud
|
|
170
180
|
this.deviceSocket.connect(port, address, () => {
|
|
181
|
+
// Check if socket is still valid (not cleaned up by removeConnection)
|
|
182
|
+
if (!this.deviceSocket) {
|
|
183
|
+
warning(`[PROXY] → [DEVICE] Device socket was cleaned up before connection completed`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
171
186
|
log(`[PROXY] → [DEVICE] Connected to local device at ${address}:${port}`);
|
|
172
187
|
this.setUpDeviceSocket();
|
|
173
188
|
log(`[PROXY] → [DEVICE] Sending initial message: ${data.toString().slice(0, -1)}`);
|
|
174
|
-
this.deviceSocket.write(data);
|
|
189
|
+
this.deviceSocket.write(new Uint8Array(data));
|
|
175
190
|
});
|
|
176
191
|
}
|
|
177
192
|
else if (this.deviceSocket.readyState === 'open') {
|
|
178
193
|
warning(`[PROXY] → [DEVICE] Device socket already open, sending data directly -- STRANGE !!`);
|
|
179
|
-
this.deviceSocket.write(data);
|
|
194
|
+
this.deviceSocket.write(new Uint8Array(data));
|
|
180
195
|
}
|
|
181
196
|
else {
|
|
182
197
|
error(`[PROXY] → [DEVICE] Device socket exists, but is not open yet !!`);
|
|
183
198
|
}
|
|
184
199
|
}
|
|
185
200
|
setUpDeviceSocket() {
|
|
201
|
+
if (!this.deviceSocket) {
|
|
202
|
+
error(`[PROXY] → [DEVICE] Cannot set up device socket - socket is null`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
186
205
|
this.deviceSocket.on('data', (data) => {
|
|
187
206
|
const message = data.toString('utf-8');
|
|
188
207
|
debug(`[DEVICE] → [PROXY] forwarding data to Cloud: ${message.slice(0, -1)}`);
|
|
189
|
-
this.cloudSocket.
|
|
208
|
+
this.cloudSocket.write(message);
|
|
190
209
|
});
|
|
191
210
|
this.deviceSocket.on('close', () => {
|
|
192
211
|
log(`[DEVICE] → [PROXY] Device socket closed`);
|
|
@@ -198,11 +217,17 @@ class Context {
|
|
|
198
217
|
});
|
|
199
218
|
}
|
|
200
219
|
removeConnection() {
|
|
201
|
-
|
|
220
|
+
// Clean up device socket
|
|
221
|
+
if (this.deviceSocket) {
|
|
222
|
+
if (!this.deviceSocket.destroyed) {
|
|
223
|
+
this.deviceSocket.end();
|
|
224
|
+
}
|
|
225
|
+
this.deviceSocket = null;
|
|
226
|
+
}
|
|
202
227
|
// remove this context and close the cloud socket
|
|
203
228
|
const index = gCloudConnections.indexOf(this);
|
|
204
229
|
if (index !== -1) {
|
|
205
|
-
this.cloudSocket.
|
|
230
|
+
this.cloudSocket.end();
|
|
206
231
|
this.cloudSocket = null;
|
|
207
232
|
gCloudConnections.splice(index, 1);
|
|
208
233
|
log(`[PROXY] → [CLOUD] Closed socket to Cloud and removed context for device ${this.master}`);
|
|
@@ -238,19 +263,20 @@ class Context {
|
|
|
238
263
|
}
|
|
239
264
|
// check if it's a heartbeat request from the server
|
|
240
265
|
isHeartbeatRequest(data) {
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
266
|
+
// hearbeatKind = (poll = 1, poll_old = 2, serve = 3)
|
|
267
|
+
return ((data[0] === 91) && // [
|
|
268
|
+
(data[1] === 50) && // 2
|
|
269
|
+
(data[2] === 49) && // 1
|
|
270
|
+
(data[3] === 53) && // 5
|
|
271
|
+
(data[4] === 44) && // ,
|
|
272
|
+
(data[5] === 51) && // 3
|
|
273
|
+
(data[6] === 93)); // ]
|
|
274
|
+
}
|
|
275
|
+
// check if it's a server response after connecting and sending our uniqueID
|
|
276
|
+
isConnectionResponse(data) {
|
|
277
|
+
const message = data.toString();
|
|
278
|
+
// Check for various server response patterns
|
|
279
|
+
return (message.startsWith('[OK')) || (message.startsWith('[ERROR'));
|
|
254
280
|
}
|
|
255
281
|
}
|
|
256
282
|
exports.Context = Context;
|
package/server/proxy.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as net from 'net';
|
|
2
|
-
import * as WebSocket from 'ws';
|
|
3
2
|
import { spawn } from 'child_process';
|
|
4
3
|
import { argv, cwd, execPath } from 'process';
|
|
5
4
|
|
|
6
5
|
//
|
|
7
|
-
// [ device (TCP) ] ←→ [ Proxy =
|
|
6
|
+
// [ device (TCP) ] ←→ [ Proxy = TCP client ] ←→ [ Cloud = TCP server ] ←→ [ browser client ]
|
|
8
7
|
//
|
|
9
8
|
|
|
10
9
|
export interface ProxyConfig {
|
|
@@ -16,6 +15,7 @@ export interface ProxyConfig {
|
|
|
16
15
|
debug?: boolean; // debug mode
|
|
17
16
|
kind?: "pm2" | "gw" | "solo";
|
|
18
17
|
}
|
|
18
|
+
|
|
19
19
|
export const kEmptyProxy: ProxyConfig = {
|
|
20
20
|
cloudServer: "ws.duotecno.eu",
|
|
21
21
|
cloudPort: 5098,
|
|
@@ -50,6 +50,8 @@ try {
|
|
|
50
50
|
console.log("No config-proxy.json found, using default values from homebridge or other.");
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
|
|
54
|
+
|
|
53
55
|
const gCloudConnections: Array<Context> = [];
|
|
54
56
|
const kDebug = config.debug || false; // enable debug mode from config
|
|
55
57
|
|
|
@@ -75,26 +77,34 @@ function error(str: string) {
|
|
|
75
77
|
///////////////////////////////
|
|
76
78
|
// Start up the proxy server //
|
|
77
79
|
///////////////////////////////
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// }
|
|
80
|
+
if (config.uniqueId) {
|
|
81
|
+
makeNewCloudConnection(config.masterAddress, config.masterPort, config.cloudServer, config.cloudPort, config.uniqueId);
|
|
82
|
+
}
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
export function makeNewCloudConnection(master: string, masterPort: number, server: string, serverPort: number, uniqueId: string) {
|
|
85
|
-
const cloudSocket = new
|
|
86
|
-
|
|
87
|
-
cloudSocket.
|
|
86
|
+
const cloudSocket = new net.Socket();
|
|
87
|
+
|
|
88
|
+
cloudSocket.connect(serverPort, server, () => {
|
|
88
89
|
log(`[PROXY] → [CLOUD] Connected to Cloud at ${server}:${serverPort}`);
|
|
90
|
+
|
|
91
|
+
// Send unique ID as the first message to identify this proxy/device
|
|
92
|
+
cloudSocket.write(`[${uniqueId}]`);
|
|
93
|
+
debug(`[PROXY] → [CLOUD] Sent unique ID: ${uniqueId}`);
|
|
89
94
|
|
|
90
95
|
const context = new Context(cloudSocket, master, masterPort, server, serverPort, uniqueId);
|
|
91
96
|
gCloudConnections.push(context);
|
|
92
97
|
log(`[PROXY] → [CLOUD] New free connection #${gCloudConnections.length} to the Cloud at ${server}:${serverPort}`);
|
|
93
98
|
});
|
|
94
99
|
|
|
100
|
+
cloudSocket.on('error', (err) => {
|
|
101
|
+
error(`[PROXY] → [CLOUD] Failed to connect to Cloud: ${err.message}`);
|
|
102
|
+
});
|
|
103
|
+
|
|
95
104
|
log(`[PROXY] → [CLOUD] Attempting to start a new free connection for ${config.uniqueId} to the Cloud at ${server}:${serverPort}`);
|
|
96
105
|
}
|
|
97
106
|
|
|
107
|
+
|
|
98
108
|
////////////////
|
|
99
109
|
// Restarting //
|
|
100
110
|
////////////////
|
|
@@ -105,7 +115,7 @@ export function cleanStart(restart: boolean = false) {
|
|
|
105
115
|
context.deviceSocket.end();
|
|
106
116
|
context.deviceSocket = null;
|
|
107
117
|
}
|
|
108
|
-
context.cloudSocket.
|
|
118
|
+
context.cloudSocket.end();
|
|
109
119
|
context.cloudSocket = null;
|
|
110
120
|
});
|
|
111
121
|
gCloudConnections.splice(0, gCloudConnections.length); // clear the array
|
|
@@ -129,14 +139,14 @@ export class Context {
|
|
|
129
139
|
master: string;
|
|
130
140
|
masterPort: number;
|
|
131
141
|
|
|
132
|
-
cloudSocket:
|
|
142
|
+
cloudSocket: net.Socket;
|
|
133
143
|
server: string;
|
|
134
144
|
serverPort: number;
|
|
135
145
|
|
|
136
146
|
// unique identifier for this device-gateway combination
|
|
137
147
|
uniqueId: string;
|
|
138
148
|
|
|
139
|
-
constructor(cloudSocket:
|
|
149
|
+
constructor(cloudSocket: net.Socket, master: string, masterPort: number, server: string, serverPort: number, uniqueId: string) {
|
|
140
150
|
this.deviceSocket = null;
|
|
141
151
|
this.master = master;
|
|
142
152
|
this.masterPort = masterPort;
|
|
@@ -152,12 +162,12 @@ export class Context {
|
|
|
152
162
|
|
|
153
163
|
setupCloudSocket() {
|
|
154
164
|
// set up handlers for the cloud socket
|
|
155
|
-
this.cloudSocket.on('
|
|
156
|
-
this.handleDataFromCloud(
|
|
165
|
+
this.cloudSocket.on('data', data => {
|
|
166
|
+
this.handleDataFromCloud(data as Buffer);
|
|
157
167
|
});
|
|
158
168
|
|
|
159
|
-
this.cloudSocket.on('close', (
|
|
160
|
-
warning('[CLOUD] Connection closed
|
|
169
|
+
this.cloudSocket.on('close', () => {
|
|
170
|
+
warning('[CLOUD] Connection closed');
|
|
161
171
|
if (this.deviceSocket) {
|
|
162
172
|
this.deviceSocket.end();
|
|
163
173
|
this.deviceSocket = null;
|
|
@@ -174,28 +184,33 @@ export class Context {
|
|
|
174
184
|
}
|
|
175
185
|
|
|
176
186
|
handleDataFromCloud(data: Buffer) {
|
|
177
|
-
debug(`[CLOUD] → [PROXY]: Data from Cloud: ${data.toString()
|
|
187
|
+
debug(`[CLOUD] → [PROXY]: Data from Cloud: ${data.toString()}`);
|
|
178
188
|
|
|
179
189
|
if (!this.deviceSocket) {
|
|
180
190
|
// we either have a real client that wants to connect to the device,
|
|
181
191
|
// or we receive a heartbeat from the cloud server
|
|
192
|
+
// or we receive a connection response [OK], [OK-=2], [ERROR-...]
|
|
182
193
|
|
|
183
194
|
if (this.isHeartbeatRequest(data)) {
|
|
184
195
|
debug(`[CLOUD] → [PROXY]: Received heartbeat from Cloud, returning response.`);
|
|
185
196
|
// respond to the heartbeat of the cloud server
|
|
186
|
-
this.cloudSocket.
|
|
197
|
+
this.cloudSocket.write("[72,3]"); // command([Command.HeartbeatStatus, CommandMethod.Heartbeat.server])
|
|
187
198
|
|
|
199
|
+
} else if (this.isConnectionResponse(data)) {
|
|
200
|
+
debug(`[CLOUD] → [PROXY]: Received connection response from Cloud: ${data.toString()}`);
|
|
201
|
+
// Handle connection response from the cloud server
|
|
202
|
+
|
|
188
203
|
} else {
|
|
189
|
-
// There is incomming data, it's a fresh connection, so: a new client wants to connect to the device
|
|
204
|
+
// There is real incomming data, it's a fresh connection, so: a new client wants to connect to the device
|
|
190
205
|
// 1) create a new (free) connection to the cloud server for the next client
|
|
191
206
|
// 2) connect to the device and send the initial data
|
|
192
|
-
log(`[PROXY] → [CLOUD] New client connection, creating new free connection for this proxy/device: ${
|
|
207
|
+
log(`[PROXY] → [CLOUD] New client connection, creating new free connection for this proxy/device: ${config.uniqueId}.`);
|
|
193
208
|
makeNewCloudConnection(this.master, this.masterPort, this.server, this.serverPort, this.uniqueId);
|
|
194
209
|
this.makeDeviceConnection(this.master, this.masterPort, data);
|
|
195
210
|
}
|
|
196
211
|
|
|
197
212
|
} else if (this.deviceSocket.readyState === 'open') {
|
|
198
|
-
this.deviceSocket.write(data);
|
|
213
|
+
this.deviceSocket.write(new Uint8Array(data));
|
|
199
214
|
debug(`[PROXY] → [DEVICE] forwarding data to device: ${data.toString().slice(0, -1)}`);
|
|
200
215
|
|
|
201
216
|
} else {
|
|
@@ -212,17 +227,23 @@ export class Context {
|
|
|
212
227
|
|
|
213
228
|
// Connect to local device and send the data that came in from the cloud
|
|
214
229
|
this.deviceSocket.connect(port, address, () => {
|
|
230
|
+
// Check if socket is still valid (not cleaned up by removeConnection)
|
|
231
|
+
if (!this.deviceSocket) {
|
|
232
|
+
warning(`[PROXY] → [DEVICE] Device socket was cleaned up before connection completed`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
215
236
|
log(`[PROXY] → [DEVICE] Connected to local device at ${address}:${port}`);
|
|
216
237
|
|
|
217
238
|
this.setUpDeviceSocket();
|
|
218
239
|
|
|
219
240
|
log(`[PROXY] → [DEVICE] Sending initial message: ${data.toString().slice(0, -1)}`);
|
|
220
|
-
this.deviceSocket.write(data);
|
|
241
|
+
this.deviceSocket.write(new Uint8Array(data));
|
|
221
242
|
});
|
|
222
243
|
|
|
223
244
|
} else if (this.deviceSocket.readyState === 'open') {
|
|
224
245
|
warning(`[PROXY] → [DEVICE] Device socket already open, sending data directly -- STRANGE !!`);
|
|
225
|
-
this.deviceSocket.write(data);
|
|
246
|
+
this.deviceSocket.write(new Uint8Array(data));
|
|
226
247
|
|
|
227
248
|
} else {
|
|
228
249
|
error(`[PROXY] → [DEVICE] Device socket exists, but is not open yet !!`);
|
|
@@ -230,10 +251,15 @@ export class Context {
|
|
|
230
251
|
}
|
|
231
252
|
|
|
232
253
|
setUpDeviceSocket() {
|
|
254
|
+
if (!this.deviceSocket) {
|
|
255
|
+
error(`[PROXY] → [DEVICE] Cannot set up device socket - socket is null`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
233
259
|
this.deviceSocket.on('data', (data: Buffer) => {
|
|
234
260
|
const message = data.toString('utf-8');
|
|
235
261
|
debug(`[DEVICE] → [PROXY] forwarding data to Cloud: ${message.slice(0, -1)}`);
|
|
236
|
-
this.cloudSocket.
|
|
262
|
+
this.cloudSocket.write(message);
|
|
237
263
|
});
|
|
238
264
|
|
|
239
265
|
this.deviceSocket.on('close', () => {
|
|
@@ -248,12 +274,18 @@ export class Context {
|
|
|
248
274
|
}
|
|
249
275
|
|
|
250
276
|
removeConnection() {
|
|
251
|
-
|
|
277
|
+
// Clean up device socket
|
|
278
|
+
if (this.deviceSocket) {
|
|
279
|
+
if (!this.deviceSocket.destroyed) {
|
|
280
|
+
this.deviceSocket.end();
|
|
281
|
+
}
|
|
282
|
+
this.deviceSocket = null;
|
|
283
|
+
}
|
|
252
284
|
|
|
253
285
|
// remove this context and close the cloud socket
|
|
254
286
|
const index = gCloudConnections.indexOf(this);
|
|
255
287
|
if (index !== -1) {
|
|
256
|
-
this.cloudSocket.
|
|
288
|
+
this.cloudSocket.end();
|
|
257
289
|
this.cloudSocket = null;
|
|
258
290
|
gCloudConnections.splice(index, 1);
|
|
259
291
|
log(`[PROXY] → [CLOUD] Closed socket to Cloud and removed context for device ${this.master}`);
|
|
@@ -262,7 +294,6 @@ export class Context {
|
|
|
262
294
|
if (gCloudConnections.length === 0) {
|
|
263
295
|
log(`[PROXY] → [CLOUD] No more cloud connections, restarting proxy.`);
|
|
264
296
|
// just to be sure: restart...
|
|
265
|
-
|
|
266
297
|
if (config.kind === "pm2") {
|
|
267
298
|
// PM2 should restart us.
|
|
268
299
|
log(`[PROXY] → [CLOUD] PM2 should restart us, exiting now.`);
|
|
@@ -297,19 +328,21 @@ export class Context {
|
|
|
297
328
|
|
|
298
329
|
// check if it's a heartbeat request from the server
|
|
299
330
|
isHeartbeatRequest(data: Buffer): boolean {
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
331
|
+
// hearbeatKind = (poll = 1, poll_old = 2, serve = 3)
|
|
332
|
+
return ((data[0] === 91) && // [
|
|
333
|
+
(data[1] === 50) && // 2
|
|
334
|
+
(data[2] === 49) && // 1
|
|
335
|
+
(data[3] === 53) && // 5
|
|
336
|
+
(data[4] === 44) && // ,
|
|
337
|
+
(data[5] === 51) && // 3
|
|
338
|
+
(data[6] === 93)); // ]
|
|
339
|
+
}
|
|
304
340
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
(data[5] === 51) && // 3
|
|
312
|
-
(data[6] === 93)); // ]
|
|
313
|
-
}
|
|
341
|
+
// check if it's a server response after connecting and sending our uniqueID
|
|
342
|
+
isConnectionResponse(data: Buffer): boolean {
|
|
343
|
+
const message = data.toString();
|
|
344
|
+
|
|
345
|
+
// Check for various server response patterns
|
|
346
|
+
return (message.startsWith('[OK')) || (message.startsWith('[ERROR'));
|
|
314
347
|
}
|
|
315
348
|
}
|