node-red-contrib-homebridge-automation 0.1.12-beta.31 → 0.1.12-beta.32

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.31",
3
+ "version": "0.1.12-beta.32",
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.5"
51
+ "@homebridge/hap-client": "2.0.5-beta.7"
52
52
  },
53
53
  "author": "NorthernMan54",
54
54
  "license": "ISC",
@@ -30,7 +30,7 @@ class HapDeviceRoutes {
30
30
  getDeviceById(req, res, key) {
31
31
  const devices = this.RED.nodes.getNode(req.params.id)?.[key];
32
32
  if (devices) {
33
- debug(`${key} devices`, devices.length);
33
+ // debug(`${key} devices`, devices.length);
34
34
  res.send(devices);
35
35
  } else {
36
36
  res.status(404).send();
@@ -8,6 +8,7 @@ class HBConfigNode {
8
8
  if (!config.jest) {
9
9
  RED.nodes.createNode(this, config);
10
10
 
11
+ // Initialize properties
11
12
  this.username = config.username;
12
13
  this.macAddress = config.macAddress || '';
13
14
  this.users = {};
@@ -19,6 +20,7 @@ class HBConfigNode {
19
20
  this.monitorNodes = [];
20
21
  this.log = new Log(console, true);
21
22
 
23
+ // Initialize queue
22
24
  this.reqisterQueue = new Queue(this._register.bind(this), {
23
25
  concurrent: 1,
24
26
  autoResume: false,
@@ -29,27 +31,25 @@ class HBConfigNode {
29
31
  });
30
32
  this.reqisterQueue.pause();
31
33
 
34
+ // Initialize HAP client
32
35
  this.hapClient = new HapClient({
33
36
  config: { debug: false },
34
37
  pin: config.username,
35
38
  logger: this.log,
36
39
  });
37
40
 
38
- this.waitForNoMoreDiscoveries();
39
41
  this.hapClient.on('instance-discovered', this.waitForNoMoreDiscoveries);
40
-
42
+ this.waitForNoMoreDiscoveries();
41
43
  this.on('close', this.close.bind(this));
42
44
  }
43
45
  }
44
46
 
45
47
  waitForNoMoreDiscoveries = () => {
46
- if (this.discoveryTimeout) {
47
- clearTimeout(this.discoveryTimeout);
48
- }
49
-
48
+ clearTimeout(this.discoveryTimeout);
50
49
  this.discoveryTimeout = setTimeout(() => {
51
50
  this.log.debug('No more instances discovered, publishing services');
52
51
  this.hapClient.removeListener('instance-discovered', this.waitForNoMoreDiscoveries);
52
+ this.hapClient.on('instance-discovered', async (instance) => { debug('instance-discovered', instance); await this.monitorDevices(); });
53
53
  this.handleReady();
54
54
  }, 5000);
55
55
  };
@@ -58,10 +58,9 @@ 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('evDevices', this.evDevices);
62
- console.log('ctDevices', this.ctDevices);
61
+ this.log.info(`Devices initialized: evDevices: ${this.evDevices.length}, ctDevices: ${this.ctDevices.length}`);
63
62
  this.handleDuplicates(this.evDevices);
64
- debug('Queue', this.reqisterQueue.getStats());
63
+ debug('Queue stats:', this.reqisterQueue.getStats());
65
64
  this.reqisterQueue.resume();
66
65
  }
67
66
 
@@ -92,23 +91,19 @@ class HBConfigNode {
92
91
  const seenFullNames = new Set();
93
92
  const seenUniqueIds = new Set();
94
93
 
95
- for (const endpoint of list) {
96
- if (seenFullNames.has(endpoint.fullName)) {
94
+ list.forEach(endpoint => {
95
+ if (!seenFullNames.add(endpoint.fullName)) {
97
96
  console.warn('WARNING: Duplicate device name', endpoint.fullName);
98
- } else {
99
- seenFullNames.add(endpoint.fullName);
100
97
  }
101
98
 
102
- if (seenUniqueIds.has(endpoint.uniqueId)) {
103
- console.error('ERROR: Parsing failed, duplicate uniqueID.', endpoint.fullName);
104
- } else {
105
- seenUniqueIds.add(endpoint.uniqueId);
99
+ if (!seenUniqueIds.add(endpoint.uniqueId)) {
100
+ console.error('ERROR: Duplicate uniqueId detected.', endpoint.fullName);
106
101
  }
107
- }
102
+ });
108
103
  }
109
104
 
110
105
  register(clientNode) {
111
- debug('Register %s -> %s', clientNode.type, clientNode.name);
106
+ debug('Register: %s type: %s', clientNode.type, clientNode.name);
112
107
  this.clientNodes[clientNode.id] = clientNode;
113
108
  this.reqisterQueue.push(clientNode);
114
109
  clientNode.status({ fill: 'yellow', shape: 'ring', text: 'connecting' });
@@ -116,64 +111,52 @@ class HBConfigNode {
116
111
 
117
112
  async _register(clientNodes, cb) {
118
113
  for (const clientNode of clientNodes) {
119
- debug('_Register %s -> %s', clientNode.type, clientNode.name);
120
- clientNode.hbDevice = this.hbDevices.find(service => {
121
- const deviceUnique = `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`;
122
- if (clientNode.device === deviceUnique) {
123
- clientNode.status({ fill: 'green', shape: 'dot', text: 'connected' });
124
- clientNode.emit('hbReady', service);
114
+ debug('_Register: %s type: %s', clientNode.type, clientNode.name);
115
+ const matchedDevice = this.hbDevices.find(service =>
116
+ clientNode.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
117
+ );
118
+
119
+ if (matchedDevice) {
120
+ clientNode.hbDevice = matchedDevice;
121
+ clientNode.status({ fill: 'green', shape: 'dot', text: 'connected' });
122
+ clientNode.emit('hbReady', matchedDevice);
123
+ if (clientNode.config.type === 'hb-status') {
124
+ this.monitorNodes[clientNode.device] = matchedDevice;
125
125
  }
126
- return clientNode.device === deviceUnique;
127
- });
128
-
129
- if (clientNode.config.type === 'hb-status') {
130
- this.monitorNodes[clientNode.device] = clientNode.hbDevice;
131
- }
132
-
133
- if (!clientNode.hbDevice) {
134
- console.error('ERROR: _register - HB Device Missing', clientNode.name);
126
+ } else {
127
+ console.error('ERROR: Device registration failed', clientNode.name);
135
128
  }
136
129
  }
137
130
 
131
+ await this.monitorDevices();
132
+
133
+ cb(null);
134
+ }
135
+
136
+ async monitorDevices() {
138
137
  if (Object.keys(this.monitorNodes).length) {
139
138
  this.monitor = await this.hapClient.monitorCharacteristics(Object.values(this.monitorNodes));
140
139
  this.monitor.on('service-update', (services) => {
141
- for (const service of services) {
142
- const eventNodes = Object.values(this.clientNodes).filter(
143
- clientNode =>
144
- clientNode.config.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
140
+ services.forEach(service => {
141
+ const eventNodes = Object.values(this.clientNodes).filter(clientNode =>
142
+ clientNode.config.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
145
143
  );
146
-
147
- eventNodes.forEach((eventNode) => {
148
- if (eventNode._events && typeof eventNode.emit === 'function') {
149
- eventNode.emit('hbEvent', service);
150
- }
151
- });
152
- }
144
+ eventNodes.forEach(eventNode => eventNode.emit('hbEvent', service));
145
+ });
153
146
  });
154
147
  }
155
- cb(null);
156
148
  }
157
- /*
158
- deregister(clientNode) {
159
- clientNode.status({ text: 'disconnected', shape: 'ring', fill: 'red' });
160
- delete this.clientNodes[clientNode.id];
161
- }
162
- */
163
149
  close() {
164
- if (this.hapClient) {
165
- this.hapClient.destroy();
166
- }
150
+ this.hapClient?.destroy();
167
151
  }
168
-
169
152
  }
170
153
 
171
- // Cameras have multiple AID's of 1....
154
+
155
+ // Filter unique devices by AID, service name, username, and port
172
156
  const filterUnique = (data) => {
173
157
  const seen = new Set();
174
158
  return data.filter(item => {
175
159
  const uniqueKey = `${item.aid}-${item.serviceName}-${item.instance.username}-${item.instance.port}`;
176
- console.log(uniqueKey, seen.has(uniqueKey))
177
160
  if (seen.has(uniqueKey)) return false;
178
161
  seen.add(uniqueKey);
179
162
  return true;
@@ -54,6 +54,7 @@ class HbControlNode extends hbBaseNode {
54
54
  const result = await this.hbDevice.setCharacteristicByType(key, message.payload[key]);
55
55
  results.push({ [result.type]: result.value });
56
56
  } catch (error) {
57
+ console.log(error)
57
58
  this.error(`Failed to set value for "${key}": ${error.message}`);
58
59
  results.push({ [key]: `Error: ${error.message}` });
59
60
  fill = 'red';
@@ -15,13 +15,19 @@ class HbStatusNode extends HbBaseNode {
15
15
  }
16
16
 
17
17
  const result = await this.hbDevice.refreshCharacteristics();
18
- this.status({
19
- text: this.statusText(JSON.stringify(await this.hbDevice.values)),
20
- shape: 'dot',
21
- fill: 'green'
22
- });
18
+ if (result) {
19
+ this.status({
20
+ text: this.statusText(JSON.stringify(await this.hbDevice.values)),
21
+ shape: 'dot',
22
+ fill: 'green'
23
+ });
24
+
25
+ send(Object.assign(message, this.createMessage(result)));
26
+ } else {
27
+ this.status({ fill: "red", shape: "ring", text: "disconnected" });
28
+ this.error("No response from device", this.name);
29
+ }
23
30
 
24
- send(Object.assign(message, this.createMessage(result)));
25
31
  }
26
32
  }
27
33
 
@@ -243,7 +243,7 @@
243
243
  "payload": "{\"On\": true}",
244
244
  "payloadType": "json",
245
245
  "x": 110,
246
- "y": 280,
246
+ "y": 300,
247
247
  "wires": [
248
248
  [
249
249
  "6703815a8874b156"
@@ -436,14 +436,14 @@
436
436
  "id": "12ce98441601c981",
437
437
  "type": "hb-event",
438
438
  "z": "caef1e7b5b399e80",
439
- "name": "Driveway 8E52",
439
+ "name": "Driveway",
440
440
  "Homebridge": "ECI-T24F2",
441
441
  "Manufacturer": "HikVision",
442
- "Service": "CameraRTPStreamManagement",
443
- "device": "ECI-T24F2CB:6F:94:DD:43:77HikVisionDriveway 8E5200000110",
442
+ "Service": "MotionSensor",
443
+ "device": "ECI-T24F2CB:6F:94:DD:43:77HikVisionDriveway00000085",
444
444
  "conf": "557aec8e8c47e61e",
445
445
  "sendInitialState": true,
446
- "x": 340,
446
+ "x": 320,
447
447
  "y": 520,
448
448
  "wires": [
449
449
  [
@@ -603,5 +603,161 @@
603
603
  "wires": [
604
604
  []
605
605
  ]
606
+ },
607
+ {
608
+ "id": "4b787bfb023ccf23",
609
+ "type": "hb-event",
610
+ "z": "caef1e7b5b399e80",
611
+ "name": "Backyard",
612
+ "Homebridge": "Default Model",
613
+ "Manufacturer": "NRCHKB",
614
+ "Service": "TemperatureSensor",
615
+ "device": "Default Model69:62:B7:AE:38:D4NRCHKBBackyard0000008A",
616
+ "conf": "557aec8e8c47e61e",
617
+ "sendInitialState": true,
618
+ "x": 160,
619
+ "y": 660,
620
+ "wires": [
621
+ [
622
+ "c68bef6563c5d07e"
623
+ ]
624
+ ]
625
+ },
626
+ {
627
+ "id": "c68bef6563c5d07e",
628
+ "type": "debug",
629
+ "z": "caef1e7b5b399e80",
630
+ "name": "debug 7",
631
+ "active": true,
632
+ "tosidebar": true,
633
+ "console": false,
634
+ "tostatus": true,
635
+ "complete": "payload",
636
+ "targetType": "msg",
637
+ "statusVal": "payload",
638
+ "statusType": "auto",
639
+ "x": 620,
640
+ "y": 740,
641
+ "wires": []
642
+ },
643
+ {
644
+ "id": "3c36252a6f45eed9",
645
+ "type": "hb-status",
646
+ "z": "caef1e7b5b399e80",
647
+ "name": "Backyard Tree",
648
+ "Homebridge": "Default Model",
649
+ "Manufacturer": "NRCHKB",
650
+ "Service": "TemperatureSensor",
651
+ "device": "Default Model69:62:B7:AE:38:D4NRCHKBBackyard Tree0000008A",
652
+ "conf": "557aec8e8c47e61e",
653
+ "x": 360,
654
+ "y": 800,
655
+ "wires": [
656
+ [
657
+ "c68bef6563c5d07e"
658
+ ]
659
+ ]
660
+ },
661
+ {
662
+ "id": "3b7537939b63eee8",
663
+ "type": "inject",
664
+ "z": "caef1e7b5b399e80",
665
+ "name": "",
666
+ "props": [
667
+ {
668
+ "p": "payload"
669
+ },
670
+ {
671
+ "p": "topic",
672
+ "vt": "str"
673
+ }
674
+ ],
675
+ "repeat": "60",
676
+ "crontab": "",
677
+ "once": true,
678
+ "onceDelay": "60",
679
+ "topic": "",
680
+ "payload": "",
681
+ "payloadType": "date",
682
+ "x": 150,
683
+ "y": 800,
684
+ "wires": [
685
+ [
686
+ "3c36252a6f45eed9"
687
+ ]
688
+ ]
689
+ },
690
+ {
691
+ "id": "aabce5ff7fbbeec6",
692
+ "type": "status",
693
+ "z": "caef1e7b5b399e80",
694
+ "name": "",
695
+ "scope": [
696
+ "3d7babac3a298e60",
697
+ "452e3e6171aa7a25",
698
+ "0ed3cd7e0d60beda",
699
+ "6703815a8874b156",
700
+ "82638dac6ac32bb1",
701
+ "6216377792cba653",
702
+ "12ce98441601c981",
703
+ "4b787bfb023ccf23",
704
+ "3c36252a6f45eed9"
705
+ ],
706
+ "x": 1020,
707
+ "y": 380,
708
+ "wires": [
709
+ [
710
+ "94f051597d18eaa3"
711
+ ]
712
+ ]
713
+ },
714
+ {
715
+ "id": "94f051597d18eaa3",
716
+ "type": "debug",
717
+ "z": "caef1e7b5b399e80",
718
+ "name": "debug 8",
719
+ "active": true,
720
+ "tosidebar": true,
721
+ "console": false,
722
+ "tostatus": true,
723
+ "complete": "true",
724
+ "targetType": "full",
725
+ "statusVal": "status.source.name",
726
+ "statusType": "msg",
727
+ "x": 1180,
728
+ "y": 380,
729
+ "wires": []
730
+ },
731
+ {
732
+ "id": "296297c79b544f59",
733
+ "type": "catch",
734
+ "z": "caef1e7b5b399e80",
735
+ "name": "",
736
+ "scope": null,
737
+ "uncaught": false,
738
+ "x": 1020,
739
+ "y": 300,
740
+ "wires": [
741
+ [
742
+ "0073218b1ebc53f1"
743
+ ]
744
+ ]
745
+ },
746
+ {
747
+ "id": "0073218b1ebc53f1",
748
+ "type": "debug",
749
+ "z": "caef1e7b5b399e80",
750
+ "name": "debug 9",
751
+ "active": true,
752
+ "tosidebar": true,
753
+ "console": false,
754
+ "tostatus": true,
755
+ "complete": "payload",
756
+ "targetType": "msg",
757
+ "statusVal": "payload",
758
+ "statusType": "auto",
759
+ "x": 1200,
760
+ "y": 300,
761
+ "wires": []
606
762
  }
607
763
  ]