node-red-contrib-homebridge-automation 0.1.12-beta.22 → 0.1.12-beta.24
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 +14 -23
- package/src/hbConfigNode.js +36 -24
- 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.24",
|
|
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,21 @@ class HbBaseNode {
|
|
|
24
24
|
this.on('input', this.handleInput.bind(this));
|
|
25
25
|
}
|
|
26
26
|
this.on('close', this.handleClose.bind(this));
|
|
27
|
+
this.on('event', this.handleHBEventMessage.bind(this));
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
handleHBEventMessage(service) {
|
|
31
|
+
debug('topic for', this.id, service.serviceName, service.values);
|
|
32
|
+
|
|
33
|
+
this.status({
|
|
34
|
+
text: JSON.stringify(service.values),
|
|
35
|
+
shape: 'dot',
|
|
36
|
+
fill: 'green',
|
|
37
|
+
});
|
|
38
|
+
this.send({ payload: service.values });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
29
42
|
registerNode() {
|
|
30
43
|
debug("Registering node:", this.fullName);
|
|
31
44
|
this.hbDevice = hbDevices.findDevice(this.device);
|
|
@@ -38,6 +51,7 @@ class HbBaseNode {
|
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
handleClose(callback) {
|
|
54
|
+
debug('close', this.name);
|
|
41
55
|
callback();
|
|
42
56
|
}
|
|
43
57
|
|
|
@@ -101,29 +115,6 @@ class HbBaseNode {
|
|
|
101
115
|
}
|
|
102
116
|
}
|
|
103
117
|
|
|
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
118
|
async _register(node) {
|
|
128
119
|
try {
|
|
129
120
|
const device = hbDevices.findDevice(node.device, { perms: 'ev' });
|
package/src/hbConfigNode.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
const hbBaseNode = require('./hbBaseNode.js');
|
|
2
1
|
const { HapClient } = require('@homebridge/hap-client');
|
|
3
2
|
const debug = require('debug')('hapNodeRed:hbConfigNode');
|
|
4
|
-
const { Homebridges } = require('./lib/Homebridges.js');
|
|
5
3
|
const { Log } = require('./lib/logger.js');
|
|
6
4
|
const Queue = require('better-queue');
|
|
7
5
|
|
|
@@ -18,6 +16,7 @@ class HBConfigNode {
|
|
|
18
16
|
this.ctDevices = [];
|
|
19
17
|
this.hbDevices = [];
|
|
20
18
|
this.clientNodes = [];
|
|
19
|
+
this.monitorNodes = [];
|
|
21
20
|
this.log = new Log(console, true);
|
|
22
21
|
|
|
23
22
|
this.reqisterQueue = new Queue(this._register.bind(this), {
|
|
@@ -39,9 +38,7 @@ class HBConfigNode {
|
|
|
39
38
|
this.waitForNoMoreDiscoveries();
|
|
40
39
|
this.hapClient.on('instance-discovered', this.waitForNoMoreDiscoveries);
|
|
41
40
|
|
|
42
|
-
this.on('close', ()
|
|
43
|
-
this.close();
|
|
44
|
-
});
|
|
41
|
+
this.on('close', this.close.bind(this));
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
|
|
@@ -67,19 +64,16 @@ class HBConfigNode {
|
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
toList(perms) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
];
|
|
78
|
-
return supportedTypes.includes(service.humanType);
|
|
79
|
-
};
|
|
67
|
+
const supportedTypes = [
|
|
68
|
+
'Battery', 'Carbon Dioxide Sensor', 'Carbon Monoxide Sensor', 'Doorbell',
|
|
69
|
+
'Fan', 'Fanv2', 'Garage Door Opener', 'Humidity Sensor', 'Input Source',
|
|
70
|
+
'Leak Sensor', 'Lightbulb', 'Lock Mechanism', 'Motion Sensor', 'Occupancy Sensor',
|
|
71
|
+
'Outlet', 'Smoke Sensor', 'Speaker', 'Stateless Programmable Switch', 'Switch',
|
|
72
|
+
'Television', 'Temperature Sensor', 'Thermostat', 'Contact Sensor',
|
|
73
|
+
];
|
|
80
74
|
|
|
81
75
|
return this.hbDevices
|
|
82
|
-
.filter(service =>
|
|
76
|
+
.filter(service => supportedTypes.includes(service.humanType))
|
|
83
77
|
.map(service => ({
|
|
84
78
|
name: service.serviceName,
|
|
85
79
|
fullName: `${service.serviceName} - ${service.type}`,
|
|
@@ -122,30 +116,48 @@ class HBConfigNode {
|
|
|
122
116
|
for (const clientNode of clientNodes) {
|
|
123
117
|
debug('_Register %s -> %s', clientNode.type, clientNode.name);
|
|
124
118
|
clientNode.hbDevice = this.hbDevices.find(service => {
|
|
125
|
-
const
|
|
119
|
+
const deviceUnique = `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`;
|
|
126
120
|
clientNode.status({ fill: 'green', shape: 'dot', text: 'connected' });
|
|
127
|
-
return clientNode.device ===
|
|
121
|
+
return clientNode.device === deviceUnique;
|
|
128
122
|
});
|
|
129
123
|
|
|
124
|
+
if (clientNode.config.type === 'hb-status') {
|
|
125
|
+
this.monitorNodes[clientNode.device] = clientNode.hbDevice;
|
|
126
|
+
}
|
|
127
|
+
|
|
130
128
|
if (!clientNode.hbDevice) {
|
|
131
129
|
console.error('ERROR: _register - HB Device Missing', clientNode.name);
|
|
132
130
|
}
|
|
133
131
|
}
|
|
132
|
+
|
|
133
|
+
if (Object.keys(this.monitorNodes).length) {
|
|
134
|
+
this.monitor = await this.hapClient.monitorCharacteristics(Object.values(this.monitorNodes));
|
|
135
|
+
this.monitor.on('service-update', (services) => {
|
|
136
|
+
for (const service of services) {
|
|
137
|
+
const eventNodes = Object.values(this.clientNodes).filter(
|
|
138
|
+
clientNode =>
|
|
139
|
+
clientNode.config.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
eventNodes.forEach((eventNode) => {
|
|
143
|
+
if (eventNode._events && typeof eventNode.emit === 'function') {
|
|
144
|
+
eventNode.emit('event', service);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
134
150
|
cb(null);
|
|
135
151
|
}
|
|
136
152
|
|
|
137
153
|
deregister(clientNode) {
|
|
138
|
-
clientNode.status({
|
|
139
|
-
text: 'disconnected',
|
|
140
|
-
shape: 'ring',
|
|
141
|
-
fill: 'red',
|
|
142
|
-
});
|
|
154
|
+
clientNode.status({ text: 'disconnected', shape: 'ring', fill: 'red' });
|
|
143
155
|
delete this.clientNodes[clientNode.id];
|
|
144
156
|
}
|
|
145
157
|
|
|
146
158
|
close() {
|
|
147
159
|
if (this.hapClient) {
|
|
148
|
-
this.hapClient.
|
|
160
|
+
this.hapClient.destroy();
|
|
149
161
|
}
|
|
150
162
|
}
|
|
151
163
|
}
|
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, send) {
|
|
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
|
+
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"
|