node-red-contrib-homebridge-automation 0.1.12-beta.11 → 0.1.12-beta.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-homebridge-automation",
3
- "version": "0.1.12-beta.11",
3
+ "version": "0.1.12-beta.13",
4
4
  "description": "NodeRED Automation for HomeBridge",
5
5
  "main": "src/HAP-NodeRed.js",
6
6
  "scripts": {
@@ -62,7 +62,7 @@
62
62
  ],
63
63
  "ext": "js,html",
64
64
  "ignore": [],
65
- "exec": "DEBUG=hapNodeRed ~/npm/bin/node-red -u test/node-red",
65
+ "exec": "npm run lint && DEBUG=hapNodeRed ~/npm/bin/node-red -u test/node-red",
66
66
  "signal": "SIGTERM",
67
67
  "env": {
68
68
  "NODE_OPTIONS": "--trace-warnings"
@@ -3,6 +3,8 @@ var Queue = require('better-queue');
3
3
  // var register = require('./lib/register.js');
4
4
 
5
5
  const HBConfNode = require('./hbConfigNode');
6
+ const HbEventNode = require('./hbEventNode'); // Import the class
7
+ const HbResumeNode = require('./hbResumeNode'); // Import the class
6
8
 
7
9
  module.exports = function (RED) {
8
10
  var evDevices = [];
@@ -54,129 +56,14 @@ module.exports = function (RED) {
54
56
  * @return {type} description
55
57
  */
56
58
 
57
- function hbEvent(n) {
58
- // debug("hbEvent", n);
59
+ function hbEventNode(n) {
59
60
  RED.nodes.createNode(this, n);
60
- this.conf = RED.nodes.getNode(n.conf);
61
- this.confId = n.conf;
62
- this.device = n.device;
63
- this.service = n.Service;
64
- this.name = n.name;
65
- this.fullName = n.name + ' - ' + n.Service;
66
- this.sendInitialState = n.sendInitialState === true;
67
- this.state = {};
68
-
69
- var node = this;
70
-
71
- node.command = function (event) {
72
- // False messages can be received from accessories with multiple services
73
- // if (Object.keys(_convertHBcharactericToNode(event, node)).length > 0) {
74
- // debug("hbEvent", node.name, event);
75
- if (event.status === true && event.value !== undefined) {
76
- node.state = Object.assign(node.state, _convertHBcharactericToNode([event], node));
77
- var msg = {
78
- name: node.name,
79
- payload: node.state,
80
- Homebridge: node.hbDevice.homebridge,
81
- Manufacturer: node.hbDevice.manufacturer,
82
- Service: node.hbDevice.deviceType,
83
- _device: node.device,
84
- _confId: node.confId,
85
- _rawEvent: event
86
- };
87
- node.status({
88
- text: JSON.stringify(msg.payload).slice(0, 30) + '...',
89
- shape: 'dot',
90
- fill: 'green'
91
- });
92
- clearTimeout(node.timeout);
93
- node.timeout = setTimeout(function () {
94
- node.status({});
95
- }, 10 * 1000);
96
- node.send(msg);
97
- } else if (event.status === true) {
98
- node.status({
99
- text: 'connected',
100
- shape: 'dot',
101
- fill: 'green'
102
- });
103
- } else {
104
- node.status({
105
- text: 'disconnected: ' + event.status,
106
- shape: 'ring',
107
- fill: 'red'
108
- });
109
- }
110
- };
111
- // };
112
-
113
- node.conf.register(node, function () {
114
- debug("hbEvent.register", node.fullName);
115
- this.hbDevice = hbDevices.findDevice(node.device, {
116
- perms: 'pr'
117
- });
118
- if (this.hbDevice) {
119
- node.hbDevice = this.hbDevice;
120
- node.deviceType = this.hbDevice.deviceType;
121
-
122
- _status(node.device, node, {
123
- perms: 'ev'
124
- }, function (err, message) {
125
- if (!err) {
126
- node.state = _convertHBcharactericToNode(message.characteristics, node);
127
- debug("hbEvent received: %s = %s", node.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
128
- if (node.sendInitialState) {
129
- var msg = {
130
- name: node.name,
131
- payload: node.state,
132
- Homebridge: node.hbDevice.homebridge,
133
- Manufacturer: node.hbDevice.manufacturer,
134
- Service: node.hbDevice.deviceType,
135
- _device: node.device,
136
- _confId: node.confId,
137
- _rawMessage: message,
138
- };
139
- node.status({
140
- text: JSON.stringify(msg.payload).slice(0, 30) + '...',
141
- shape: 'dot',
142
- fill: 'green'
143
- });
144
- clearTimeout(node.timeout);
145
- node.timeout = setTimeout(function () {
146
- node.status({});
147
- }, 10 * 1000);
148
- node.send(msg);
149
- }
150
- } else {
151
- node.error("hbEvent _status: error", node.fullName, err);
152
- }
153
- });
154
- // Register for events
155
- node.listener = node.command;
156
- node.eventName = [];
157
- // node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
158
- // debug("DEVICE", this.hbDevice);
159
- this.hbDevice.eventRegisters.forEach(function (event) {
160
- homebridge.on(node.hbDevice.id + event.aid + event.iid, node.command);
161
- node.eventName.push(node.hbDevice.id + event.aid + event.iid);
162
- });
163
- // homebridge.on(this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid, node.command);
164
- node.status({
165
- text: 'connected',
166
- shape: 'dot',
167
- fill: 'green'
168
- });
169
- } else {
170
- node.error("197:Can't find device " + node.device, null);
171
- }
172
- }.bind(this));
173
-
174
- node.on('close', function (callback) {
175
- node.conf.deregister(node, callback);
176
- });
61
+
62
+ // Create instance of HbEventNode class to handle events
63
+ new HbEventNode(this, n); // Pass current node and config object
177
64
  }
178
65
 
179
- RED.nodes.registerType("hb-event", hbEvent);
66
+ RED.nodes.registerType("hb-event", hbEventNode);
180
67
 
181
68
  /**
182
69
  * hbResume - description
@@ -199,182 +86,14 @@ module.exports = function (RED) {
199
86
  * @return {type} description
200
87
  */
201
88
 
202
- function hbResume(n) {
89
+ function hbResumeNode(n) {
203
90
  RED.nodes.createNode(this, n);
204
- this.conf = RED.nodes.getNode(n.conf);
205
- this.confId = n.conf;
206
- this.device = n.device;
207
- this.service = n.Service;
208
- this.name = n.name;
209
- this.fullName = n.name + ' - ' + n.Service;
210
- var node = this;
211
-
212
- node.state = null;
213
- node.lastMessageTime = null;
214
- node.lastMessageValue = null;
215
- node.lastPayload = {
216
- On: false
217
- };
218
-
219
- node.on('input', function (msg) {
220
- this.msg = msg;
221
- debug("hbResume.input: %s input", node.fullName, JSON.stringify(msg));
222
- if (typeof msg.payload === "object") {
223
- // Using this to validate input message contains valid Accessory Characteristics
224
- if (node.hbDevice) { // not populated until initialization is complete
225
- var message = _createControlMessage.call(this, msg.payload, node, node.hbDevice);
226
-
227
- if (message.characteristics.length > 0) {
228
- var newMsg;
229
- if (!msg.payload.On) {
230
- // false / Turn Off
231
- // debug("hbResume-Node lastPayload %s", JSON.stringify(node.lastPayload));
232
- if (node.lastPayload.On) {
233
- // last msg was on, restore previous state
234
- newMsg = {
235
- name: node.name,
236
- _device: node.device,
237
- _confId: node.confId
238
- };
239
- if (node.hbDevice) {
240
- newMsg.Homebridge = node.hbDevice.homebridge;
241
- newMsg.Manufacturer = node.hbDevice.manufacturer;
242
- newMsg.Type = node.hbDevice.deviceType;
243
- }
244
- newMsg.payload = node.state;
245
- } else {
246
- // last msg was off, pass thru
247
- node.state = JSON.parse(JSON.stringify(msg.payload));
248
- newMsg = msg;
249
- }
250
- } else {
251
- // True / Turn on
252
- newMsg = msg;
253
- }
254
- // Off messages should not include brightness
255
- node.send((newMsg.payload.On ? newMsg : newMsg.payload = {
256
- On: false
257
- }, newMsg));
258
- debug("hbResume.input: %s output", node.fullName, JSON.stringify(newMsg));
259
- node.status({
260
- text: JSON.stringify(newMsg.payload).slice(0, 30) + '...',
261
- shape: 'dot',
262
- fill: 'green'
263
- });
264
- clearTimeout(node.timeout);
265
- node.timeout = setTimeout(function () {
266
- node.status({});
267
- }, 10 * 1000);
268
- node.lastMessageValue = newMsg.payload;
269
- node.lastMessageTime = Date.now();
270
- // debug("hbResume.input: %s updating lastPayload %s", node.fullName, JSON.stringify(msg.payload));
271
- node.lastPayload = JSON.parse(JSON.stringify(msg.payload)); // store value not reference
272
- }
273
- } else {
274
- node.error("Homebridge not initialized - 1", this.msg);
275
- node.status({
276
- text: 'Homebridge not initialized -1',
277
- shape: 'ring',
278
- fill: 'red'
279
- });
280
- }
281
- } else {
282
- node.error("Payload should be an JSON object containing device characteristics and values, ie {\"On\":false, \"Brightness\":0 }\nValid values include: " + node.hbDevice.descriptions, this.msg);
283
- node.status({
284
- text: 'Invalid payload',
285
- shape: 'ring',
286
- fill: 'red'
287
- });
288
- }
289
- });
290
-
291
- node.command = function (event) {
292
- // debug("hbResume received event: %s ->", node.fullName, event);
293
- // debug("hbResume - internals %s millis, old %s, event %s, previous %s", Date.now() - node.lastMessageTime, node.lastMessageValue, event.status, node.state);
294
- // Don't update for events originating from here
295
- // if Elapsed is greater than 5 seconds, update stored state
296
- // if Elapsed is less then 5, and lastMessage doesn't match event update stored state
297
-
298
- var payload = Object.assign({}, node.state);
299
-
300
- // debug("should be true", _getObjectDiff(payload, node.state).length);
301
-
302
- payload = Object.assign(payload, _convertHBcharactericToNode([event], node));
303
-
304
- // debug("should be false", _getObjectDiff(payload, node.state).length);
305
-
306
- debug("hbResume.event: %s %s -> %s", node.fullName, JSON.stringify(node.state), JSON.stringify(payload));
307
-
308
- if (event.status === true && event.value !== undefined) {
309
- if ((Date.now() - node.lastMessageTime) > 5000) {
310
- debug("hbResume.update: %s - updating stored event >5", node.fullName, payload);
311
- node.state = JSON.parse(JSON.stringify(payload));
312
- } else if (_getObjectDiff(payload, node.lastMessageValue).length > 0) {
313
- // debug("hbResume - updating stored event !=", payload, node.lastMessageValue);
314
- // node.state = payload;
315
- }
316
- } else if (event.status === true) {
317
- node.status({
318
- text: 'connected',
319
- shape: 'dot',
320
- fill: 'green'
321
- });
322
- } else {
323
- node.status({
324
- text: 'disconnected: ' + event.status,
325
- shape: 'ring',
326
- fill: 'red'
327
- });
328
- }
329
- };
330
-
331
- node.conf.register(node, function () {
332
- debug("hbResume.register:", node.fullName);
333
- this.hbDevice = hbDevices.findDevice(node.device, {
334
- perms: 'pw'
335
- });
336
- if (this.hbDevice) {
337
- _status(node.device, node, {
338
- perms: 'pw'
339
- }, function (err, message) {
340
- if (!err) {
341
- node.state = _convertHBcharactericToNode(message.characteristics, node);
342
- debug("hbResume received: %s = %s", node.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
343
- } else {
344
- node.error(err);
345
- }
346
- });
347
- node.hbDevice = this.hbDevice;
348
- node.deviceType = this.hbDevice.deviceType;
349
- // Register for events
350
- node.listener = node.command;
351
- node.eventName = [];
352
- // node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
353
- // homebridge.on(this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid, node.command);
354
- this.hbDevice.eventRegisters.forEach(function (event) {
355
- homebridge.on(node.hbDevice.id + event.aid + event.iid, node.command);
356
- node.eventName.push(node.hbDevice.id + event.aid + event.iid);
357
- });
358
- node.status({
359
- text: 'connected',
360
- shape: 'dot',
361
- fill: 'green'
362
- });
363
- clearTimeout(node.timeout);
364
- node.timeout = setTimeout(function () {
365
- node.status({});
366
- }, 30 * 1000);
367
- } else {
368
- node.error("365:Can't find device " + node.device, null);
369
- }
370
- }.bind(this));
371
-
372
- node.on('close', function (callback) {
373
- node.conf.deregister(node, callback);
374
- });
91
+
92
+ // Create instance of HbEventNode class to handle events
93
+ new HbResumeNode(this, n); // Pass current node and config object
375
94
  }
376
95
 
377
- RED.nodes.registerType("hb-resume", hbResume);
96
+ RED.nodes.registerType("hb-resume", hbResumeNode);
378
97
 
379
98
  /**
380
99
  * hbControl - description
@@ -1,32 +1,32 @@
1
1
  const HAPNodeJSClient = require('hap-node-client').HAPNodeJSClient;
2
2
  const debug = require('debug')('hapNodeRed:hbConfigNode');
3
- var Homebridges = require('./lib/Homebridges.js').Homebridges;
3
+ const { Homebridges } = require('./lib/Homebridges.js');
4
4
 
5
5
  class HBConfNode {
6
- constructor(n, RED) {
6
+ constructor(nodeConfig, RED) {
7
7
  this.RED = RED;
8
- this.username = n.username;
9
- this.macAddress = n.macAddress || '';
10
- this.password = n.credentials.password;
8
+ this.username = nodeConfig.username;
9
+ this.macAddress = nodeConfig.macAddress || '';
10
+ this.password = nodeConfig.credentials.password;
11
11
  this.users = {};
12
12
  this.homebridge = null;
13
13
 
14
- this.initHomebridge(n);
14
+ this.initHomebridge(nodeConfig);
15
15
  }
16
16
 
17
- initHomebridge(n) {
17
+ initHomebridge(nodeConfig) {
18
18
  if (this.homebridge) {
19
19
  if (this.macAddress) {
20
20
  // Register additional PIN on existing instance
21
- this.homebridge.RegisterPin(this.macAddress, n.username);
21
+ this.homebridge.RegisterPin(this.macAddress, nodeConfig.username);
22
22
  }
23
23
  } else {
24
24
  this.homebridge = new HAPNodeJSClient({
25
- pin: n.username,
25
+ pin: nodeConfig.username,
26
26
  refresh: 900,
27
27
  debug: false,
28
28
  timeout: 20,
29
- reqTimeout: 7000
29
+ reqTimeout: 7000,
30
30
  });
31
31
  this.homebridge.on('Ready', this.handleReady.bind(this));
32
32
  }
@@ -36,58 +36,59 @@ class HBConfNode {
36
36
  const hbDevices = new Homebridges(accessories);
37
37
  debug('Discovered %s new evDevices', hbDevices.toList({ perms: 'ev' }).length);
38
38
 
39
- let list = hbDevices.toList({ perms: 'ev' });
39
+ const list = hbDevices.toList({ perms: 'ev' });
40
40
  this.handleDuplicates(list);
41
41
  }
42
42
 
43
43
  handleDuplicates(list) {
44
- let deleteSeen = [];
44
+ const deleteSeen = new Set();
45
45
 
46
- for (let i = 0; i < list.length; i++) {
47
- const endpoint = list[i];
48
- if (deleteSeen[endpoint.fullName]) {
49
- console.log("WARNING: Duplicate device name", endpoint.fullName);
46
+ for (const endpoint of list) {
47
+ if (deleteSeen.has(endpoint.fullName)) {
48
+ console.warn('WARNING: Duplicate device name', endpoint.fullName);
50
49
  } else {
51
- deleteSeen[endpoint.fullName] = true;
50
+ deleteSeen.add(endpoint.fullName);
52
51
  }
53
52
  }
54
53
 
55
- deleteSeen = [];
54
+ deleteSeen.clear();
56
55
 
57
- for (let i = 0; i < list.length; i++) {
58
- const endpoint = list[i];
59
- if (deleteSeen[endpoint.uniqueId]) {
60
- console.log("ERROR: Parsing failed, duplicate uniqueID.", endpoint.fullName);
56
+ for (const endpoint of list) {
57
+ if (deleteSeen.has(endpoint.uniqueId)) {
58
+ console.error('ERROR: Parsing failed, duplicate uniqueID.', endpoint.fullName);
61
59
  } else {
62
- deleteSeen[endpoint.uniqueId] = true;
60
+ deleteSeen.add(endpoint.uniqueId);
63
61
  }
64
62
  }
65
63
  }
66
64
 
67
65
  register(deviceNode, callback) {
68
- debug("hbConf.register", deviceNode.fullName);
66
+ debug('hbConf.register', deviceNode.fullName);
69
67
  this.users[deviceNode.id] = deviceNode;
70
- debug("Register %s -> %s", deviceNode.type, deviceNode.fullName);
71
- reqisterQueue.push({
72
- that: this,
73
- device: deviceNode.device,
74
- type: deviceNode.type,
75
- name: deviceNode.name,
76
- fullName: deviceNode.fullName,
77
- node: this
78
- }, callback);
68
+ debug('Register %s -> %s', deviceNode.type, deviceNode.fullName);
69
+ reqisterQueue.push(
70
+ {
71
+ that: this,
72
+ device: deviceNode.device,
73
+ type: deviceNode.type,
74
+ name: deviceNode.name,
75
+ fullName: deviceNode.fullName,
76
+ node: this,
77
+ },
78
+ callback
79
+ );
79
80
  }
80
81
 
81
82
  deregister(deviceNode, callback) {
82
83
  deviceNode.status({
83
84
  text: 'disconnected',
84
85
  shape: 'ring',
85
- fill: 'red'
86
+ fill: 'red',
86
87
  });
87
88
 
88
- deviceNode.eventName.forEach((event) => {
89
+ for (const event of deviceNode.eventName) {
89
90
  this.homebridge.removeListener(event, deviceNode.listener);
90
- });
91
+ }
91
92
 
92
93
  callback();
93
94
  }
@@ -0,0 +1,58 @@
1
+ var debug = require('debug')('hapNodeRed:hbControlNode');
2
+
3
+ function hbControl(n) {
4
+ RED.nodes.createNode(this, n);
5
+ this.conf = RED.nodes.getNode(n.conf); // The configuration node
6
+ this.confId = n.conf;
7
+ this.device = n.device;
8
+ this.service = n.Service;
9
+ this.name = n.name;
10
+ this.fullName = n.name + ' - ' + n.Service;
11
+
12
+ var node = this;
13
+
14
+ node.on('input', function (msg) {
15
+ this.msg = msg;
16
+ _control.call(this, node, msg.payload, function (err, data) {
17
+ // debug('hbControl complete [%s] - [%s]', node, node.hbDevice); // Images produce alot of noise
18
+ if (!err && data && (node.deviceType == '00000110' || node.deviceType == '00000111')) {
19
+ const msg = {
20
+ name: node.name,
21
+ payload: node.state,
22
+ _device: node.device,
23
+ _confId: node.confId
24
+ };
25
+ if (node.hbDevice) {
26
+ msg.Homebridge = node.hbDevice.homebridge;
27
+ msg.Manufacturer = node.hbDevice.manufacturer;
28
+ msg.Service = node.hbDevice.deviceType;
29
+ }
30
+ msg.payload = data;
31
+ node.send(msg);
32
+ } else if (err) {
33
+ node.error(err, this.msg);
34
+ }
35
+ }.bind(this));
36
+
37
+ });
38
+
39
+ node.on('close', function (callback) {
40
+ callback();
41
+ });
42
+
43
+ node.conf.register(node, function () {
44
+ debug("hbControl.register:", node.fullName);
45
+ this.hbDevice = hbDevices.findDevice(node.device);
46
+ // console.log('hbControl Register', this.hbDevice)
47
+ if (this.hbDevice) {
48
+ node.hbDevice = this.hbDevice;
49
+ node.deviceType = this.hbDevice.type;
50
+ // Register for events
51
+ node.listener = node.command;
52
+ // node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
53
+ } else {
54
+ node.error("437:Can't find device " + node.device, null);
55
+ // this.error("Missing device " + node.device);
56
+ }
57
+ });
58
+ }
@@ -1,130 +1,135 @@
1
- /**
2
- * hbEventNode - Node that listens to HomeKit Events, and sends message into NodeRED
3
- *
4
- * @param {type} n description
5
- * @return {type} description
6
- */
1
+ const debug = require('debug')('hapNodeRed:hbEventNode');
7
2
 
8
- function hbEventNode(n) {
9
- // debug("hbEvent", n);
10
- RED.nodes.createNode(this, n);
11
- this.conf = RED.nodes.getNode(n.conf);
12
- this.confId = n.conf;
13
- this.device = n.device;
14
- this.service = n.Service;
15
- this.name = n.name;
16
- this.fullName = n.name + ' - ' + n.Service;
17
- this.sendInitialState = n.sendInitialState === true;
18
- this.state = {};
3
+ class HbEventNode {
4
+ constructor(nodeConfig, RED) {
5
+ this.RED = RED;
6
+ this.node = nodeConfig;
7
+ this.conf = RED.nodes.getNode(nodeConfig.conf);
8
+ this.confId = nodeConfig.conf;
9
+ this.device = nodeConfig.device;
10
+ this.service = nodeConfig.Service;
11
+ this.name = nodeConfig.name;
12
+ this.fullName = `${nodeConfig.name} - ${nodeConfig.Service}`;
13
+ this.sendInitialState = nodeConfig.sendInitialState === true;
14
+ this.state = {};
19
15
 
20
- var node = this;
16
+ this.init();
17
+ }
21
18
 
22
- node.command = function (event) {
23
- // False messages can be received from accessories with multiple services
24
- // if (Object.keys(_convertHBcharactericToNode(event, node)).length > 0) {
25
- // debug("hbEvent", node.name, event);
19
+ // Initialize the event handling logic
20
+ init() {
21
+ this.node.command = this.command.bind(this);
22
+
23
+ // Register the node with the HbConf class
24
+ this.conf.register(this.node, this.handleDeviceRegistration.bind(this));
25
+
26
+ // Clean up when the node is closed
27
+ this.node.on('close', (callback) => {
28
+ this.conf.deregister(this.node, callback);
29
+ });
30
+ }
31
+
32
+ // Handle event command processing
33
+ command(event) {
26
34
  if (event.status === true && event.value !== undefined) {
27
- node.state = Object.assign(node.state, _convertHBcharactericToNode([event], node));
28
- var msg = {
29
- name: node.name,
30
- payload: node.state,
31
- Homebridge: node.hbDevice.homebridge,
32
- Manufacturer: node.hbDevice.manufacturer,
33
- Service: node.hbDevice.deviceType,
34
- _device: node.device,
35
- _confId: node.confId,
36
- _rawEvent: event
35
+ this.state = Object.assign(this.state, _convertHBcharactericToNode([event], this.node));
36
+ const msg = {
37
+ name: this.node.name,
38
+ payload: this.state,
39
+ Homebridge: this.node.hbDevice.homebridge,
40
+ Manufacturer: this.node.hbDevice.manufacturer,
41
+ Service: this.node.hbDevice.deviceType,
42
+ _device: this.node.device,
43
+ _confId: this.node.confId,
44
+ _rawEvent: event,
37
45
  };
38
- node.status({
39
- text: JSON.stringify(msg.payload).slice(0, 30) + '...',
46
+ this.node.status({
47
+ text: `${JSON.stringify(msg.payload).slice(0, 30)}...`,
40
48
  shape: 'dot',
41
- fill: 'green'
49
+ fill: 'green',
42
50
  });
43
- clearTimeout(node.timeout);
44
- node.timeout = setTimeout(function () {
45
- node.status({});
51
+ clearTimeout(this.node.timeout);
52
+ this.node.timeout = setTimeout(() => {
53
+ this.node.status({});
46
54
  }, 10 * 1000);
47
- node.send(msg);
55
+ this.node.send(msg);
48
56
  } else if (event.status === true) {
49
- node.status({
57
+ this.node.status({
50
58
  text: 'connected',
51
59
  shape: 'dot',
52
- fill: 'green'
60
+ fill: 'green',
53
61
  });
54
62
  } else {
55
- node.status({
56
- text: 'disconnected: ' + event.status,
63
+ this.node.status({
64
+ text: `disconnected: ${event.status}`,
57
65
  shape: 'ring',
58
- fill: 'red'
66
+ fill: 'red',
59
67
  });
60
68
  }
61
- };
62
- // };
69
+ }
63
70
 
64
- node.conf.register(node, function () {
65
- debug("hbEvent.register", node.fullName);
66
- this.hbDevice = hbDevices.findDevice(node.device, {
67
- perms: 'pr'
68
- });
69
- if (this.hbDevice) {
70
- node.hbDevice = this.hbDevice;
71
- node.deviceType = this.hbDevice.deviceType;
71
+ // Handle device registration logic
72
+ handleDeviceRegistration() {
73
+ debug('hbEvent.register', this.node.fullName);
74
+ this.node.hbDevice = hbDevices.findDevice(this.node.device, { perms: 'pr' });
75
+
76
+ if (this.node.hbDevice) {
77
+ this.node.deviceType = this.node.hbDevice.deviceType;
72
78
 
73
- _status(node.device, node, {
74
- perms: 'ev'
75
- }, function (err, message) {
79
+ _status(this.node.device, this.node, { perms: 'ev' }, (err, message) => {
76
80
  if (!err) {
77
- node.state = _convertHBcharactericToNode(message.characteristics, node);
78
- debug("hbEvent received: %s = %s", node.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
79
- if (node.sendInitialState) {
80
- var msg = {
81
- name: node.name,
82
- payload: node.state,
83
- Homebridge: node.hbDevice.homebridge,
84
- Manufacturer: node.hbDevice.manufacturer,
85
- Service: node.hbDevice.deviceType,
86
- _device: node.device,
87
- _confId: node.confId,
81
+ this.state = _convertHBcharactericToNode(message.characteristics, this.node);
82
+ debug(
83
+ 'hbEvent received: %s = %s',
84
+ this.node.fullName,
85
+ `${JSON.stringify(message.characteristics).slice(0, 80)}...`
86
+ );
87
+
88
+ if (this.sendInitialState) {
89
+ const msg = {
90
+ name: this.node.name,
91
+ payload: this.state,
92
+ Homebridge: this.node.hbDevice.homebridge,
93
+ Manufacturer: this.node.hbDevice.manufacturer,
94
+ Service: this.node.hbDevice.deviceType,
95
+ _device: this.node.device,
96
+ _confId: this.node.confId,
88
97
  _rawMessage: message,
89
98
  };
90
- node.status({
91
- text: JSON.stringify(msg.payload).slice(0, 30) + '...',
99
+ this.node.status({
100
+ text: `${JSON.stringify(msg.payload).slice(0, 30)}...`,
92
101
  shape: 'dot',
93
- fill: 'green'
102
+ fill: 'green',
94
103
  });
95
- clearTimeout(node.timeout);
96
- node.timeout = setTimeout(function () {
97
- node.status({});
104
+ clearTimeout(this.node.timeout);
105
+ this.node.timeout = setTimeout(() => {
106
+ this.node.status({});
98
107
  }, 10 * 1000);
99
- node.send(msg);
108
+ this.node.send(msg);
100
109
  }
101
110
  } else {
102
- node.error("hbEvent _status: error", node.fullName, err);
111
+ this.node.error('hbEvent _status: error', this.node.fullName, err);
103
112
  }
104
113
  });
114
+
105
115
  // Register for events
106
- node.listener = node.command;
107
- node.eventName = [];
108
- // node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
109
- // debug("DEVICE", this.hbDevice);
110
- this.hbDevice.eventRegisters.forEach(function (event) {
111
- homebridge.on(node.hbDevice.id + event.aid + event.iid, node.command);
112
- node.eventName.push(node.hbDevice.id + event.aid + event.iid);
116
+ this.node.listener = this.node.command;
117
+ this.node.eventName = [];
118
+
119
+ this.node.hbDevice.eventRegisters.forEach((event) => {
120
+ homebridge.on(this.node.hbDevice.id + event.aid + event.iid, this.node.command);
121
+ this.node.eventName.push(this.node.hbDevice.id + event.aid + event.iid);
113
122
  });
114
- // homebridge.on(this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid, node.command);
115
- node.status({
123
+
124
+ this.node.status({
116
125
  text: 'connected',
117
126
  shape: 'dot',
118
- fill: 'green'
127
+ fill: 'green',
119
128
  });
120
129
  } else {
121
- node.error("197:Can't find device " + node.device, null);
130
+ this.node.error(`197:Can't find device ${this.node.device}`, null);
122
131
  }
123
- }.bind(this));
124
-
125
- node.on('close', function (callback) {
126
- node.conf.deregister(node, callback);
127
- });
132
+ }
128
133
  }
129
134
 
130
- RED.nodes.registerType("hb-event", hbEventNode);
135
+ module.exports = HbEventNode;
@@ -0,0 +1,137 @@
1
+ const debug = require('debug')('hapNodeRed:hbResumeNode');
2
+
3
+ class HbResumeNode {
4
+ constructor(nodeConfig, RED) {
5
+ RED.nodes.createNode(this, nodeConfig);
6
+ this.conf = RED.nodes.getNode(nodeConfig.conf);
7
+ this.confId = nodeConfig.conf;
8
+ this.device = nodeConfig.device;
9
+ this.service = nodeConfig.Service;
10
+ this.name = nodeConfig.name;
11
+ this.fullName = `${nodeConfig.name} - ${nodeConfig.Service}`;
12
+ this.state = null;
13
+ this.lastMessageTime = null;
14
+ this.lastMessageValue = null;
15
+ this.lastPayload = { On: false };
16
+ this.timeout = null;
17
+
18
+ this.on('input', this.handleInput.bind(this));
19
+
20
+ this.command = this.handleCommand.bind(this);
21
+
22
+ this.conf.register(this, this.handleDeviceRegistration.bind(this));
23
+
24
+ this.on('close', (callback) => {
25
+ this.conf.deregister(this, callback);
26
+ });
27
+ }
28
+
29
+ handleInput(msg) {
30
+ this.msg = msg;
31
+ debug("hbResume.input: %s input", this.fullName, JSON.stringify(msg));
32
+
33
+ if (typeof msg.payload === "object") {
34
+ if (this.hbDevice) {
35
+ const message = _createControlMessage.call(this, msg.payload, this, this.hbDevice);
36
+
37
+ if (message.characteristics.length > 0) {
38
+ let newMsg;
39
+ if (!msg.payload.On) {
40
+ if (this.lastPayload.On) {
41
+ newMsg = {
42
+ name: this.name,
43
+ _device: this.device,
44
+ _confId: this.confId,
45
+ payload: this.state,
46
+ Homebridge: this.hbDevice?.homebridge,
47
+ Manufacturer: this.hbDevice?.manufacturer,
48
+ Type: this.hbDevice?.deviceType,
49
+ };
50
+ } else {
51
+ this.state = JSON.parse(JSON.stringify(msg.payload));
52
+ newMsg = msg;
53
+ }
54
+ } else {
55
+ newMsg = msg;
56
+ }
57
+
58
+ this.send(newMsg.payload.On ? newMsg : { ...newMsg, payload: { On: false } });
59
+ debug("hbResume.input: %s output", this.fullName, JSON.stringify(newMsg));
60
+ this.updateStatus(newMsg.payload);
61
+ this.lastMessageValue = newMsg.payload;
62
+ this.lastMessageTime = Date.now();
63
+ this.lastPayload = JSON.parse(JSON.stringify(msg.payload));
64
+ }
65
+ } else {
66
+ this.handleError("Homebridge not initialized - 1");
67
+ }
68
+ } else {
69
+ this.handleError(
70
+ "Payload should be a JSON object containing device characteristics and values, e.g., {\"On\":false, \"Brightness\":0 }"
71
+ );
72
+ }
73
+ }
74
+
75
+ handleCommand(event) {
76
+ const payload = { ...this.state, ..._convertHBcharactericToNode([event], this) };
77
+ debug("hbResume.event: %s %s -> %s", this.fullName, JSON.stringify(this.state), JSON.stringify(payload));
78
+
79
+ if (event.status === true && event.value !== undefined) {
80
+ if (Date.now() - this.lastMessageTime > 5000) {
81
+ debug("hbResume.update: %s - updating stored event >5", this.fullName, payload);
82
+ this.state = JSON.parse(JSON.stringify(payload));
83
+ }
84
+ } else if (event.status === true) {
85
+ this.updateStatus({ text: 'connected', shape: 'dot', fill: 'green' });
86
+ } else {
87
+ this.updateStatus({ text: `disconnected: ${event.status}`, shape: 'ring', fill: 'red' });
88
+ }
89
+ }
90
+
91
+ handleDeviceRegistration() {
92
+ debug("hbResume.register:", this.fullName);
93
+ this.hbDevice = hbDevices.findDevice(this.device, { perms: 'pw' });
94
+
95
+ if (this.hbDevice) {
96
+ _status(this.device, this, { perms: 'pw' }, (err, message) => {
97
+ if (!err) {
98
+ this.state = _convertHBcharactericToNode(message.characteristics, this);
99
+ debug("hbResume received: %s = %s", this.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
100
+ } else {
101
+ this.error(err);
102
+ }
103
+ });
104
+
105
+ this.deviceType = this.hbDevice.deviceType;
106
+ this.listener = this.command;
107
+ this.eventName = [];
108
+
109
+ this.hbDevice.eventRegisters.forEach((event) => {
110
+ homebridge.on(this.hbDevice.id + event.aid + event.iid, this.command);
111
+ this.eventName.push(this.hbDevice.id + event.aid + event.iid);
112
+ });
113
+
114
+ this.updateStatus({ text: 'connected', shape: 'dot', fill: 'green' });
115
+ this.resetTimeout(30000);
116
+ } else {
117
+ this.error(`Can't find device ${this.device}`);
118
+ }
119
+ }
120
+
121
+ updateStatus(status) {
122
+ this.status(status);
123
+ this.resetTimeout(10000);
124
+ }
125
+
126
+ resetTimeout(duration) {
127
+ clearTimeout(this.timeout);
128
+ this.timeout = setTimeout(() => this.status({}), duration);
129
+ }
130
+
131
+ handleError(message) {
132
+ this.error(message, this.msg);
133
+ this.updateStatus({ text: message, shape: 'ring', fill: 'red' });
134
+ }
135
+ }
136
+
137
+ module.exports = HbResumeNode;