node-red-contrib-homebridge-automation 0.1.12-beta.28 → 0.1.12-beta.29

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.28",
3
+ "version": "0.1.12-beta.29",
4
4
  "description": "NodeRED Automation for HomeBridge",
5
5
  "main": "src/HAP-NodeRed.js",
6
6
  "scripts": {
@@ -48,7 +48,7 @@
48
48
  "dependencies": {
49
49
  "better-queue": ">=3.8.12",
50
50
  "debug": "^4.3.5",
51
- "@homebridge/hap-client": "2.0.5-beta.2"
51
+ "@homebridge/hap-client": "2.0.5-beta.5"
52
52
  },
53
53
  "author": "NorthernMan54",
54
54
  "license": "ISC",
@@ -1,16 +1,13 @@
1
- // HapDeviceRoutes.js
2
1
  const debug = require('debug')('hapNodeRed:HapDeviceRoutes');
3
2
 
4
3
  class HapDeviceRoutes {
5
4
  constructor(RED) {
6
5
  this.RED = RED;
7
- this.hbDevices = false; // remove this line
8
6
  }
9
7
 
10
8
  // POST /hap-device/refresh/:id
11
9
  refreshDevice(req, res) {
12
- const id = req.params.id;
13
- const conf = this.RED.nodes.getNode(id);
10
+ const conf = this.RED.nodes.getNode(req.params.id);
14
11
  if (conf) {
15
12
  res.status(200).send();
16
13
  } else {
@@ -20,103 +17,42 @@ class HapDeviceRoutes {
20
17
  }
21
18
 
22
19
  // GET /hap-device/evDevices/
23
- getEvDevices(req, res) {
24
- debug("evDevices", this.hbDevices.toList({ perms: 'ev' }).length);
25
- if (this.hbDevices) {
26
- res.send(this.hbDevices.toList({ perms: 'ev' }));
20
+ getDevices(req, res, perms) {
21
+ const devices = this.RED.nodes.getNode(req.params.id)?.evDevices;
22
+ if (devices && devices.length) {
23
+ res.send(devices);
27
24
  } else {
28
- res.status(404).send();
25
+ res.status(404).send({ error: `No devices found for perms: ${perms}` });
29
26
  }
30
27
  }
31
28
 
32
29
  // GET /hap-device/evDevices/:id
33
- getEvDeviceById(req, res) {
34
- debug('req', req.params.id);
35
- var evDevices = this.RED.nodes.getNode(req.params.id).evDevices;
36
- // debug('hbDevices', evDevices);
37
- debug("evDevices", evDevices.length);
38
- if (evDevices) {
39
- res.send(evDevices);
40
- } else {
41
- res.status(404).send();
42
- }
43
- }
44
-
45
- // POST /hap-device/refresh/:id for hb-resume
46
- refreshResumeDevice(req, res) {
47
- const id = req.params.id;
48
- const conf = this.RED.nodes.getNode(id);
49
- if (conf) {
50
- res.status(200).send();
51
- } else {
52
- debug("Can't refresh until deployed");
53
- res.status(404).send();
54
- }
55
- }
56
-
57
- // GET /hap-device/evDevices/ for hb-resume
58
- getEvDevicesForResume(req, res) {
59
- debug("evDevices", this.hbDevices.toList({ perms: 'ev' }).length);
60
- if (this.hbDevices) {
61
- res.send(this.hbDevices.toList({ perms: 'ev' }));
30
+ getDeviceById(req, res, key) {
31
+ const devices = this.RED.nodes.getNode(req.params.id)?.[key];
32
+ if (devices) {
33
+ debug(`${key} devices`, devices.length);
34
+ res.send(devices);
62
35
  } else {
63
36
  res.status(404).send();
64
37
  }
65
38
  }
66
39
 
67
- // GET /hap-device/evDevices/:id for hb-resume
68
- getEvDeviceByIdForResume(req, res) {
69
- debug("evDevices", this.hbDevices.toList({ perms: 'ev' }).length);
70
- if (this.hbDevices) {
71
- res.send(this.hbDevices.toList({ perms: 'ev' }));
72
- } else {
73
- res.status(404).send();
74
- }
75
- }
76
-
77
- // GET /hap-device/ctDevices/
78
- getCtDevices(req, res) {
79
- // debug("ctDevices", this.hbDevices.toList({ perms: 'pw' }).length);
80
- if (this.hbDevices) {
81
- res.send(this.hbDevices.toList({ perms: 'pw' }));
82
- } else {
83
- res.status(404).send();
84
- }
85
- }
86
-
87
- // GET /hap-device/evDevices/:id
88
- getCtDeviceById(req, res) {
89
- debug('getCtDeviceById', req.params.id);
90
- const ctDevices = this.RED.nodes.getNode(req.params.id).ctDevices;
91
- // debug('ctDevices', ctDevices);
92
- debug("ctDevices", ctDevices.length);
93
- if (ctDevices) {
94
- res.send(ctDevices);
95
- } else {
96
- res.status(404).send();
97
- }
98
- }
99
-
100
- // GET /hap-device/ctDevices/:id
101
- // getCtDeviceById(req, res) {
102
- // debug("ctDevices", this.hbDevices.toList({ perms: 'pw' }).length);
103
- // if (this.hbDevices) {
104
- // res.send(this.hbDevices.toList({ perms: 'pw' }));
105
- // } else {
106
- // res.status(404).send();
107
- // }
108
- // }
109
-
110
40
  // Register all routes
111
41
  registerRoutes() {
112
- this.RED.httpAdmin.post('/hap-device/refresh/:id', this.RED.auth.needsPermission('hb-event.read'), this.refreshDevice.bind(this));
113
- this.RED.httpAdmin.get('/hap-device/evDevices/', this.RED.auth.needsPermission('hb-event.read'), this.getEvDevices.bind(this));
114
- this.RED.httpAdmin.get('/hap-device/evDevices/:id', this.RED.auth.needsPermission('hb-event.read'), this.getEvDeviceById.bind(this));
115
- this.RED.httpAdmin.post('/hap-device/refresh/:id', this.RED.auth.needsPermission('hb-resume.read'), this.refreshResumeDevice.bind(this));
116
- this.RED.httpAdmin.get('/hap-device/evDevices/', this.RED.auth.needsPermission('hb-resume.read'), this.getEvDevicesForResume.bind(this));
117
- this.RED.httpAdmin.get('/hap-device/evDevices/:id', this.RED.auth.needsPermission('hb-resume.read'), this.getEvDeviceByIdForResume.bind(this));
118
- this.RED.httpAdmin.get('/hap-device/ctDevices/', this.RED.auth.needsPermission('hb-control.read'), this.getCtDevices.bind(this));
119
- this.RED.httpAdmin.get('/hap-device/ctDevices/:id', this.RED.auth.needsPermission('hb-control.read'), this.getCtDeviceById.bind(this));
42
+ const routes = [
43
+ { method: 'post', path: '/hap-device/refresh/:id', permission: 'hb-event.read', handler: this.refreshDevice },
44
+ { method: 'get', path: '/hap-device/evDevices/', permission: 'hb-event.read', handler: (req, res) => this.getDevices(req, res, 'ev') },
45
+ { method: 'get', path: '/hap-device/evDevices/:id', permission: 'hb-event.read', handler: (req, res) => this.getDeviceById(req, res, 'evDevices') },
46
+ { method: 'post', path: '/hap-device/refresh/:id', permission: 'hb-resume.read', handler: this.refreshDevice },
47
+ { method: 'get', path: '/hap-device/evDevices/', permission: 'hb-resume.read', handler: (req, res) => this.getDevices(req, res, 'ev') },
48
+ { method: 'get', path: '/hap-device/evDevices/:id', permission: 'hb-resume.read', handler: (req, res) => this.getDeviceById(req, res, 'evDevices') },
49
+ { method: 'get', path: '/hap-device/ctDevices/', permission: 'hb-control.read', handler: (req, res) => this.getDevices(req, res, 'pw') },
50
+ { method: 'get', path: '/hap-device/ctDevices/:id', permission: 'hb-control.read', handler: (req, res) => this.getDeviceById(req, res, 'ctDevices') },
51
+ ];
52
+
53
+ routes.forEach(({ method, path, permission, handler }) => {
54
+ this.RED.httpAdmin[method](path, this.RED.auth.needsPermission(permission), handler.bind(this));
55
+ });
120
56
  }
121
57
  }
122
58
 
package/src/hbBaseNode.js CHANGED
@@ -58,6 +58,9 @@ class HbBaseNode {
58
58
  done();
59
59
  }
60
60
 
61
+ statusText(message) {
62
+ return message.slice(0, 20)
63
+ }
61
64
  /**
62
65
  *
63
66
  * @param {*} warning - Message to log and display in debug panel
@@ -66,7 +69,7 @@ class HbBaseNode {
66
69
  handleError(warning, statusText) {
67
70
  this.warn(warning);
68
71
  this.status({
69
- text: (statusText ? statusText : warning),
72
+ text: (statusText ? statusText : warning).slice(0, 20),
70
73
  shape: 'ring',
71
74
  fill: 'red',
72
75
  });
@@ -58,6 +58,7 @@ class HBConfigNode {
58
58
  this.hbDevices = await this.hapClient.getAllServices();
59
59
  this.evDevices = this.toList({ perms: 'ev' });
60
60
  this.ctDevices = this.toList({ perms: 'pw' });
61
+ console.log('ctDevices', this.hbDevices.filter(service => service.type == 'CameraRTPStreamManagement' && service.serviceName == 'Driveway 8E52'));
61
62
  this.handleDuplicates(this.evDevices);
62
63
  debug('Queue', this.reqisterQueue.getStats());
63
64
  this.reqisterQueue.resume();
@@ -65,7 +66,7 @@ class HBConfigNode {
65
66
 
66
67
  toList(perms) {
67
68
  const supportedTypes = [
68
- 'Battery', 'Carbon Dioxide Sensor', 'Carbon Monoxide Sensor', 'Doorbell',
69
+ 'Battery', 'Carbon Dioxide Sensor', 'Carbon Monoxide Sensor', 'Camera Rtp Stream Management', 'Doorbell',
69
70
  'Fan', 'Fanv2', 'Garage Door Opener', 'Humidity Sensor', 'Input Source',
70
71
  'Leak Sensor', 'Lightbulb', 'Lock Mechanism', 'Motion Sensor', 'Occupancy Sensor',
71
72
  'Outlet', 'Smoke Sensor', 'Speaker', 'Stateless Programmable Switch', 'Switch',
@@ -12,7 +12,13 @@ class HbControlNode extends hbBaseNode {
12
12
  this.handleError('HB not initialized');
13
13
  return;
14
14
  }
15
-
15
+ if (this.hbDevice.type == 'CameraRTPStreamManagement') {
16
+ message.payload = {
17
+ "resource-type": "image",
18
+ "image-width": 1920,
19
+ "image-height": 1080
20
+ };
21
+ }
16
22
  if (typeof message.payload !== 'object') {
17
23
  const validNames = Object.keys(this.hbDevice.values)
18
24
  .filter(key => key !== 'ConfiguredName')
@@ -25,24 +31,48 @@ class HbControlNode extends hbBaseNode {
25
31
  const results = [];
26
32
  let fill = 'green';
27
33
 
28
- for (const key of Object.keys(message.payload)) {
34
+ if (this.hbDevice.type == 'CameraRTPStreamManagement') {
35
+ const result = await this.hbDevice.getResource({
36
+ "resource-type": "image",
37
+ "image-width": 1920,
38
+ "image-height": 1080
39
+ });
40
+ message = { ...message, ...this.createMessage(this.hbDevice) };
41
+ message.payload = result;
42
+ send(message);
43
+ results.push({ 'Received': result.length })
44
+ } else {
45
+ for (const key of Object.keys(message.payload)) {
29
46
 
30
- try {
31
- const result = await this.hbDevice.setCharacteristicByType(key, message.payload[key]);
32
- results.push({ [result.type]: result.value });
33
- } catch (error) {
34
- this.error(`Failed to set value for ${key}: ${error.message}`);
35
- results.push({ key: key + ' ' + error.message })
36
- fill = 'red';
47
+ try {
48
+ const result = await this.hbDevice.setCharacteristicByType(key, message.payload[key]);
49
+ results.push({ [result.type]: result.value });
50
+ } catch (error) {
51
+ this.error(`Failed to set value for ${key}: ${error.message}`);
52
+ results.push({ key: key + ' ' + error.message })
53
+ fill = 'red';
54
+ }
37
55
  }
38
56
  }
39
57
 
40
58
  this.status({
41
- text: JSON.stringify(Object.assign({}, ...results)),
59
+ text: this.statusText(JSON.stringify(Object.assign({}, ...results))),
42
60
  shape: 'dot',
43
61
  fill,
44
62
  });
45
63
  }
46
64
  }
47
65
 
66
+ function btoa(str) {
67
+ var buffer;
68
+
69
+ if (str instanceof Buffer) {
70
+ buffer = str;
71
+ } else {
72
+ buffer = Buffer.from(str.toString(), 'binary');
73
+ }
74
+
75
+ return buffer.toString('base64');
76
+ }
77
+
48
78
  module.exports = HbControlNode;
@@ -11,7 +11,7 @@ class HbEventNode extends hbBaseNode {
11
11
  debug('handleHbReady', this.id, this.name, service.values)
12
12
  if (this.sendInitialState) {
13
13
  this.status({
14
- text: JSON.stringify(service.values),
14
+ text: this.statusText(JSON.stringify(service.values)),
15
15
  shape: 'dot',
16
16
  fill: 'green',
17
17
  });
@@ -40,7 +40,7 @@ class HbResumeNode extends HbBaseNode {
40
40
  }
41
41
 
42
42
  this.status({
43
- text: JSON.stringify(message.payload),
43
+ text: this.statusText(JSON.stringify(message.payload)),
44
44
  shape: 'dot',
45
45
  fill: 'green',
46
46
  });
@@ -16,7 +16,7 @@ class HbStatusNode extends HbBaseNode {
16
16
 
17
17
  const result = await this.hbDevice.refreshCharacteristics();
18
18
  this.status({
19
- text: JSON.stringify(await this.hbDevice.values),
19
+ text: this.statusText(JSON.stringify(await this.hbDevice.values)),
20
20
  shape: 'dot',
21
21
  fill: 'green'
22
22
  });
@@ -431,5 +431,177 @@
431
431
  "x": 800,
432
432
  "y": 300,
433
433
  "wires": []
434
+ },
435
+ {
436
+ "id": "12ce98441601c981",
437
+ "type": "hb-event",
438
+ "z": "caef1e7b5b399e80",
439
+ "name": "Driveway 8E52",
440
+ "Homebridge": "ECI-T24F2",
441
+ "Manufacturer": "HikVision",
442
+ "Service": "CameraRTPStreamManagement",
443
+ "device": "ECI-T24F2CB:6F:94:DD:43:77HikVisionDriveway 8E5200000110",
444
+ "conf": "557aec8e8c47e61e",
445
+ "sendInitialState": true,
446
+ "x": 340,
447
+ "y": 520,
448
+ "wires": [
449
+ [
450
+ "e2919464e7c4ab5c",
451
+ "08bc0ab3e3f2439f"
452
+ ]
453
+ ]
454
+ },
455
+ {
456
+ "id": "6216377792cba653",
457
+ "type": "hb-control",
458
+ "z": "caef1e7b5b399e80",
459
+ "name": "Driveway 8E52",
460
+ "Homebridge": "ECI-T24F2",
461
+ "Manufacturer": "HikVision",
462
+ "Service": "CameraRTPStreamManagement",
463
+ "device": "ECI-T24F2CB:6F:94:DD:43:77HikVisionDriveway 8E5200000110",
464
+ "conf": "557aec8e8c47e61e",
465
+ "outputs": 1,
466
+ "x": 940,
467
+ "y": 500,
468
+ "wires": [
469
+ [
470
+ "9dc75e95be7ba465",
471
+ "12d2a40f6441c49c"
472
+ ]
473
+ ]
474
+ },
475
+ {
476
+ "id": "08bc0ab3e3f2439f",
477
+ "type": "debug",
478
+ "z": "caef1e7b5b399e80",
479
+ "name": "debug 5",
480
+ "active": true,
481
+ "tosidebar": true,
482
+ "console": false,
483
+ "tostatus": true,
484
+ "complete": "true",
485
+ "targetType": "full",
486
+ "statusVal": "payload",
487
+ "statusType": "auto",
488
+ "x": 600,
489
+ "y": 580,
490
+ "wires": []
491
+ },
492
+ {
493
+ "id": "e2919464e7c4ab5c",
494
+ "type": "switch",
495
+ "z": "caef1e7b5b399e80",
496
+ "name": "",
497
+ "property": "payload.MotionDetected",
498
+ "propertyType": "msg",
499
+ "rules": [
500
+ {
501
+ "t": "eq",
502
+ "v": "1",
503
+ "vt": "num"
504
+ }
505
+ ],
506
+ "checkall": "true",
507
+ "repair": false,
508
+ "outputs": 1,
509
+ "x": 590,
510
+ "y": 500,
511
+ "wires": [
512
+ [
513
+ "4982be53458df3a8"
514
+ ]
515
+ ]
516
+ },
517
+ {
518
+ "id": "4982be53458df3a8",
519
+ "type": "change",
520
+ "z": "caef1e7b5b399e80",
521
+ "name": "",
522
+ "rules": [
523
+ {
524
+ "t": "set",
525
+ "p": "payload",
526
+ "pt": "msg",
527
+ "to": "",
528
+ "tot": "date"
529
+ }
530
+ ],
531
+ "action": "",
532
+ "property": "",
533
+ "from": "",
534
+ "to": "",
535
+ "reg": false,
536
+ "x": 740,
537
+ "y": 500,
538
+ "wires": [
539
+ [
540
+ "6216377792cba653"
541
+ ]
542
+ ]
543
+ },
544
+ {
545
+ "id": "9dc75e95be7ba465",
546
+ "type": "debug",
547
+ "z": "caef1e7b5b399e80",
548
+ "name": "debug 6",
549
+ "active": true,
550
+ "tosidebar": true,
551
+ "console": false,
552
+ "tostatus": true,
553
+ "complete": "true",
554
+ "targetType": "full",
555
+ "statusVal": "payload",
556
+ "statusType": "msg",
557
+ "x": 1120,
558
+ "y": 500,
559
+ "wires": []
560
+ },
561
+ {
562
+ "id": "25a7fbf22f944415",
563
+ "type": "inject",
564
+ "z": "caef1e7b5b399e80",
565
+ "name": "",
566
+ "props": [
567
+ {
568
+ "p": "payload"
569
+ },
570
+ {
571
+ "p": "topic",
572
+ "vt": "str"
573
+ }
574
+ ],
575
+ "repeat": "",
576
+ "crontab": "",
577
+ "once": false,
578
+ "onceDelay": 0.1,
579
+ "topic": "",
580
+ "payload": "{ \"MotionDetected\": 1 }",
581
+ "payloadType": "json",
582
+ "x": 340,
583
+ "y": 460,
584
+ "wires": [
585
+ [
586
+ "e2919464e7c4ab5c"
587
+ ]
588
+ ]
589
+ },
590
+ {
591
+ "id": "12d2a40f6441c49c",
592
+ "type": "file",
593
+ "z": "caef1e7b5b399e80",
594
+ "name": "",
595
+ "filename": "/Users/sgracey/Desktop/image.jpeg",
596
+ "filenameType": "str",
597
+ "appendNewline": false,
598
+ "createDir": false,
599
+ "overwriteFile": "true",
600
+ "encoding": "none",
601
+ "x": 1210,
602
+ "y": 620,
603
+ "wires": [
604
+ []
605
+ ]
434
606
  }
435
607
  ]