node-red-contrib-homebridge-automation 0.1.12-beta.22 → 0.1.12-beta.23
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/hbBaseNode.js +12 -23
- package/src/hbConfigNode.js +53 -6
- package/src/hbControlNode.js +7 -18
- package/src/hbStatusNode.js +16 -38
- package/test/node-red/flows.json +1 -2
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.23",
|
|
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.
|
|
51
|
+
"@homebridge/hap-client": "2.0.5-beta.2"
|
|
52
52
|
},
|
|
53
53
|
"author": "NorthernMan54",
|
|
54
54
|
"license": "ISC",
|
package/src/hbBaseNode.js
CHANGED
|
@@ -24,8 +24,19 @@ class HbBaseNode {
|
|
|
24
24
|
this.on('input', this.handleInput.bind(this));
|
|
25
25
|
}
|
|
26
26
|
this.on('close', this.handleClose.bind(this));
|
|
27
|
+
this.hbConfigNode.on('event', this.handleHBEventMessage.bind(this));
|
|
28
|
+
this.on('topic', this.handleHBTopicMessage.bind(this));
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
handleHBEventMessage(service) {
|
|
32
|
+
debug('event for', this.id, service.serviceName, service.values);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
handleHBTopicMessage(service) {
|
|
36
|
+
debug('topic for', this.id, service.serviceName, service.values);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
29
40
|
registerNode() {
|
|
30
41
|
debug("Registering node:", this.fullName);
|
|
31
42
|
this.hbDevice = hbDevices.findDevice(this.device);
|
|
@@ -38,6 +49,7 @@ class HbBaseNode {
|
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
handleClose(callback) {
|
|
52
|
+
debug('close', this.name);
|
|
41
53
|
callback();
|
|
42
54
|
}
|
|
43
55
|
|
|
@@ -101,29 +113,6 @@ class HbBaseNode {
|
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
115
|
|
|
104
|
-
async _control(node, payload) {
|
|
105
|
-
try {
|
|
106
|
-
const device = hbDevices.findDevice(node.device, { perms: 'pw' });
|
|
107
|
-
if (!device) throw new Error('Device not available');
|
|
108
|
-
|
|
109
|
-
const message = typeof payload === "object"
|
|
110
|
-
? this._createControlMessage(payload, node, device)
|
|
111
|
-
: null;
|
|
112
|
-
|
|
113
|
-
if (message && message.characteristics.length > 0) {
|
|
114
|
-
const status = await this.HAPcontrolByDeviceIDAsync(device.id, JSON.stringify(message));
|
|
115
|
-
node.status({ text: 'Controlled', shape: 'dot', fill: 'green' });
|
|
116
|
-
return status;
|
|
117
|
-
} else {
|
|
118
|
-
throw new Error('Invalid payload');
|
|
119
|
-
}
|
|
120
|
-
} catch (err) {
|
|
121
|
-
debug("Error in _control:", err);
|
|
122
|
-
node.status({ text: 'Control error', shape: 'ring', fill: 'red' });
|
|
123
|
-
throw err;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
116
|
async _register(node) {
|
|
128
117
|
try {
|
|
129
118
|
const device = hbDevices.findDevice(node.device, { perms: 'ev' });
|
package/src/hbConfigNode.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
const
|
|
1
|
+
// const EventEmitter = require('events'); // Import EventEmitter
|
|
2
2
|
const { HapClient } = require('@homebridge/hap-client');
|
|
3
3
|
const debug = require('debug')('hapNodeRed:hbConfigNode');
|
|
4
|
-
const { Homebridges } = require('./lib/Homebridges.js');
|
|
5
4
|
const { Log } = require('./lib/logger.js');
|
|
6
5
|
const Queue = require('better-queue');
|
|
7
6
|
|
|
8
|
-
class HBConfigNode {
|
|
7
|
+
class HBConfigNode { // Extend EventEmitter
|
|
9
8
|
constructor(config, RED) {
|
|
9
|
+
// super(); // Call EventEmitter's constructor
|
|
10
|
+
|
|
10
11
|
if (!config.jest) {
|
|
11
12
|
RED.nodes.createNode(this, config);
|
|
12
13
|
|
|
@@ -18,6 +19,7 @@ class HBConfigNode {
|
|
|
18
19
|
this.ctDevices = [];
|
|
19
20
|
this.hbDevices = [];
|
|
20
21
|
this.clientNodes = [];
|
|
22
|
+
this.monitorNodes = [];
|
|
21
23
|
this.log = new Log(console, true);
|
|
22
24
|
|
|
23
25
|
this.reqisterQueue = new Queue(this._register.bind(this), {
|
|
@@ -40,6 +42,7 @@ class HBConfigNode {
|
|
|
40
42
|
this.hapClient.on('instance-discovered', this.waitForNoMoreDiscoveries);
|
|
41
43
|
|
|
42
44
|
this.on('close', () => {
|
|
45
|
+
debug('close');
|
|
43
46
|
this.close();
|
|
44
47
|
});
|
|
45
48
|
}
|
|
@@ -122,15 +125,59 @@ class HBConfigNode {
|
|
|
122
125
|
for (const clientNode of clientNodes) {
|
|
123
126
|
debug('_Register %s -> %s', clientNode.type, clientNode.name);
|
|
124
127
|
clientNode.hbDevice = this.hbDevices.find(service => {
|
|
125
|
-
const
|
|
128
|
+
const deviceUnique = `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`;
|
|
126
129
|
clientNode.status({ fill: 'green', shape: 'dot', text: 'connected' });
|
|
127
|
-
return clientNode.device ===
|
|
130
|
+
return clientNode.device === deviceUnique;
|
|
128
131
|
});
|
|
129
132
|
|
|
133
|
+
debug(clientNode.type);
|
|
134
|
+
if (clientNode.config.type === 'hb-status') {
|
|
135
|
+
debug('Adding', clientNode.name, clientNode.device)
|
|
136
|
+
this.monitorNodes[clientNode.device] = clientNode.hbDevice;
|
|
137
|
+
}
|
|
138
|
+
console.log(this.monitorNodes);
|
|
130
139
|
if (!clientNode.hbDevice) {
|
|
131
140
|
console.error('ERROR: _register - HB Device Missing', clientNode.name);
|
|
132
141
|
}
|
|
133
142
|
}
|
|
143
|
+
debug(`Registering ${Object.keys(this.monitorNodes).length} nodes for events`);
|
|
144
|
+
// console.log(Object.keys(this.monitorNodes));
|
|
145
|
+
if (Object.keys(this.monitorNodes).length) {
|
|
146
|
+
debug(`Registering ${Object.keys(this.monitorNodes).length} nodes for events`);
|
|
147
|
+
this.monitor = await this.hapClient.monitorCharacteristics(Object.values(this.monitorNodes));
|
|
148
|
+
this.monitor.on('service-update', (services) => {
|
|
149
|
+
// Emit events for updated services
|
|
150
|
+
for (const service of services) {
|
|
151
|
+
debug(`${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`,
|
|
152
|
+
service.values)
|
|
153
|
+
this.emit(
|
|
154
|
+
`${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`,
|
|
155
|
+
service.values
|
|
156
|
+
);
|
|
157
|
+
debug('event',
|
|
158
|
+
service.serviceName, service.values)
|
|
159
|
+
this.emit(
|
|
160
|
+
'event',
|
|
161
|
+
service
|
|
162
|
+
);
|
|
163
|
+
// console.log(this.clientNodes);
|
|
164
|
+
|
|
165
|
+
const eventNodes = Object.values(this.clientNodes).filter(
|
|
166
|
+
|
|
167
|
+
(clientNode) =>
|
|
168
|
+
clientNode.config.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
|
|
169
|
+
|
|
170
|
+
);
|
|
171
|
+
console.log('eventNodes, ', eventNodes);
|
|
172
|
+
eventNodes.forEach((eventNode) => {
|
|
173
|
+
debug('emit', eventNode.name, eventNode.type)
|
|
174
|
+
if (eventNode._events && typeof eventNode.emit === 'function') {
|
|
175
|
+
eventNode.emit('topic', service);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
134
181
|
cb(null);
|
|
135
182
|
}
|
|
136
183
|
|
|
@@ -145,7 +192,7 @@ class HBConfigNode {
|
|
|
145
192
|
|
|
146
193
|
close() {
|
|
147
194
|
if (this.hapClient) {
|
|
148
|
-
this.hapClient.
|
|
195
|
+
this.hapClient.destroy();
|
|
149
196
|
}
|
|
150
197
|
}
|
|
151
198
|
}
|
package/src/hbControlNode.js
CHANGED
|
@@ -8,7 +8,6 @@ class HbControlNode extends hbBaseNode {
|
|
|
8
8
|
|
|
9
9
|
async handleInput(message) {
|
|
10
10
|
debug('handleInput', message.payload, this.name);
|
|
11
|
-
|
|
12
11
|
if (!this.hbDevice) {
|
|
13
12
|
this.error('HB not initialized');
|
|
14
13
|
this.status({ text: 'HB not initialized', shape: 'ring', fill: 'red' });
|
|
@@ -28,19 +27,13 @@ class HbControlNode extends hbBaseNode {
|
|
|
28
27
|
let fill = 'green';
|
|
29
28
|
|
|
30
29
|
for (const key of Object.keys(message.payload)) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.error(`Failed to set value for ${key}: ${error.message}`);
|
|
39
|
-
fill = 'red';
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
this.error(`Invalid characteristic '${key}' found in the message: ${JSON.stringify(message.payload)}`);
|
|
43
|
-
results.push({ 'Invalid Key': key });
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const result = await this.hbDevice.setCharacteristicByType(key, message.payload[key]);
|
|
33
|
+
results.push({ [result.type]: result.value });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
this.error(`Failed to set value for ${key}: ${error.message}`);
|
|
36
|
+
results.push({ key: key + ' ' + error.message })
|
|
44
37
|
fill = 'red';
|
|
45
38
|
}
|
|
46
39
|
}
|
|
@@ -51,10 +44,6 @@ class HbControlNode extends hbBaseNode {
|
|
|
51
44
|
fill,
|
|
52
45
|
});
|
|
53
46
|
}
|
|
54
|
-
|
|
55
|
-
handleClose(callback) {
|
|
56
|
-
callback();
|
|
57
|
-
}
|
|
58
47
|
}
|
|
59
48
|
|
|
60
49
|
module.exports = HbControlNode;
|
package/src/hbStatusNode.js
CHANGED
|
@@ -1,51 +1,29 @@
|
|
|
1
|
-
const HbBaseNode = require('./hbBaseNode');
|
|
1
|
+
const HbBaseNode = require('./hbBaseNode');
|
|
2
2
|
const debug = require('debug')('hapNodeRed:hbStatusNode');
|
|
3
3
|
|
|
4
4
|
class HbStatusNode extends HbBaseNode {
|
|
5
5
|
constructor(config, RED) {
|
|
6
|
-
// console.log('hbStatusNode - contructor', config);
|
|
7
6
|
super(config, RED);
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* @param {Object} msg - Input message
|
|
13
|
-
*/
|
|
14
|
-
handleInput(msg, send, done) {
|
|
15
|
-
this.msg = msg;
|
|
16
|
-
debug('handleInput', this);
|
|
17
|
-
this.node._status(this.device, this, { perms: 'pr' }, (err, message) => {
|
|
18
|
-
if (!err) {
|
|
19
|
-
debug(
|
|
20
|
-
"hbStatus received: %s = %s",
|
|
21
|
-
JSON.stringify(this.fullName),
|
|
22
|
-
JSON.stringify(message).slice(0, 80) + '...',
|
|
23
|
-
JSON.stringify(this.hbDevice)
|
|
24
|
-
);
|
|
9
|
+
async handleInput(message) {
|
|
10
|
+
debug('handleInput', message.payload, this.name);
|
|
25
11
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
if (!this.hbDevice) {
|
|
13
|
+
this.error('HB not initialized');
|
|
14
|
+
this.status({ text: 'HB not initialized', shape: 'ring', fill: 'red' });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.msg._confId = this.confId;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
this.status({
|
|
39
|
-
text: JSON.stringify(this.msg.payload).slice(0, 30) + '...',
|
|
40
|
-
shape: 'dot',
|
|
41
|
-
fill: 'green',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
this.send(this.msg);
|
|
45
|
-
} else {
|
|
46
|
-
this.error(err, this.msg);
|
|
47
|
-
}
|
|
18
|
+
const result = await this.hbDevice.refreshCharacteristics();
|
|
19
|
+
this.status({
|
|
20
|
+
text: JSON.stringify(await this.hbDevice.values),
|
|
21
|
+
shape: 'dot',
|
|
22
|
+
fill: 'green'
|
|
48
23
|
});
|
|
24
|
+
|
|
25
|
+
message.payload = result.values;
|
|
26
|
+
this.send(message);
|
|
49
27
|
}
|
|
50
28
|
}
|
|
51
29
|
|
package/test/node-red/flows.json
CHANGED
|
@@ -81,7 +81,6 @@
|
|
|
81
81
|
"id": "3d7babac3a298e60",
|
|
82
82
|
"type": "hb-status",
|
|
83
83
|
"z": "caef1e7b5b399e80",
|
|
84
|
-
"d": true,
|
|
85
84
|
"name": "West Bedroom",
|
|
86
85
|
"Homebridge": "homebridge",
|
|
87
86
|
"Manufacturer": "Tasmota",
|
|
@@ -108,7 +107,7 @@
|
|
|
108
107
|
"device": "homebridge1C:22:3D:E3:CF:34TasmotaWest Bedroom00000043",
|
|
109
108
|
"conf": "557aec8e8c47e61e",
|
|
110
109
|
"x": 480,
|
|
111
|
-
"y":
|
|
110
|
+
"y": 300,
|
|
112
111
|
"wires": [
|
|
113
112
|
[
|
|
114
113
|
"a866ae0bb24ce682"
|