node-red-contrib-homebridge-automation 0.1.12-beta.30 → 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.30",
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,9 +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('ctDevices', this.hbDevices.filter(service => service.type == 'CameraRTPStreamManagement' && service.serviceName == 'Driveway 8E52'));
61
+ this.log.info(`Devices initialized: evDevices: ${this.evDevices.length}, ctDevices: ${this.ctDevices.length}`);
62
62
  this.handleDuplicates(this.evDevices);
63
- debug('Queue', this.reqisterQueue.getStats());
63
+ debug('Queue stats:', this.reqisterQueue.getStats());
64
64
  this.reqisterQueue.resume();
65
65
  }
66
66
 
@@ -73,7 +73,7 @@ class HBConfigNode {
73
73
  'Television', 'Temperature Sensor', 'Thermostat', 'Contact Sensor',
74
74
  ];
75
75
 
76
- return this.hbDevices
76
+ return filterUnique(this.hbDevices)
77
77
  .filter(service => supportedTypes.includes(service.humanType))
78
78
  .map(service => ({
79
79
  name: service.serviceName,
@@ -91,23 +91,19 @@ class HBConfigNode {
91
91
  const seenFullNames = new Set();
92
92
  const seenUniqueIds = new Set();
93
93
 
94
- for (const endpoint of list) {
95
- if (seenFullNames.has(endpoint.fullName)) {
94
+ list.forEach(endpoint => {
95
+ if (!seenFullNames.add(endpoint.fullName)) {
96
96
  console.warn('WARNING: Duplicate device name', endpoint.fullName);
97
- } else {
98
- seenFullNames.add(endpoint.fullName);
99
97
  }
100
98
 
101
- if (seenUniqueIds.has(endpoint.uniqueId)) {
102
- console.error('ERROR: Parsing failed, duplicate uniqueID.', endpoint.fullName);
103
- } else {
104
- seenUniqueIds.add(endpoint.uniqueId);
99
+ if (!seenUniqueIds.add(endpoint.uniqueId)) {
100
+ console.error('ERROR: Duplicate uniqueId detected.', endpoint.fullName);
105
101
  }
106
- }
102
+ });
107
103
  }
108
104
 
109
105
  register(clientNode) {
110
- debug('Register %s -> %s', clientNode.type, clientNode.name);
106
+ debug('Register: %s type: %s', clientNode.type, clientNode.name);
111
107
  this.clientNodes[clientNode.id] = clientNode;
112
108
  this.reqisterQueue.push(clientNode);
113
109
  clientNode.status({ fill: 'yellow', shape: 'ring', text: 'connecting' });
@@ -115,56 +111,56 @@ class HBConfigNode {
115
111
 
116
112
  async _register(clientNodes, cb) {
117
113
  for (const clientNode of clientNodes) {
118
- debug('_Register %s -> %s', clientNode.type, clientNode.name);
119
- clientNode.hbDevice = this.hbDevices.find(service => {
120
- const deviceUnique = `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`;
121
- if (clientNode.device === deviceUnique) {
122
- clientNode.status({ fill: 'green', shape: 'dot', text: 'connected' });
123
- 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;
124
125
  }
125
- return clientNode.device === deviceUnique;
126
- });
127
-
128
- if (clientNode.config.type === 'hb-status') {
129
- this.monitorNodes[clientNode.device] = clientNode.hbDevice;
130
- }
131
-
132
- if (!clientNode.hbDevice) {
133
- console.error('ERROR: _register - HB Device Missing', clientNode.name);
126
+ } else {
127
+ console.error('ERROR: Device registration failed', clientNode.name);
134
128
  }
135
129
  }
136
130
 
131
+ await this.monitorDevices();
132
+
133
+ cb(null);
134
+ }
135
+
136
+ async monitorDevices() {
137
137
  if (Object.keys(this.monitorNodes).length) {
138
138
  this.monitor = await this.hapClient.monitorCharacteristics(Object.values(this.monitorNodes));
139
139
  this.monitor.on('service-update', (services) => {
140
- for (const service of services) {
141
- const eventNodes = Object.values(this.clientNodes).filter(
142
- clientNode =>
143
- 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)}`
144
143
  );
145
-
146
- eventNodes.forEach((eventNode) => {
147
- if (eventNode._events && typeof eventNode.emit === 'function') {
148
- eventNode.emit('hbEvent', service);
149
- }
150
- });
151
- }
144
+ eventNodes.forEach(eventNode => eventNode.emit('hbEvent', service));
145
+ });
152
146
  });
153
147
  }
154
- cb(null);
155
148
  }
156
- /*
157
- deregister(clientNode) {
158
- clientNode.status({ text: 'disconnected', shape: 'ring', fill: 'red' });
159
- delete this.clientNodes[clientNode.id];
160
- }
161
- */
162
149
  close() {
163
- if (this.hapClient) {
164
- this.hapClient.destroy();
165
- }
150
+ this.hapClient?.destroy();
166
151
  }
167
-
168
152
  }
169
153
 
154
+
155
+ // Filter unique devices by AID, service name, username, and port
156
+ const filterUnique = (data) => {
157
+ const seen = new Set();
158
+ return data.filter(item => {
159
+ const uniqueKey = `${item.aid}-${item.serviceName}-${item.instance.username}-${item.instance.port}`;
160
+ if (seen.has(uniqueKey)) return false;
161
+ seen.add(uniqueKey);
162
+ return true;
163
+ });
164
+ };
165
+
170
166
  module.exports = HBConfigNode;
@@ -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
  ]