node-red-contrib-homebridge-automation 0.1.12-beta.9 → 0.2.1-beta.0
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/.github/npm-version-script.js +35 -43
- package/.github/workflows/Build and Publish.yml +81 -75
- package/README.md +7 -4
- package/eslint.config.mjs +34 -0
- package/package.json +34 -25
- package/src/HAP-NodeRed.html +71 -71
- package/src/HAP-NodeRed.js +32 -1082
- package/src/HapDeviceRoutes.js +59 -0
- package/src/hbBaseNode.js +94 -0
- package/src/hbConfigNode.js +239 -0
- package/src/hbConfigNode.test.js +2179 -0
- package/src/hbControlNode.js +77 -0
- package/src/hbEventNode.js +23 -0
- package/src/hbResumeNode.js +63 -0
- package/src/hbStatusNode.js +37 -0
- package/test/node-red/.config.nodes.json +453 -0
- package/test/node-red/.config.nodes.json.backup +453 -0
- package/test/node-red/.config.runtime.json +4 -0
- package/test/node-red/.config.runtime.json.backup +3 -0
- package/test/node-red/.config.users.json +23 -0
- package/test/node-red/.config.users.json.backup +20 -0
- package/test/node-red/.flows.json.backup +2452 -0
- package/test/node-red/flows.json +2453 -0
- package/test/node-red/package.json +6 -0
- package/test/node-red/settings.js +593 -0
- package/test/node-red/test/node-red/.config.nodes.json +430 -0
- package/test/node-red/test/node-red/.config.runtime.json +4 -0
- package/test/node-red/test/node-red/.config.runtime.json.backup +3 -0
- package/test/node-red/test/node-red/.config.users.json +20 -0
- package/test/node-red/test/node-red/.config.users.json.backup +17 -0
- package/test/node-red/test/node-red/package.json +6 -0
- package/test/node-red/test/node-red/settings.js +593 -0
- package/.eslintrc.js +0 -24
- package/.nycrc.json +0 -11
- package/src/lib/Accessory.js +0 -126
- package/src/lib/Characteristic.js +0 -30
- package/src/lib/HbAccessories.js +0 -167
- package/src/lib/Homebridge.js +0 -71
- package/src/lib/Homebridges.js +0 -68
- package/src/lib/Service.js +0 -307
- package/src/lib/register.js +0 -156
package/src/HAP-NodeRed.js
CHANGED
|
@@ -1,154 +1,24 @@
|
|
|
1
1
|
var debug = require('debug')('hapNodeRed');
|
|
2
|
-
var Queue = require('better-queue');
|
|
3
|
-
// var register = require('./lib/register.js');
|
|
4
|
-
var Homebridges = require('./lib/Homebridges.js').Homebridges;
|
|
5
|
-
var HAPNodeJSClient = require('hap-node-client').HAPNodeJSClient;
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var reqisterQueue = new Queue(function (node, cb) {
|
|
13
|
-
_register.call(node.that, node, cb);
|
|
14
|
-
}, {
|
|
15
|
-
concurrent: 1,
|
|
16
|
-
autoResume: false,
|
|
17
|
-
maxRetries: 1000,
|
|
18
|
-
retryDelay: 30000
|
|
19
|
-
});
|
|
20
|
-
reqisterQueue.pause();
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* hbConf - Configuration
|
|
24
|
-
*
|
|
25
|
-
* @param {type} n description
|
|
26
|
-
* @return {type} description
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
function hbConf(n) {
|
|
30
|
-
RED.nodes.createNode(this, n);
|
|
31
|
-
this.username = n.username;
|
|
32
|
-
this.macAddress = n.macAddress || '';
|
|
33
|
-
this.password = this.credentials.password;
|
|
34
|
-
|
|
35
|
-
this.users = {};
|
|
36
|
-
|
|
37
|
-
if (homebridge) {
|
|
38
|
-
if (this.macAddress) {
|
|
39
|
-
// register additional PIN on existing instance
|
|
40
|
-
homebridge.RegisterPin(this.macAddress, n.username);
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
homebridge = new HAPNodeJSClient({
|
|
44
|
-
"pin": n.username,
|
|
45
|
-
"refresh": 900,
|
|
46
|
-
"debug": false,
|
|
47
|
-
"timeout": 20,
|
|
48
|
-
"reqTimeout": 7000
|
|
49
|
-
});
|
|
50
|
-
reqisterQueue.pause();
|
|
51
|
-
homebridge.on('Ready', function (accessories) {
|
|
52
|
-
// evDevices = register.registerEv(homebridge, accessories);
|
|
53
|
-
// ctDevices = register.registerCt(homebridge, accessories);
|
|
54
|
-
hbDevices = new Homebridges(accessories);
|
|
55
|
-
// debug("output", JSON.stringify(hbDevices.toList({ perms: 'ev'}), null, 4));
|
|
56
|
-
// debug("evDevices", evDevices);
|
|
57
|
-
// debug('Discovered %s evDevices', evDevices.length);
|
|
58
|
-
debug('Discovered %s new evDevices', hbDevices.toList({
|
|
59
|
-
perms: 'ev'
|
|
60
|
-
}).length);
|
|
61
|
-
// debug(hbDevices.toList({perms: 'pw'}));
|
|
3
|
+
const HBConfigNode = require('./hbConfigNode.js');
|
|
4
|
+
const HbEventNode = require('./hbEventNode'); // Import the class
|
|
5
|
+
const HbResumeNode = require('./hbResumeNode'); // Import the class
|
|
6
|
+
const HbControlNode = require('./hbControlNode');
|
|
7
|
+
const HbStatusNode = require('./hbStatusNode');
|
|
62
8
|
|
|
63
|
-
|
|
64
|
-
perms: 'ev'
|
|
65
|
-
});
|
|
9
|
+
const HapDeviceRoutes = require('./HapDeviceRoutes');
|
|
66
10
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
for (var i = 0; i < list.length; i++) {
|
|
70
|
-
var endpoint = list[i];
|
|
71
|
-
// console.log("Checking", endpoint.fullName);
|
|
72
|
-
if (deleteSeen[endpoint.fullName]) {
|
|
73
|
-
console.log("WARNING: Duplicate device name", endpoint.fullName);
|
|
74
|
-
// debug('Duplicate', endpoint);
|
|
75
|
-
// response.event.payload.endpoints.splice(i, 1);
|
|
76
|
-
} else {
|
|
77
|
-
deleteSeen[endpoint.fullName] = true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
deleteSeen = [];
|
|
82
|
-
|
|
83
|
-
for (i = 0; i < list.length; i++) {
|
|
84
|
-
endpoint = list[i];
|
|
85
|
-
// console.log("Checking uniqueId", endpoint.uniqueId);
|
|
86
|
-
if (deleteSeen[endpoint.uniqueId]) {
|
|
87
|
-
console.log("ERROR: Parsing failed, duplicate uniqueID.", endpoint.fullName);
|
|
88
|
-
// response.event.payload.endpoints.splice(i, 1);
|
|
89
|
-
} else {
|
|
90
|
-
deleteSeen[endpoint.uniqueId] = true;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// evDevices.sort((a, b) => (a.sortName > b.sortName) ? 1 : ((b.sortName > a.sortName) ? -1 : 0));
|
|
94
|
-
// ctDevices.sort((a, b) => (a.sortName > b.sortName) ? 1 : ((b.sortName > a.sortName) ? -1 : 0));
|
|
11
|
+
module.exports = function (RED) {
|
|
12
|
+
var hbDevices;
|
|
95
13
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// debug('Discovered %s new ctDevices', hbDevices.toList({ perms: 'pw'}).length);
|
|
101
|
-
// debug("Register Queue", reqisterQueue.getStats());
|
|
102
|
-
reqisterQueue.resume();
|
|
103
|
-
});
|
|
14
|
+
class hbConfigNode extends HBConfigNode {
|
|
15
|
+
constructor(config) {
|
|
16
|
+
debug('hbConfigNode', JSON.stringify(config));
|
|
17
|
+
super(config, RED);
|
|
104
18
|
}
|
|
105
|
-
|
|
106
|
-
var node = this;
|
|
107
|
-
|
|
108
|
-
this.connect = function (callback) {
|
|
109
|
-
callback();
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
this.register = function (deviceNode, callback) {
|
|
113
|
-
debug("hbConf.register", deviceNode.fullName);
|
|
114
|
-
node.users[deviceNode.id] = deviceNode;
|
|
115
|
-
debug("Register %s -> %s", deviceNode.type, deviceNode.fullName);
|
|
116
|
-
reqisterQueue.push({
|
|
117
|
-
that: this,
|
|
118
|
-
device: deviceNode.device,
|
|
119
|
-
type: deviceNode.type,
|
|
120
|
-
name: deviceNode.name,
|
|
121
|
-
fullName: deviceNode.fullName,
|
|
122
|
-
node: node
|
|
123
|
-
}, callback);
|
|
124
|
-
// debug("Register Queue - push", reqisterQueue.getStats());
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
this.deregister = function (deviceNode, callback) {
|
|
128
|
-
deviceNode.status({
|
|
129
|
-
text: 'disconnected',
|
|
130
|
-
shape: 'ring',
|
|
131
|
-
fill: 'red'
|
|
132
|
-
});
|
|
133
|
-
// Should this also remove the homebridge registered event?
|
|
134
|
-
//
|
|
135
|
-
// debug("hbEvent deregistered:", deviceNode.name);
|
|
136
|
-
// if (homebridge.listenerCount(deviceNode.eventName)) {
|
|
137
|
-
deviceNode.eventName.forEach(function (event) {
|
|
138
|
-
homebridge.removeListener(event, deviceNode.listener);
|
|
139
|
-
});
|
|
140
|
-
// }
|
|
141
|
-
callback();
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
this.on('close', function () {
|
|
145
|
-
if (node.client && node.client.connected) {
|
|
146
|
-
node.client.end();
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
19
|
}
|
|
150
20
|
|
|
151
|
-
RED.nodes.registerType("hb-conf",
|
|
21
|
+
RED.nodes.registerType("hb-conf", hbConfigNode, {
|
|
152
22
|
credentials: {
|
|
153
23
|
password: {
|
|
154
24
|
type: "password"
|
|
@@ -156,959 +26,39 @@ module.exports = function (RED) {
|
|
|
156
26
|
}
|
|
157
27
|
});
|
|
158
28
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
* @param {type} n description
|
|
163
|
-
* @return {type} description
|
|
164
|
-
*/
|
|
165
|
-
|
|
166
|
-
function hbEvent(n) {
|
|
167
|
-
// debug("hbEvent", n);
|
|
168
|
-
RED.nodes.createNode(this, n);
|
|
169
|
-
this.conf = RED.nodes.getNode(n.conf);
|
|
170
|
-
this.confId = n.conf;
|
|
171
|
-
this.device = n.device;
|
|
172
|
-
this.service = n.Service;
|
|
173
|
-
this.name = n.name;
|
|
174
|
-
this.fullName = n.name + ' - ' + n.Service;
|
|
175
|
-
this.sendInitialState = n.sendInitialState === true;
|
|
176
|
-
this.state = {};
|
|
177
|
-
|
|
178
|
-
var node = this;
|
|
179
|
-
|
|
180
|
-
node.command = function (event) {
|
|
181
|
-
// False messages can be received from accessories with multiple services
|
|
182
|
-
// if (Object.keys(_convertHBcharactericToNode(event, node)).length > 0) {
|
|
183
|
-
// debug("hbEvent", node.name, event);
|
|
184
|
-
if (event.status === true && event.value !== undefined) {
|
|
185
|
-
node.state = Object.assign(node.state, _convertHBcharactericToNode([event], node));
|
|
186
|
-
var msg = {
|
|
187
|
-
name: node.name,
|
|
188
|
-
payload: node.state,
|
|
189
|
-
Homebridge: node.hbDevice.homebridge,
|
|
190
|
-
Manufacturer: node.hbDevice.manufacturer,
|
|
191
|
-
Service: node.hbDevice.deviceType,
|
|
192
|
-
_device: node.device,
|
|
193
|
-
_confId: node.confId,
|
|
194
|
-
_rawEvent: event
|
|
195
|
-
};
|
|
196
|
-
node.status({
|
|
197
|
-
text: JSON.stringify(msg.payload).slice(0, 30) + '...',
|
|
198
|
-
shape: 'dot',
|
|
199
|
-
fill: 'green'
|
|
200
|
-
});
|
|
201
|
-
clearTimeout(node.timeout);
|
|
202
|
-
node.timeout = setTimeout(function () {
|
|
203
|
-
node.status({});
|
|
204
|
-
}, 10 * 1000);
|
|
205
|
-
node.send(msg);
|
|
206
|
-
} else if (event.status === true) {
|
|
207
|
-
node.status({
|
|
208
|
-
text: 'connected',
|
|
209
|
-
shape: 'dot',
|
|
210
|
-
fill: 'green'
|
|
211
|
-
});
|
|
212
|
-
} else {
|
|
213
|
-
node.status({
|
|
214
|
-
text: 'disconnected: ' + event.status,
|
|
215
|
-
shape: 'ring',
|
|
216
|
-
fill: 'red'
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
// };
|
|
221
|
-
|
|
222
|
-
node.conf.register(node, function () {
|
|
223
|
-
debug("hbEvent.register", node.fullName);
|
|
224
|
-
this.hbDevice = hbDevices.findDevice(node.device, {
|
|
225
|
-
perms: 'pr'
|
|
226
|
-
});
|
|
227
|
-
if (this.hbDevice) {
|
|
228
|
-
node.hbDevice = this.hbDevice;
|
|
229
|
-
node.deviceType = this.hbDevice.deviceType;
|
|
230
|
-
|
|
231
|
-
_status(node.device, node, {
|
|
232
|
-
perms: 'ev'
|
|
233
|
-
}, function (err, message) {
|
|
234
|
-
if (!err) {
|
|
235
|
-
node.state = _convertHBcharactericToNode(message.characteristics, node);
|
|
236
|
-
debug("hbEvent received: %s = %s", node.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
|
|
237
|
-
if (node.sendInitialState) {
|
|
238
|
-
var msg = {
|
|
239
|
-
name: node.name,
|
|
240
|
-
payload: node.state,
|
|
241
|
-
Homebridge: node.hbDevice.homebridge,
|
|
242
|
-
Manufacturer: node.hbDevice.manufacturer,
|
|
243
|
-
Service: node.hbDevice.deviceType,
|
|
244
|
-
_device: node.device,
|
|
245
|
-
_confId: node.confId,
|
|
246
|
-
_rawMessage: message,
|
|
247
|
-
};
|
|
248
|
-
node.status({
|
|
249
|
-
text: JSON.stringify(msg.payload).slice(0, 30) + '...',
|
|
250
|
-
shape: 'dot',
|
|
251
|
-
fill: 'green'
|
|
252
|
-
});
|
|
253
|
-
clearTimeout(node.timeout);
|
|
254
|
-
node.timeout = setTimeout(function () {
|
|
255
|
-
node.status({});
|
|
256
|
-
}, 10 * 1000);
|
|
257
|
-
node.send(msg);
|
|
258
|
-
}
|
|
259
|
-
} else {
|
|
260
|
-
node.error("hbEvent _status: error", node.fullName, err);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
// Register for events
|
|
264
|
-
node.listener = node.command;
|
|
265
|
-
node.eventName = [];
|
|
266
|
-
// node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
|
|
267
|
-
// debug("DEVICE", this.hbDevice);
|
|
268
|
-
this.hbDevice.eventRegisters.forEach(function (event) {
|
|
269
|
-
homebridge.on(node.hbDevice.id + event.aid + event.iid, node.command);
|
|
270
|
-
node.eventName.push(node.hbDevice.id + event.aid + event.iid);
|
|
271
|
-
});
|
|
272
|
-
// homebridge.on(this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid, node.command);
|
|
273
|
-
node.status({
|
|
274
|
-
text: 'connected',
|
|
275
|
-
shape: 'dot',
|
|
276
|
-
fill: 'green'
|
|
277
|
-
});
|
|
278
|
-
} else {
|
|
279
|
-
node.error("197:Can't find device " + node.device, null);
|
|
280
|
-
}
|
|
281
|
-
}.bind(this));
|
|
282
|
-
|
|
283
|
-
node.on('close', function (callback) {
|
|
284
|
-
node.conf.deregister(node, callback);
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
RED.nodes.registerType("hb-event", hbEvent);
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* hbResume - description
|
|
292
|
-
*
|
|
293
|
-
* State operating model
|
|
294
|
-
* - Store msg into node.lastPayload
|
|
295
|
-
* - Store device state into node.state on events
|
|
296
|
-
*
|
|
297
|
-
* Turn on message just passes thru
|
|
298
|
-
* - if msg = on
|
|
299
|
-
*
|
|
300
|
-
* First turn off message restores state from Turn on
|
|
301
|
-
* - if msg = off and node.lastPayload === on
|
|
302
|
-
*
|
|
303
|
-
* Second turn off message just passes thru
|
|
304
|
-
* - if msg = off and node.lastPayload === off
|
|
305
|
-
* - Update stored device state to off
|
|
306
|
-
*
|
|
307
|
-
* @param {type} n description
|
|
308
|
-
* @return {type} description
|
|
309
|
-
*/
|
|
310
|
-
|
|
311
|
-
function hbResume(n) {
|
|
312
|
-
RED.nodes.createNode(this, n);
|
|
313
|
-
this.conf = RED.nodes.getNode(n.conf);
|
|
314
|
-
this.confId = n.conf;
|
|
315
|
-
this.device = n.device;
|
|
316
|
-
this.service = n.Service;
|
|
317
|
-
this.name = n.name;
|
|
318
|
-
this.fullName = n.name + ' - ' + n.Service;
|
|
319
|
-
var node = this;
|
|
320
|
-
|
|
321
|
-
node.state = null;
|
|
322
|
-
node.lastMessageTime = null;
|
|
323
|
-
node.lastMessageValue = null;
|
|
324
|
-
node.lastPayload = {
|
|
325
|
-
On: false
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
node.on('input', function (msg) {
|
|
329
|
-
this.msg = msg;
|
|
330
|
-
debug("hbResume.input: %s input", node.fullName, JSON.stringify(msg));
|
|
331
|
-
if (typeof msg.payload === "object") {
|
|
332
|
-
// Using this to validate input message contains valid Accessory Characteristics
|
|
333
|
-
if (node.hbDevice) { // not populated until initialization is complete
|
|
334
|
-
var message = _createControlMessage.call(this, msg.payload, node, node.hbDevice);
|
|
335
|
-
|
|
336
|
-
if (message.characteristics.length > 0) {
|
|
337
|
-
var newMsg;
|
|
338
|
-
if (!msg.payload.On) {
|
|
339
|
-
// false / Turn Off
|
|
340
|
-
// debug("hbResume-Node lastPayload %s", JSON.stringify(node.lastPayload));
|
|
341
|
-
if (node.lastPayload.On) {
|
|
342
|
-
// last msg was on, restore previous state
|
|
343
|
-
newMsg = {
|
|
344
|
-
name: node.name,
|
|
345
|
-
_device: node.device,
|
|
346
|
-
_confId: node.confId
|
|
347
|
-
};
|
|
348
|
-
if (node.hbDevice) {
|
|
349
|
-
newMsg.Homebridge = node.hbDevice.homebridge;
|
|
350
|
-
newMsg.Manufacturer = node.hbDevice.manufacturer;
|
|
351
|
-
newMsg.Type = node.hbDevice.deviceType;
|
|
352
|
-
}
|
|
353
|
-
newMsg.payload = node.state;
|
|
354
|
-
} else {
|
|
355
|
-
// last msg was off, pass thru
|
|
356
|
-
node.state = JSON.parse(JSON.stringify(msg.payload));
|
|
357
|
-
newMsg = msg;
|
|
358
|
-
}
|
|
359
|
-
} else {
|
|
360
|
-
// True / Turn on
|
|
361
|
-
newMsg = msg;
|
|
362
|
-
}
|
|
363
|
-
// Off messages should not include brightness
|
|
364
|
-
node.send((newMsg.payload.On ? newMsg : newMsg.payload = {
|
|
365
|
-
On: false
|
|
366
|
-
}, newMsg));
|
|
367
|
-
debug("hbResume.input: %s output", node.fullName, JSON.stringify(newMsg));
|
|
368
|
-
node.status({
|
|
369
|
-
text: JSON.stringify(newMsg.payload).slice(0, 30) + '...',
|
|
370
|
-
shape: 'dot',
|
|
371
|
-
fill: 'green'
|
|
372
|
-
});
|
|
373
|
-
clearTimeout(node.timeout);
|
|
374
|
-
node.timeout = setTimeout(function () {
|
|
375
|
-
node.status({});
|
|
376
|
-
}, 10 * 1000);
|
|
377
|
-
node.lastMessageValue = newMsg.payload;
|
|
378
|
-
node.lastMessageTime = Date.now();
|
|
379
|
-
// debug("hbResume.input: %s updating lastPayload %s", node.fullName, JSON.stringify(msg.payload));
|
|
380
|
-
node.lastPayload = JSON.parse(JSON.stringify(msg.payload)); // store value not reference
|
|
381
|
-
}
|
|
382
|
-
} else {
|
|
383
|
-
node.error("Homebridge not initialized - 1", this.msg);
|
|
384
|
-
node.status({
|
|
385
|
-
text: 'Homebridge not initialized -1',
|
|
386
|
-
shape: 'ring',
|
|
387
|
-
fill: 'red'
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
} else {
|
|
391
|
-
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);
|
|
392
|
-
node.status({
|
|
393
|
-
text: 'Invalid payload',
|
|
394
|
-
shape: 'ring',
|
|
395
|
-
fill: 'red'
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
node.command = function (event) {
|
|
401
|
-
// debug("hbResume received event: %s ->", node.fullName, event);
|
|
402
|
-
// debug("hbResume - internals %s millis, old %s, event %s, previous %s", Date.now() - node.lastMessageTime, node.lastMessageValue, event.status, node.state);
|
|
403
|
-
// Don't update for events originating from here
|
|
404
|
-
// if Elapsed is greater than 5 seconds, update stored state
|
|
405
|
-
// if Elapsed is less then 5, and lastMessage doesn't match event update stored state
|
|
406
|
-
|
|
407
|
-
var payload = Object.assign({}, node.state);
|
|
408
|
-
|
|
409
|
-
// debug("should be true", _getObjectDiff(payload, node.state).length);
|
|
410
|
-
|
|
411
|
-
payload = Object.assign(payload, _convertHBcharactericToNode([event], node));
|
|
412
|
-
|
|
413
|
-
// debug("should be false", _getObjectDiff(payload, node.state).length);
|
|
414
|
-
|
|
415
|
-
debug("hbResume.event: %s %s -> %s", node.fullName, JSON.stringify(node.state), JSON.stringify(payload));
|
|
416
|
-
|
|
417
|
-
if (event.status === true && event.value !== undefined) {
|
|
418
|
-
if ((Date.now() - node.lastMessageTime) > 5000) {
|
|
419
|
-
debug("hbResume.update: %s - updating stored event >5", node.fullName, payload);
|
|
420
|
-
node.state = JSON.parse(JSON.stringify(payload));
|
|
421
|
-
} else if (_getObjectDiff(payload, node.lastMessageValue).length > 0) {
|
|
422
|
-
// debug("hbResume - updating stored event !=", payload, node.lastMessageValue);
|
|
423
|
-
// node.state = payload;
|
|
424
|
-
}
|
|
425
|
-
} else if (event.status === true) {
|
|
426
|
-
node.status({
|
|
427
|
-
text: 'connected',
|
|
428
|
-
shape: 'dot',
|
|
429
|
-
fill: 'green'
|
|
430
|
-
});
|
|
431
|
-
} else {
|
|
432
|
-
node.status({
|
|
433
|
-
text: 'disconnected: ' + event.status,
|
|
434
|
-
shape: 'ring',
|
|
435
|
-
fill: 'red'
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
node.conf.register(node, function () {
|
|
441
|
-
debug("hbResume.register:", node.fullName);
|
|
442
|
-
this.hbDevice = hbDevices.findDevice(node.device, {
|
|
443
|
-
perms: 'pw'
|
|
444
|
-
});
|
|
445
|
-
if (this.hbDevice) {
|
|
446
|
-
_status(node.device, node, {
|
|
447
|
-
perms: 'pw'
|
|
448
|
-
}, function (err, message) {
|
|
449
|
-
if (!err) {
|
|
450
|
-
node.state = _convertHBcharactericToNode(message.characteristics, node);
|
|
451
|
-
debug("hbResume received: %s = %s", node.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
|
|
452
|
-
} else {
|
|
453
|
-
node.error(err);
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
node.hbDevice = this.hbDevice;
|
|
457
|
-
node.deviceType = this.hbDevice.deviceType;
|
|
458
|
-
// Register for events
|
|
459
|
-
node.listener = node.command;
|
|
460
|
-
node.eventName = [];
|
|
461
|
-
// node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
|
|
462
|
-
// homebridge.on(this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid, node.command);
|
|
463
|
-
this.hbDevice.eventRegisters.forEach(function (event) {
|
|
464
|
-
homebridge.on(node.hbDevice.id + event.aid + event.iid, node.command);
|
|
465
|
-
node.eventName.push(node.hbDevice.id + event.aid + event.iid);
|
|
466
|
-
});
|
|
467
|
-
node.status({
|
|
468
|
-
text: 'connected',
|
|
469
|
-
shape: 'dot',
|
|
470
|
-
fill: 'green'
|
|
471
|
-
});
|
|
472
|
-
clearTimeout(node.timeout);
|
|
473
|
-
node.timeout = setTimeout(function () {
|
|
474
|
-
node.status({});
|
|
475
|
-
}, 30 * 1000);
|
|
476
|
-
} else {
|
|
477
|
-
node.error("365:Can't find device " + node.device, null);
|
|
478
|
-
}
|
|
479
|
-
}.bind(this));
|
|
480
|
-
|
|
481
|
-
node.on('close', function (callback) {
|
|
482
|
-
node.conf.deregister(node, callback);
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
RED.nodes.registerType("hb-resume", hbResume);
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* hbControl - description
|
|
490
|
-
*
|
|
491
|
-
* @param {type} n description
|
|
492
|
-
* @return {type} description
|
|
493
|
-
*/
|
|
494
|
-
|
|
495
|
-
function hbControl(n) {
|
|
496
|
-
RED.nodes.createNode(this, n);
|
|
497
|
-
this.conf = RED.nodes.getNode(n.conf); // The configuration node
|
|
498
|
-
this.confId = n.conf;
|
|
499
|
-
this.device = n.device;
|
|
500
|
-
this.service = n.Service;
|
|
501
|
-
this.name = n.name;
|
|
502
|
-
this.fullName = n.name + ' - ' + n.Service;
|
|
503
|
-
|
|
504
|
-
var node = this;
|
|
505
|
-
|
|
506
|
-
node.on('input', function (msg) {
|
|
507
|
-
this.msg = msg;
|
|
508
|
-
_control.call(this, node, msg.payload, function (err, data) {
|
|
509
|
-
// debug('hbControl complete [%s] - [%s]', node, node.hbDevice); // Images produce alot of noise
|
|
510
|
-
if (!err && data && (node.deviceType == '00000110' || node.deviceType == '00000111')) {
|
|
511
|
-
const msg = {
|
|
512
|
-
name: node.name,
|
|
513
|
-
payload: node.state,
|
|
514
|
-
_device: node.device,
|
|
515
|
-
_confId: node.confId
|
|
516
|
-
};
|
|
517
|
-
if (node.hbDevice) {
|
|
518
|
-
msg.Homebridge = node.hbDevice.homebridge;
|
|
519
|
-
msg.Manufacturer = node.hbDevice.manufacturer;
|
|
520
|
-
msg.Service = node.hbDevice.deviceType;
|
|
521
|
-
}
|
|
522
|
-
msg.payload = data;
|
|
523
|
-
node.send(msg);
|
|
524
|
-
} else if (err) {
|
|
525
|
-
node.error(err, this.msg);
|
|
526
|
-
}
|
|
527
|
-
}.bind(this));
|
|
528
|
-
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
node.on('close', function (callback) {
|
|
532
|
-
callback();
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
node.conf.register(node, function () {
|
|
536
|
-
debug("hbControl.register:", node.fullName);
|
|
537
|
-
this.hbDevice = hbDevices.findDevice(node.device);
|
|
538
|
-
// console.log('hbControl Register', this.hbDevice)
|
|
539
|
-
if (this.hbDevice) {
|
|
540
|
-
node.hbDevice = this.hbDevice;
|
|
541
|
-
node.deviceType = this.hbDevice.type;
|
|
542
|
-
// Register for events
|
|
543
|
-
node.listener = node.command;
|
|
544
|
-
// node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
|
|
545
|
-
} else {
|
|
546
|
-
node.error("437:Can't find device " + node.device, null);
|
|
547
|
-
// this.error("Missing device " + node.device);
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
RED.nodes.registerType("hb-control", hbControl);
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* hbStatus - description
|
|
556
|
-
*
|
|
557
|
-
* @param {type} n description
|
|
558
|
-
* @return {type} description
|
|
559
|
-
*/
|
|
560
|
-
|
|
561
|
-
function hbStatus(n) {
|
|
562
|
-
RED.nodes.createNode(this, n);
|
|
563
|
-
this.conf = RED.nodes.getNode(n.conf); // The configuration node
|
|
564
|
-
this.confId = n.conf;
|
|
565
|
-
this.device = n.device;
|
|
566
|
-
this.service = n.Service;
|
|
567
|
-
this.name = n.name;
|
|
568
|
-
this.fullName = n.name + ' - ' + n.Service;
|
|
569
|
-
|
|
570
|
-
var node = this;
|
|
571
|
-
|
|
572
|
-
node.conf.register(node, function () {
|
|
573
|
-
debug("hbStatus Registered:", node.fullName);
|
|
574
|
-
this.hbDevice = hbDevices.findDevice(node.device);
|
|
575
|
-
if (this.hbDevice) {
|
|
576
|
-
node.hbDevice = this.hbDevice;
|
|
577
|
-
node.deviceType = this.hbDevice.deviceType;
|
|
578
|
-
// Register for events
|
|
579
|
-
node.listener = node.command;
|
|
580
|
-
// node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
|
|
581
|
-
} else {
|
|
582
|
-
node.error("437:Can't find device " + node.device, null);
|
|
583
|
-
// this.error("Missing device " + node.device);
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
node.on('input', function (msg) {
|
|
588
|
-
this.msg = msg;
|
|
589
|
-
_status(this.device, node, {
|
|
590
|
-
perms: 'pr'
|
|
591
|
-
}, function (err, message) {
|
|
592
|
-
if (!err) {
|
|
593
|
-
debug("hbStatus received: %s = %s", JSON.stringify(node.fullName), JSON.stringify(message).slice(0, 80) + '...', JSON.stringify(node.hbDevice));
|
|
594
|
-
this.msg.name = node.name;
|
|
595
|
-
this.msg._rawMessage = message;
|
|
596
|
-
this.msg.payload = _convertHBcharactericToNode(message.characteristics, node);
|
|
597
|
-
|
|
598
|
-
if (node.hbDevice) {
|
|
599
|
-
this.msg.Homebridge = node.hbDevice.homebridge;
|
|
600
|
-
this.msg.Manufacturer = node.hbDevice.manufacturer;
|
|
601
|
-
this.msg.Service = node.hbDevice.service;
|
|
602
|
-
this.msg._device = node.device;
|
|
603
|
-
this.msg._confId = node.confId;
|
|
604
|
-
}
|
|
605
|
-
node.status({
|
|
606
|
-
text: JSON.stringify(this.msg.payload).slice(0, 30) + '...',
|
|
607
|
-
shape: 'dot',
|
|
608
|
-
fill: 'green'
|
|
609
|
-
});
|
|
610
|
-
node.send(this.msg);
|
|
611
|
-
} else {
|
|
612
|
-
node.error(err, this.msg);
|
|
613
|
-
}
|
|
614
|
-
}.bind(this));
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
node.on('close', function (callback) {
|
|
618
|
-
callback();
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
RED.nodes.registerType("hb-status", hbStatus);
|
|
623
|
-
|
|
624
|
-
RED.httpAdmin.post('/hap-device/refresh/:id', RED.auth.needsPermission('hb-event.read'), function (req, res) {
|
|
625
|
-
var id = req.params.id;
|
|
626
|
-
var conf = RED.nodes.getNode(id);
|
|
627
|
-
if (conf) {
|
|
628
|
-
res.status(200).send();
|
|
629
|
-
} else {
|
|
630
|
-
// not deployed yet
|
|
631
|
-
console.log("Can't refresh until deployed");
|
|
632
|
-
res.status(404).send();
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
RED.httpAdmin.get('/hap-device/evDevices/', RED.auth.needsPermission('hb-event.read'), function (req, res) {
|
|
637
|
-
debug("evDevices", hbDevices.toList({
|
|
638
|
-
perms: 'ev'
|
|
639
|
-
}).length);
|
|
640
|
-
if (evDevices) {
|
|
641
|
-
res.send(hbDevices.toList({
|
|
642
|
-
perms: 'ev'
|
|
643
|
-
}));
|
|
644
|
-
} else {
|
|
645
|
-
res.status(404).send();
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
RED.httpAdmin.get('/hap-device/evDevices/:id', RED.auth.needsPermission('hb-event.read'), function (req, res) {
|
|
650
|
-
if (evDevices && hbDevices) {
|
|
651
|
-
debug("evDevices", hbDevices.toList({
|
|
652
|
-
perms: 'ev'
|
|
653
|
-
}).length);
|
|
654
|
-
res.send(hbDevices.toList({
|
|
655
|
-
perms: 'ev'
|
|
656
|
-
}));
|
|
657
|
-
} else {
|
|
658
|
-
res.status(404).send();
|
|
659
|
-
}
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
RED.httpAdmin.post('/hap-device/refresh/:id', RED.auth.needsPermission('hb-resume.read'), function (req, res) {
|
|
663
|
-
var id = req.params.id;
|
|
664
|
-
var conf = RED.nodes.getNode(id);
|
|
665
|
-
if (conf) {
|
|
666
|
-
res.status(200).send();
|
|
667
|
-
} else {
|
|
668
|
-
// not deployed yet
|
|
669
|
-
console.log("Can't refresh until deployed");
|
|
670
|
-
res.status(404).send();
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
RED.httpAdmin.get('/hap-device/evDevices/', RED.auth.needsPermission('hb-resume.read'), function (req, res) {
|
|
675
|
-
debug("evDevices", hbDevices.toList({
|
|
676
|
-
perms: 'ev'
|
|
677
|
-
}).length);
|
|
678
|
-
if (evDevices) {
|
|
679
|
-
res.send(hbDevices.toList({
|
|
680
|
-
perms: 'ev'
|
|
681
|
-
}));
|
|
682
|
-
} else {
|
|
683
|
-
res.status(404).send();
|
|
684
|
-
}
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
RED.httpAdmin.get('/hap-device/evDevices/:id', RED.auth.needsPermission('hb-resume.read'), function (req, res) {
|
|
688
|
-
debug("evDevices", hbDevices.toList({
|
|
689
|
-
perms: 'ev'
|
|
690
|
-
}).length);
|
|
691
|
-
if (evDevices) {
|
|
692
|
-
res.send(hbDevices.toList({
|
|
693
|
-
perms: 'ev'
|
|
694
|
-
}));
|
|
695
|
-
} else {
|
|
696
|
-
res.status(404).send();
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
RED.httpAdmin.get('/hap-device/ctDevices/', RED.auth.needsPermission('hb-control.read'), function (req, res) {
|
|
701
|
-
debug("ctDevices", hbDevices.toList({
|
|
702
|
-
perms: 'pw'
|
|
703
|
-
}).length);
|
|
704
|
-
if (ctDevices) {
|
|
705
|
-
res.send(hbDevices.toList({
|
|
706
|
-
perms: 'pw'
|
|
707
|
-
}));
|
|
708
|
-
} else {
|
|
709
|
-
res.status(404).send();
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
RED.httpAdmin.get('/hap-device/ctDevices/:id', RED.auth.needsPermission('hb-control.read'), function (req, res) {
|
|
714
|
-
debug("ctDevices", hbDevices.toList({
|
|
715
|
-
perms: 'pw'
|
|
716
|
-
}).length);
|
|
717
|
-
if (ctDevices) {
|
|
718
|
-
res.send(hbDevices.toList({
|
|
719
|
-
perms: 'pw'
|
|
720
|
-
}));
|
|
721
|
-
} else {
|
|
722
|
-
res.status(404).send();
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
/**
|
|
727
|
-
* _convertHBcharactericToNode - Convert homebridge characteric array to Node Payload
|
|
728
|
-
*
|
|
729
|
-
* @param {array} hbMessage description
|
|
730
|
-
* @param {object} node description
|
|
731
|
-
* @return {type} description
|
|
732
|
-
*/
|
|
733
|
-
|
|
734
|
-
function _convertHBcharactericToNode(hbMessage, node) {
|
|
735
|
-
// debug("_convertHBcharactericToNode", node.device);
|
|
736
|
-
var payload = {};
|
|
737
|
-
if (!hbMessage.payload) {
|
|
738
|
-
var device = hbDevices.findDevice(node.device);
|
|
739
|
-
// debug("Device", device);
|
|
740
|
-
|
|
741
|
-
// characteristics = Object.assign(characteristics, characteristic.characteristic);
|
|
742
|
-
if (device) {
|
|
743
|
-
hbMessage.forEach(function (characteristic) {
|
|
744
|
-
// debug("Exists", (device.characteristics[characteristic.aid + '.' + characteristic.iid]));
|
|
745
|
-
if (device.characteristics[characteristic.aid + '.' + characteristic.iid]) {
|
|
746
|
-
payload = Object.assign(payload, {
|
|
747
|
-
[device.characteristics[characteristic.aid + '.' + characteristic.iid].characteristic]: characteristic.value
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
});
|
|
751
|
-
}
|
|
752
|
-
} else {
|
|
753
|
-
payload = hbMessage.payload;
|
|
754
|
-
}
|
|
755
|
-
// debug("payload", payload);
|
|
756
|
-
return (payload);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
/**
|
|
760
|
-
* _createControlMessage - description
|
|
761
|
-
*
|
|
762
|
-
* @param {type} payload {"On":false,"Brightness":0}
|
|
763
|
-
* @param {type} node description
|
|
764
|
-
* @param {type} device description
|
|
765
|
-
* @return {type} description
|
|
766
|
-
*/
|
|
767
|
-
|
|
768
|
-
function _createControlMessage(payload, node, device) {
|
|
769
|
-
// debug("_createControlMessage", payload, device);
|
|
770
|
-
// debug("Device", device, device.characteristics[event.aid + '.' + event.iid]);
|
|
771
|
-
var response = [];
|
|
772
|
-
|
|
773
|
-
for (var key in payload) {
|
|
774
|
-
// debug("IID", key, _getKey(device.characteristics, key));
|
|
775
|
-
if (_getKey(device.characteristics, key)) {
|
|
776
|
-
response.push({
|
|
777
|
-
"aid": device.aid,
|
|
778
|
-
"iid": _getKey(device.characteristics, key).iid,
|
|
779
|
-
"value": payload[key]
|
|
780
|
-
});
|
|
781
|
-
} else {
|
|
782
|
-
this.warn("Characteristic '" + key + "' is not valid.\nTry one of these: " + device.descriptions);
|
|
783
|
-
node.status({
|
|
784
|
-
text: 'warn - Invalid Characteristic ' + key,
|
|
785
|
-
shape: 'ring',
|
|
786
|
-
fill: 'yellow'
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
return ({
|
|
791
|
-
"characteristics": response
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
/**
|
|
796
|
-
* _status - description
|
|
797
|
-
*
|
|
798
|
-
* @param {type} nrDevice description
|
|
799
|
-
* @param {type} node description
|
|
800
|
-
* @param {type} value description
|
|
801
|
-
* @param {type} callback description
|
|
802
|
-
* @return {type} description
|
|
803
|
-
*/
|
|
804
|
-
|
|
805
|
-
function _status(nrDevice, node, perms, callback) {
|
|
806
|
-
// debug("_status", new Error(), hbDevices);
|
|
807
|
-
var error;
|
|
808
|
-
try {
|
|
809
|
-
if (!hbDevices) {
|
|
810
|
-
throw new Error('hbDevices not initialized');
|
|
811
|
-
}
|
|
812
|
-
var device = hbDevices.findDevice(node.device, perms);
|
|
813
|
-
if (device) {
|
|
814
|
-
// debug("device.type", device.type);
|
|
815
|
-
switch (device.type) {
|
|
816
|
-
case "00000110": // Camera RTPStream Management
|
|
817
|
-
case "00000111": // Camera Control
|
|
818
|
-
var message = {
|
|
819
|
-
"resource-type": "image",
|
|
820
|
-
"image-width": 1920,
|
|
821
|
-
"image-height": 1080
|
|
822
|
-
};
|
|
823
|
-
debug("_status Control %s -> %s", device.id, JSON.stringify(message));
|
|
824
|
-
homebridge.HAPresourceByDeviceID(device.id, JSON.stringify(message), function (err, status) {
|
|
825
|
-
// debug("status", err);
|
|
826
|
-
if (!err) {
|
|
827
|
-
debug("_status Controlled %s:%s ->", device.host, device.port);
|
|
828
|
-
node.status({
|
|
829
|
-
text: 'sent',
|
|
830
|
-
shape: 'dot',
|
|
831
|
-
fill: 'green'
|
|
832
|
-
});
|
|
833
|
-
clearTimeout(node.timeout);
|
|
834
|
-
node.timeout = setTimeout(function () {
|
|
835
|
-
node.status({});
|
|
836
|
-
}, 30 * 1000);
|
|
837
|
-
// {"characteristics":[{"aid":19,"iid":10,"value":false},{"aid":19,"iid":11,"value":0}]}
|
|
838
|
-
callback(null, {
|
|
839
|
-
characteristics: {
|
|
840
|
-
payload: btoa(status)
|
|
841
|
-
}
|
|
842
|
-
});
|
|
843
|
-
} else {
|
|
844
|
-
node.error(device.host + ":" + device.port + " -> " + err);
|
|
845
|
-
node.status({
|
|
846
|
-
text: 'error',
|
|
847
|
-
shape: 'ring',
|
|
848
|
-
fill: 'red'
|
|
849
|
-
});
|
|
850
|
-
callback(err);
|
|
851
|
-
}
|
|
852
|
-
});
|
|
853
|
-
break;
|
|
854
|
-
default:
|
|
855
|
-
var message = '?id=' + device.getCharacteristics;
|
|
856
|
-
debug("_status request: %s -> %s:%s ->", node.fullName, device.id, message);
|
|
857
|
-
homebridge.HAPstatusByDeviceID(device.id, message, function (err, status) {
|
|
858
|
-
if (!err) {
|
|
859
|
-
// debug("Status %s:%s ->", device.host, device.port, status);
|
|
860
|
-
node.status({
|
|
861
|
-
text: 'sent',
|
|
862
|
-
shape: 'dot',
|
|
863
|
-
fill: 'green'
|
|
864
|
-
});
|
|
865
|
-
clearTimeout(node.timeout);
|
|
866
|
-
node.timeout = setTimeout(function () {
|
|
867
|
-
node.status({});
|
|
868
|
-
}, 30 * 1000);
|
|
869
|
-
callback(null, status);
|
|
870
|
-
} else {
|
|
871
|
-
error = device.id + " -> " + err + " -> " + status;
|
|
872
|
-
node.status({
|
|
873
|
-
text: 'error',
|
|
874
|
-
shape: 'ring',
|
|
875
|
-
fill: 'red'
|
|
876
|
-
});
|
|
877
|
-
callback(error);
|
|
878
|
-
}
|
|
879
|
-
});
|
|
880
|
-
} // End of switch
|
|
881
|
-
} else {
|
|
882
|
-
error = "Device not found: " + nrDevice;
|
|
883
|
-
node.status({
|
|
884
|
-
text: 'Device not found',
|
|
885
|
-
shape: 'ring',
|
|
886
|
-
fill: 'red'
|
|
887
|
-
});
|
|
888
|
-
callback(error);
|
|
889
|
-
} // end of device if
|
|
890
|
-
} catch (err) {
|
|
891
|
-
// debug('_status', err);
|
|
892
|
-
error = "Homebridge not initialized -2";
|
|
893
|
-
node.status({
|
|
894
|
-
text: error,
|
|
895
|
-
shape: 'ring',
|
|
896
|
-
fill: 'red'
|
|
897
|
-
});
|
|
898
|
-
callback(error);
|
|
29
|
+
class hbEventNode extends HbEventNode {
|
|
30
|
+
constructor(config) {
|
|
31
|
+
super(config, RED);
|
|
899
32
|
}
|
|
900
33
|
}
|
|
901
34
|
|
|
902
|
-
|
|
903
|
-
* _control - description
|
|
904
|
-
*
|
|
905
|
-
* @param {type} nrDevice description
|
|
906
|
-
* @param {type} node description
|
|
907
|
-
* @param {type} payload {"On":false, "Brightness":0}
|
|
908
|
-
* @param {type} callback description
|
|
909
|
-
* @return {type} description
|
|
910
|
-
*/
|
|
35
|
+
RED.nodes.registerType("hb-event", hbEventNode);
|
|
911
36
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
throw new Error('hbDevices not initialized');
|
|
916
|
-
}
|
|
917
|
-
var device = hbDevices.findDevice(node.device, {
|
|
918
|
-
perms: 'pw'
|
|
919
|
-
});
|
|
920
|
-
if (device) {
|
|
921
|
-
var message;
|
|
922
|
-
// console.log('device.type', device.type)
|
|
923
|
-
switch (device.type) {
|
|
924
|
-
case "00000110": // Camera RTPStream Management
|
|
925
|
-
case "00000111": // Camera Control
|
|
926
|
-
message = {
|
|
927
|
-
"resource-type": "image",
|
|
928
|
-
"image-width": 1920,
|
|
929
|
-
"image-height": 1080
|
|
930
|
-
};
|
|
931
|
-
debug("Control %s ->", device.id, node.fullName, JSON.stringify(message));
|
|
932
|
-
homebridge.HAPresourceByDeviceID(device.id, JSON.stringify(message), function (err, status) {
|
|
933
|
-
if (!err) {
|
|
934
|
-
// debug("Controlled %s ->", device.id, JSON.stringify(payload));
|
|
935
|
-
// debug("Payload %s ->", device.id, status);
|
|
936
|
-
node.status({
|
|
937
|
-
text: JSON.stringify(payload).slice(0, 30) + '...',
|
|
938
|
-
shape: 'dot',
|
|
939
|
-
fill: 'green'
|
|
940
|
-
});
|
|
941
|
-
clearTimeout(node.timeout);
|
|
942
|
-
node.timeout = setTimeout(function () {
|
|
943
|
-
node.status({});
|
|
944
|
-
}, 30 * 1000);
|
|
945
|
-
callback(null, status);
|
|
946
|
-
} else {
|
|
947
|
-
node.error(device.id + " -> " + err);
|
|
948
|
-
node.status({
|
|
949
|
-
text: 'error',
|
|
950
|
-
shape: 'ring',
|
|
951
|
-
fill: 'red'
|
|
952
|
-
});
|
|
953
|
-
callback(err);
|
|
954
|
-
}
|
|
955
|
-
});
|
|
956
|
-
break;
|
|
957
|
-
default:
|
|
958
|
-
// debug("Object type", typeof payload);
|
|
959
|
-
if (typeof payload === "object") {
|
|
960
|
-
message = _createControlMessage.call(this, payload, node, device);
|
|
961
|
-
debug("Control %s ->", device.id, JSON.stringify(message));
|
|
962
|
-
if (message.characteristics.length > 0) {
|
|
963
|
-
homebridge.HAPcontrolByDeviceID(device.id, JSON.stringify(message), function (err, status) {
|
|
964
|
-
if (!err && status && status.characteristics[0].status === 0) {
|
|
965
|
-
debug("Controlled %s ->", device.id, JSON.stringify(status));
|
|
966
|
-
node.status({
|
|
967
|
-
text: JSON.stringify(payload).slice(0, 30) + '...',
|
|
968
|
-
shape: 'dot',
|
|
969
|
-
fill: 'green'
|
|
970
|
-
});
|
|
971
|
-
clearTimeout(node.timeout);
|
|
972
|
-
node.timeout = setTimeout(function () {
|
|
973
|
-
node.status({});
|
|
974
|
-
}, 10 * 1000);
|
|
975
|
-
callback(null);
|
|
976
|
-
} else if (!err) {
|
|
977
|
-
debug("Controlled %s ->", device.id, payload);
|
|
978
|
-
node.status({
|
|
979
|
-
text: JSON.stringify(payload).slice(0, 30) + '...',
|
|
980
|
-
shape: 'dot',
|
|
981
|
-
fill: 'green'
|
|
982
|
-
});
|
|
983
|
-
clearTimeout(node.timeout);
|
|
984
|
-
node.timeout = setTimeout(function () {
|
|
985
|
-
node.status({});
|
|
986
|
-
}, 10 * 1000);
|
|
987
|
-
callback(null);
|
|
988
|
-
} else {
|
|
989
|
-
node.error(device.id + " -> " + err + " -> " + status);
|
|
990
|
-
node.status({
|
|
991
|
-
text: 'error',
|
|
992
|
-
shape: 'ring',
|
|
993
|
-
fill: 'red'
|
|
994
|
-
});
|
|
995
|
-
callback(err);
|
|
996
|
-
}
|
|
997
|
-
});
|
|
998
|
-
} else {
|
|
999
|
-
// Bad message
|
|
1000
|
-
/* - This is handled in createcontrolmessage
|
|
1001
|
-
this.warn("Invalid payload-");
|
|
1002
|
-
node.status({
|
|
1003
|
-
text: 'error - Invalid payload',
|
|
1004
|
-
shape: 'ring',
|
|
1005
|
-
fill: 'red'
|
|
1006
|
-
});
|
|
1007
|
-
*/
|
|
1008
|
-
var err = 'Invalid payload';
|
|
1009
|
-
callback(err);
|
|
1010
|
-
}
|
|
1011
|
-
} else {
|
|
1012
|
-
node.error("Payload should be an JSON object containing device characteristics and values, ie {\"On\":false, \"Brightness\":0 }\nValid values include: " + device.descriptions);
|
|
1013
|
-
var err = 'Invalid payload';
|
|
1014
|
-
node.status({
|
|
1015
|
-
text: err,
|
|
1016
|
-
shape: 'ring',
|
|
1017
|
-
fill: 'red'
|
|
1018
|
-
});
|
|
1019
|
-
callback(err);
|
|
1020
|
-
}
|
|
1021
|
-
} // End of switch
|
|
1022
|
-
} else {
|
|
1023
|
-
var error = 'Device not available';
|
|
1024
|
-
node.status({
|
|
1025
|
-
text: error,
|
|
1026
|
-
shape: 'ring',
|
|
1027
|
-
fill: 'red'
|
|
1028
|
-
});
|
|
1029
|
-
callback(error);
|
|
1030
|
-
}
|
|
1031
|
-
} catch (err) {
|
|
1032
|
-
var error = "Homebridge not initialized - 3 "+ err;
|
|
1033
|
-
node.status({
|
|
1034
|
-
text: error,
|
|
1035
|
-
shape: 'ring',
|
|
1036
|
-
fill: 'red'
|
|
1037
|
-
});
|
|
1038
|
-
callback(error);
|
|
37
|
+
class hbResumeNode extends HbResumeNode {
|
|
38
|
+
constructor(config) {
|
|
39
|
+
super(config, RED);
|
|
1039
40
|
}
|
|
1040
41
|
}
|
|
1041
42
|
|
|
1042
|
-
|
|
1043
|
-
* _register - description
|
|
1044
|
-
*
|
|
1045
|
-
* @param {type} node description
|
|
1046
|
-
* @param {type} callback callback
|
|
1047
|
-
* @return {type} description
|
|
1048
|
-
*/
|
|
43
|
+
RED.nodes.registerType("hb-resume", hbResumeNode);
|
|
1049
44
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
perms: 'ev'
|
|
1054
|
-
});
|
|
1055
|
-
if (node.type === 'hb-event' || node.type === 'hb-resume') {
|
|
1056
|
-
var message = {
|
|
1057
|
-
"characteristics": device.eventRegisters
|
|
1058
|
-
};
|
|
1059
|
-
debug("_register", node.fullName, device.id, message);
|
|
1060
|
-
homebridge.HAPeventByDeviceID(device.id, JSON.stringify(message), function (err, status) {
|
|
1061
|
-
if (!err && status === null) {
|
|
1062
|
-
debug("%s registered: %s -> %s", node.type, node.fullName, device.id);
|
|
1063
|
-
callback(null);
|
|
1064
|
-
} else if (!err) {
|
|
1065
|
-
debug("%s registered: %s -> %s", node.type, node.fullName, device.id, JSON.stringify(status));
|
|
1066
|
-
callback(null);
|
|
1067
|
-
} else {
|
|
1068
|
-
// Fix for # 47
|
|
1069
|
-
// node.error(device.host + ":" + device.port + " -> " + err);
|
|
1070
|
-
callback(err);
|
|
1071
|
-
}
|
|
1072
|
-
}.bind(this));
|
|
1073
|
-
} else {
|
|
1074
|
-
callback(null);
|
|
45
|
+
class hbControlNode extends HbControlNode {
|
|
46
|
+
constructor(config) {
|
|
47
|
+
super(config, RED);
|
|
1075
48
|
}
|
|
1076
49
|
}
|
|
1077
|
-
};
|
|
1078
50
|
|
|
1079
|
-
|
|
1080
|
-
const diff = Object.keys(obj1).reduce((result, key) => {
|
|
1081
|
-
if (!obj2.hasOwnProperty(key)) {
|
|
1082
|
-
result.push(key);
|
|
1083
|
-
} else if (obj1[key] === obj2[key]) {
|
|
1084
|
-
const resultKeyIndex = result.indexOf(key);
|
|
1085
|
-
result.splice(resultKeyIndex, 1);
|
|
1086
|
-
}
|
|
1087
|
-
return result;
|
|
1088
|
-
}, Object.keys(obj2));
|
|
51
|
+
RED.nodes.registerType("hb-control", hbControlNode);
|
|
1089
52
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
function _getKey(obj, value) {
|
|
1094
|
-
for (var key in obj) {
|
|
1095
|
-
// debug("%s === %s", obj[key].characteristic, value);
|
|
1096
|
-
// debug("%s === %s", obj[key].characteristic.toLowerCase(), value.toLowerCase());
|
|
1097
|
-
if (obj[key].characteristic.toLowerCase() === value.toLowerCase()) {
|
|
1098
|
-
return obj[key];
|
|
53
|
+
class hbStatusNode extends HbStatusNode {
|
|
54
|
+
constructor(config) {
|
|
55
|
+
super(config, RED);
|
|
1099
56
|
}
|
|
1100
57
|
}
|
|
1101
|
-
return null;
|
|
1102
|
-
}
|
|
1103
58
|
|
|
1104
|
-
|
|
1105
|
-
var buffer;
|
|
59
|
+
RED.nodes.registerType("hb-status", hbStatusNode);
|
|
1106
60
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
} else {
|
|
1110
|
-
buffer = Buffer.from(str.toString(), 'binary');
|
|
1111
|
-
}
|
|
61
|
+
const hapDeviceRoutes = new HapDeviceRoutes(RED, hbDevices);
|
|
62
|
+
hapDeviceRoutes.registerRoutes();
|
|
1112
63
|
|
|
1113
|
-
|
|
1114
|
-
}
|
|
64
|
+
};
|