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 +2 -2
- package/src/HapDeviceRoutes.js +1 -1
- package/src/hbConfigNode.js +41 -58
- package/src/hbControlNode.js +1 -0
- package/src/hbStatusNode.js +12 -6
- package/test/node-red/flows.json +161 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-homebridge-automation",
|
|
3
|
-
"version": "0.1.12-beta.
|
|
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.
|
|
51
|
+
"@homebridge/hap-client": "2.0.5-beta.7"
|
|
52
52
|
},
|
|
53
53
|
"author": "NorthernMan54",
|
|
54
54
|
"license": "ISC",
|
package/src/HapDeviceRoutes.js
CHANGED
|
@@ -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();
|
package/src/hbConfigNode.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
-
if (seenFullNames.
|
|
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.
|
|
103
|
-
console.error('ERROR:
|
|
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
|
|
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
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
165
|
-
this.hapClient.destroy();
|
|
166
|
-
}
|
|
150
|
+
this.hapClient?.destroy();
|
|
167
151
|
}
|
|
168
|
-
|
|
169
152
|
}
|
|
170
153
|
|
|
171
|
-
|
|
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;
|
package/src/hbControlNode.js
CHANGED
|
@@ -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';
|
package/src/hbStatusNode.js
CHANGED
|
@@ -15,13 +15,19 @@ class HbStatusNode extends HbBaseNode {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const result = await this.hbDevice.refreshCharacteristics();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
package/test/node-red/flows.json
CHANGED
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
"payload": "{\"On\": true}",
|
|
244
244
|
"payloadType": "json",
|
|
245
245
|
"x": 110,
|
|
246
|
-
"y":
|
|
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
|
|
439
|
+
"name": "Driveway",
|
|
440
440
|
"Homebridge": "ECI-T24F2",
|
|
441
441
|
"Manufacturer": "HikVision",
|
|
442
|
-
"Service": "
|
|
443
|
-
"device": "ECI-T24F2CB:6F:94:DD:43:
|
|
442
|
+
"Service": "MotionSensor",
|
|
443
|
+
"device": "ECI-T24F2CB:6F:94:DD:43:77HikVisionDriveway00000085",
|
|
444
444
|
"conf": "557aec8e8c47e61e",
|
|
445
445
|
"sendInitialState": true,
|
|
446
|
-
"x":
|
|
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
|
]
|