node-red-contrib-homebridge-automation 0.3.0-beta.1 → 0.3.0-beta.11

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/README.md CHANGED
@@ -9,59 +9,59 @@ The above Node-RED Flow, turns on my 'Outside Office' light when the powder room
9
9
  # Table of Contents
10
10
 
11
11
  <!--ts-->
12
- - [Homebridge Automation powered by Node-RED](#homebridge-automation-powered-by-node-red)
13
- - [Table of Contents](#table-of-contents)
14
- - [Introduction](#introduction)
15
- - [Caveats](#caveats)
16
- - [Changes](#changes)
17
- - [Mar 18, 2019 - Version 0.0.39](#mar-18-2019---version-0039)
18
- - [Mar 19, 2019 - Version 0.0.42](#mar-19-2019---version-0042)
19
- - [Mar 31, 2019 - Version 0.0.](#mar-31-2019---version-00)
20
- - [May 9, 2019 - Version 0.0.43](#may-9-2019---version-0043)
21
- - [May 15, 2019 - Version 0.0.44](#may-15-2019---version-0044)
22
- - [May 29, 2019 - Version 0.0.45](#may-29-2019---version-0045)
23
- - [July 27, 2019 - Version 0.0.50](#july-27-2019---version-0050)
24
- - [Feb 24, 2020 - Version 0.0.56](#feb-24-2020---version-0056)
25
- - [Mar 18, 2020 - Version 0.0.59](#mar-18-2020---version-0059)
26
- - [Oct 13, 2020 - Version 0.0.71](#oct-13-2020---version-0071)
27
- - [Oct 24, 2020 - Version 0.0.71](#oct-24-2020---version-0071)
28
- - [Nov 1, 2020 - Version 0.0.73](#nov-1-2020---version-0073)
29
- - [Nov 13, 2020 - Version 0.0.76](#nov-13-2020---version-0076)
30
- - [Feb 2, 2021 - Version 0.0.78](#feb-2-2021---version-0078)
31
- - [Mar 9, 2021 - Version 0.0.80](#mar-9-2021---version-0080)
32
- - [April 23, 2021 - Version 0.0.82](#april-23-2021---version-0082)
33
- - [Dec 31, 2022 - Version 0.1.3](#dec-31-2022---version-013)
34
- - [Jan 6, 2023 - Version 0.1.5](#jan-6-2023---version-015)
35
- - [Jan 11, 2023 - Version 0.1.7](#jan-11-2023---version-017)
36
- - [Jan 15, 2023 - Version 0.1.8](#jan-15-2023---version-018)
37
- - [Dec 15, 2024 - Version 0.2.0](#dec-15-2024---version-020)
38
- - [April 15, 2025 - Version 0.3.0](#april-15-2025---version-030)
39
- - [Breaking Change](#breaking-change)
40
- - [Fixes](#fixes)
41
- - [Backlog / Roadmap](#backlog--roadmap)
42
- - [Dropped items](#dropped-items)
43
- - [Installation Steps](#installation-steps)
44
- - [1 - Install Node-RED and Homebridge](#1---install-node-red-and-homebridge)
45
- - [2 - Prepare Homebridge for integration with Homebridge-Automation](#2---prepare-homebridge-for-integration-with-homebridge-automation)
46
- - [3 - Install Homebridge-Automation into Node-Red](#3---install-homebridge-automation-into-node-red)
47
- - [4 - Start Node-Red](#4---start-node-red)
48
- - [5 - Initial setup and configuration inside Node-Red](#5---initial-setup-and-configuration-inside-node-red)
49
- - [6 - Configure 'hb event' to receive updates from your Accessories](#6---configure-hb-event-to-receive-updates-from-your-accessories)
50
- - [Node-RED Homebridge-Automation Message Structure](#node-red-homebridge-automation-message-structure)
51
- - [hb event](#hb-event)
52
- - [Output](#output)
53
- - [hb resume](#hb-resume)
54
- - [input](#input)
55
- - [output](#output-1)
56
- - [hb status](#hb-status)
57
- - [input](#input-1)
58
- - [output](#output-2)
59
- - [hb control](#hb-control)
60
- - [Input](#input-2)
61
- - [Flows Shared from Community](#flows-shared-from-community)
62
- - [Donations](#donations)
63
- - [Troubleshooting / DEBUG MODE](#troubleshooting--debug-mode)
64
- - [To start Node-RED in DEBUG mode, and output Homebridge-Automation debug logs start Node-RED like this.](#to-start-node-red-in-debug-mode-and-output-homebridge-automation-debug-logs-start-node-red-like-this)
12
+ * [Homebridge Automation powered by Node-RED](#homebridge-automation-powered-by-node-red)
13
+ * [Table of Contents](#table-of-contents)
14
+ * [Introduction](#introduction)
15
+ * [Caveats](#caveats)
16
+ * [Changes](#changes)
17
+ * [Mar 18, 2019 - Version 0.0.39](#mar-18-2019---version-0039)
18
+ * [Mar 19, 2019 - Version 0.0.42](#mar-19-2019---version-0042)
19
+ * [Mar 31, 2019 - Version 0.0.](#mar-31-2019---version-00)
20
+ * [May 9, 2019 - Version 0.0.43](#may-9-2019---version-0043)
21
+ * [May 15, 2019 - Version 0.0.44](#may-15-2019---version-0044)
22
+ * [May 29, 2019 - Version 0.0.45](#may-29-2019---version-0045)
23
+ * [July 27, 2019 - Version 0.0.50](#july-27-2019---version-0050)
24
+ * [Feb 24, 2020 - Version 0.0.56](#feb-24-2020---version-0056)
25
+ * [Mar 18, 2020 - Version 0.0.59](#mar-18-2020---version-0059)
26
+ * [Oct 13, 2020 - Version 0.0.71](#oct-13-2020---version-0071)
27
+ * [Oct 24, 2020 - Version 0.0.71](#oct-24-2020---version-0071)
28
+ * [Nov 1, 2020 - Version 0.0.73](#nov-1-2020---version-0073)
29
+ * [Nov 13, 2020 - Version 0.0.76](#nov-13-2020---version-0076)
30
+ * [Feb 2, 2021 - Version 0.0.78](#feb-2-2021---version-0078)
31
+ * [Mar 9, 2021 - Version 0.0.80](#mar-9-2021---version-0080)
32
+ * [April 23, 2021 - Version 0.0.82](#april-23-2021---version-0082)
33
+ * [Dec 31, 2022 - Version 0.1.3](#dec-31-2022---version-013)
34
+ * [Jan 6, 2023 - Version 0.1.5](#jan-6-2023---version-015)
35
+ * [Jan 11, 2023 - Version 0.1.7](#jan-11-2023---version-017)
36
+ * [Jan 15, 2023 - Version 0.1.8](#jan-15-2023---version-018)
37
+ * [Dec 15, 2024 - Version 0.2.0](#dec-15-2024---version-020)
38
+ * [April 15, 2025 - Version 0.3.0](#april-15-2025---version-030)
39
+ * [Breaking Change](#breaking-change)
40
+ * [Fixes](#fixes)
41
+ * [Backlog / Roadmap](#backlog--roadmap)
42
+ * [Dropped items](#dropped-items)
43
+ * [Installation Steps](#installation-steps)
44
+ * [1 - Install Node-RED and Homebridge](#1---install-node-red-and-homebridge)
45
+ * [2 - Prepare Homebridge for integration with Homebridge-Automation](#2---prepare-homebridge-for-integration-with-homebridge-automation)
46
+ * [3 - Install Homebridge-Automation into Node-Red](#3---install-homebridge-automation-into-node-red)
47
+ * [4 - Start Node-Red](#4---start-node-red)
48
+ * [5 - Initial setup and configuration inside Node-Red](#5---initial-setup-and-configuration-inside-node-red)
49
+ * [6 - Configure 'hb event' to receive updates from your Accessories](#6---configure-hb-event-to-receive-updates-from-your-accessories)
50
+ * [Node-RED Homebridge-Automation Message Structure](#node-red-homebridge-automation-message-structure)
51
+ * [hb event](#hb-event)
52
+ * [Output](#output)
53
+ * [hb resume](#hb-resume)
54
+ * [input](#input)
55
+ * [output](#output-1)
56
+ * [hb status](#hb-status)
57
+ * [input](#input-1)
58
+ * [output](#output-2)
59
+ * [hb control](#hb-control)
60
+ * [Input](#input-2)
61
+ * [Flows Shared from Community](#flows-shared-from-community)
62
+ * [Donations](#donations)
63
+ * [Troubleshooting / DEBUG MODE](#troubleshooting--debug-mode)
64
+ * [To start Node-RED in DEBUG mode, and output Homebridge-Automation debug logs start Node-RED like this.](#to-start-node-red-in-debug-mode-and-output-homebridge-automation-debug-logs-start-node-red-like-this)
65
65
  <!--te-->
66
66
 
67
67
  # Introduction
@@ -199,6 +199,7 @@ With a plugin, you can see if it supports Real Time events, by opening the Home
199
199
 
200
200
  - Add common supported types Window, Window Covering, Light Sensor #151, tks @HDeKnop
201
201
  - HB-Control Node Turns Off then On when this message is received #152
202
+ - Added `Debug logging` configuration option that creates a file `homebridge-automation-endpoints.json`, which contains all the homebridge devices discovered. It can be used as part of troubleshooting device issues.
202
203
 
203
204
  # Backlog / Roadmap
204
205
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-homebridge-automation",
3
- "version": "0.3.0-beta.1",
3
+ "version": "0.3.0-beta.11",
4
4
  "description": "NodeRED Automation for HomeBridge",
5
5
  "main": "src/HAP-NodeRed.js",
6
6
  "scripts": {
@@ -42,7 +42,7 @@
42
42
  "dependencies": {
43
43
  "better-queue": ">=3.8.12",
44
44
  "debug": "^4.3.7",
45
- "@homebridge/hap-client": "^2.1.0-beta.5"
45
+ "@homebridge/hap-client": "^2.1.0"
46
46
  },
47
47
  "author": "NorthernMan54",
48
48
  "license": "ISC",
@@ -7,10 +7,23 @@
7
7
  <label for="node-config-input-macAddress"><i class="icon-wifi"></i> MAC Address (optional)</label>
8
8
  <input type="text" id="node-config-input-macAddress" placeholder="00:00:00:A1:2B:CC">
9
9
  </div>
10
+ <div class="form-row">
11
+ <label for="node-config-input-debug"><i class="icon-tag"></i>Debug Logging</label>
12
+ <input type="checkbox" id="node-config-input-debug" placeholder=false>
13
+ </div>
10
14
  </script>
11
15
 
12
16
  <script type="text/x-red" data-help-name="hb-conf">
13
- <p>This allows you to register your homebridge PIN</p>
17
+ <p>Configuration Node for Homebridge</p>
18
+ <h3>Settings</h3>
19
+ <dl class="message-properties">
20
+ <dt>PIN<span class="property-type">string</span></dt>
21
+ <dd>Please enter the PIN from your homebridge instances. Please note that the same pin must be used for all instances.</dd>
22
+ <dt>MAC Address<span class="property-type">object</span></dt>
23
+ <dd>Optional - Not implemented</dd>
24
+ <dt>Debug Logging<span class="property-type">string</span></dt>
25
+ <dd>Enables debug logging and creates a file `homebridge-automation-endpoints.json`, which contains all the homebridge devices discovered. It can be used as part of troubleshooting device issues.</dd>
26
+ </dl>
14
27
  </script>
15
28
 
16
29
  <script type="text/javascript">
@@ -36,6 +49,10 @@
36
49
  },
37
50
  required: false
38
51
  },
52
+ debug: {
53
+ value: false,
54
+ required: false
55
+ }
39
56
  },
40
57
  credentials: {
41
58
  password: {
@@ -2,6 +2,7 @@ const { HapClient } = require('@homebridge/hap-client');
2
2
  const debug = require('debug')('hapNodeRed:hbConfigNode');
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const process = require('process');
5
6
 
6
7
  class HBConfigNode {
7
8
  constructor(config, RED) {
@@ -10,6 +11,7 @@ class HBConfigNode {
10
11
  // Initialize properties
11
12
  this.username = config.username;
12
13
  this.macAddress = config.macAddress || '';
14
+ this.debugLogging = config.debug || false;
13
15
  this.users = {};
14
16
  this.homebridge = null;
15
17
  this.evDevices = [];
@@ -53,14 +55,14 @@ class HBConfigNode {
53
55
  */
54
56
  async handleReady() {
55
57
  const updatedDevices = await this.hapClient.getAllServices();
56
- if (this.debug && updatedDevices && updatedDevices.length) {
58
+ if (this.debugLogging && updatedDevices && updatedDevices.length && process.uptime() < 300) {
57
59
  try {
58
- const storagePath = path.join(process.cwd(), '/homebridge-automation-endpoints.json');
60
+ const storagePath = path.join(process.cwd(), 'homebridge-automation-endpoints.json');
59
61
  this.warn(`Writing Homebridge endpoints to ${storagePath}`);
60
62
  fs.writeFileSync(storagePath, JSON.stringify(updatedDevices, null, 2));
61
63
  } catch (e) {
62
64
  this.error(`Error writing Homebridge endpoints to file: ${e.message}`);
63
- }
65
+ }
64
66
  }
65
67
  // Fix broken uniqueId's from HAP-Client
66
68
  updatedDevices.forEach((service) => {
@@ -136,10 +138,12 @@ class HBConfigNode {
136
138
  async connectClientNodes() {
137
139
  debug('connect %s nodes', Object.keys(this.clientNodes).length);
138
140
  for (const [key, clientNode] of Object.entries(this.clientNodes)) {
139
- // debug('_Register: %s type: %s', clientNode.type, clientNode.name, clientNode.instance);
140
- const matchedDevice = this.hbDevices.find(service =>
141
- clientNode.device === `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${service.serviceName}${service.uuid.slice(0, 8)}`
142
- );
141
+ // debug('_Register: %s type: "%s" "%s" "%s"', clientNode.type, clientNode.name, clientNode.instance, clientNode.device);
142
+ const matchedDevice = this.hbDevices.find(service => {
143
+ const friendlyName = (service.accessoryInformation.Name ? service.accessoryInformation.Name : service.serviceName);
144
+ const deviceIdentifier = `${service.instance.name}${service.instance.username}${service.accessoryInformation.Manufacturer}${friendlyName}${service.uuid.slice(0, 8)}`;
145
+ return clientNode.device === deviceIdentifier;
146
+ });
143
147
 
144
148
  if (matchedDevice) {
145
149
  clientNode.hbDevice = matchedDevice;
@@ -147,7 +151,7 @@ class HBConfigNode {
147
151
  clientNode.emit('hbReady', matchedDevice);
148
152
  debug('_Registered: %s type: %s', clientNode.type, matchedDevice.type, matchedDevice.serviceName);
149
153
  } else {
150
- this.error(`ERROR: Device registration failed '${clientNode.fullName}'`);
154
+ this.error(`ERROR: Device registration failed '${clientNode.fullName}' - '${clientNode.device}'`);
151
155
  }
152
156
  };
153
157
 
@@ -1,8 +1,8 @@
1
-
2
- const { HapClient } = require('@homebridge/hap-client');
1
+ // File: src/hbConfigNode.test.js
3
2
  const HBConfigNode = require('./hbConfigNode'); // Update the path as necessary
4
3
  const fs = require('fs');
5
4
  const path = require('path');
5
+ const process = require('process');
6
6
 
7
7
  jest.mock('@homebridge/hap-client', () => {
8
8
  return {
@@ -16,11 +16,10 @@ jest.mock('@homebridge/hap-client', () => {
16
16
  };
17
17
  });
18
18
 
19
- describe.skip('Issue 142', () => {
19
+ describe('Issue 142', () => {
20
20
  let mockConfig;
21
21
  let RED;
22
22
  let node;
23
- let mockHapClient;
24
23
 
25
24
  beforeEach(() => {
26
25
  mockConfig = {
@@ -57,7 +56,6 @@ describe('HBConfigNode', () => {
57
56
  let mockConfig;
58
57
  let RED;
59
58
  let node;
60
- let mockHapClient;
61
59
 
62
60
  beforeEach(() => {
63
61
  mockConfig = {
@@ -90,11 +88,10 @@ describe('HBConfigNode', () => {
90
88
  });
91
89
  });
92
90
 
93
- describe.only('from files', () => {
91
+ describe('from files', () => {
94
92
  let mockConfig;
95
93
  let RED;
96
94
  let node;
97
- let mockHapClient;
98
95
 
99
96
  beforeEach(() => {
100
97
  mockConfig = {
@@ -114,10 +111,10 @@ describe.only('from files', () => {
114
111
  node.log = console.log;
115
112
  });
116
113
 
117
- test('Retrieve devices, and compare with current (v2)', async () => {
118
- console.log('Reading Homebridge endpoints from file', process.cwd());
114
+ test.skip('Retrieve devices, and compare with current (v2)', async () => {
115
+ // console.log('Reading Homebridge endpoints from file', process.cwd());
119
116
  var storagePath = path.join(process.cwd(), 'test/homebridge-automation-endpoints.json');
120
- console.log(`Reading Homebridge endpoints from ${storagePath}`);
117
+ // console.log(`Reading Homebridge endpoints from ${storagePath}`);
121
118
  const fileHbDevices = JSON.parse(fs.readFileSync(storagePath, 'utf8'));
122
119
 
123
120
  node.hapClient.getAllServices.mockResolvedValue(fileHbDevices);
@@ -126,7 +123,7 @@ describe.only('from files', () => {
126
123
  const result = node.toList({ perms: 'ev' });
127
124
 
128
125
  storagePath = path.join(process.cwd(), 'test/homebridge-automation-hbDevices-v2.json');
129
- console.log(`Reading Homebridge results from ${storagePath}`);
126
+ // console.log(`Reading Homebridge results from ${storagePath}`);
130
127
  const fileResult = JSON.parse(fs.readFileSync(storagePath, 'utf8'));
131
128
  expect(result.length).toBe(107);
132
129
  expect(result).toEqual(fileResult);
@@ -138,9 +135,9 @@ describe.only('from files', () => {
138
135
  });
139
136
 
140
137
  test('Retrieve devices, and compare with future (v3)', async () => {
141
- console.log('Reading Homebridge endpoints from file', process.cwd());
138
+ // console.log('Reading Homebridge endpoints from file', process.cwd());
142
139
  var storagePath = path.join(process.cwd(), 'test/homebridge-automation-endpoints.json');
143
- console.log(`Reading Homebridge endpoints from ${storagePath}`);
140
+ // console.log(`Reading Homebridge endpoints from ${storagePath}`);
144
141
  const fileHbDevices = JSON.parse(fs.readFileSync(storagePath, 'utf8'));
145
142
 
146
143
  node.hapClient.getAllServices.mockResolvedValue(fileHbDevices);
@@ -149,7 +146,7 @@ describe.only('from files', () => {
149
146
  const result = node.toList({ perms: 'ev' });
150
147
 
151
148
  storagePath = path.join(process.cwd(), 'test/homebridge-automation-hbDevices-v3.json');
152
- console.log(`Reading Homebridge results from ${storagePath}`);
149
+ // console.log(`Reading Homebridge results from ${storagePath}`);
153
150
  const fileResult = JSON.parse(fs.readFileSync(storagePath, 'utf8'));
154
151
  expect(result.length).toBe(107);
155
152
  expect(result).toEqual(fileResult);
@@ -17,12 +17,12 @@ class HbControlNode extends hbBaseNode {
17
17
  const isCamera = this.hbDevice.type === 'CameraRTPStreamManagement';
18
18
  const payloadType = typeof message.payload;
19
19
 
20
- // Validate payload
20
+ // Is the payload a valid JSON object?
21
+
21
22
  if (!isCamera && payloadType !== 'object') {
22
23
  const validNames = Object.keys(this.hbDevice.values)
23
24
  .filter(key => key !== 'ConfiguredName')
24
25
  .join(', ');
25
-
26
26
  this.error(
27
27
  `Invalid payload. Expected JSON object, e.g., {"On":false, "Brightness":0}. Valid values: ${validNames}`
28
28
  );
@@ -30,6 +30,16 @@ class HbControlNode extends hbBaseNode {
30
30
  return;
31
31
  }
32
32
 
33
+ // Validate payload
34
+ let keysToKeep = Object.keys(this.hbDevice.values);
35
+
36
+ Object.keys(message.payload).forEach(key => {
37
+ if (!keysToKeep.includes(key)) {
38
+ this.handleWarning(`Unhandled Characteristic '${key}'`);
39
+ delete message.payload[key];
40
+ }
41
+ });
42
+
33
43
  const results = [];
34
44
  let fill = 'green';
35
45
 
@@ -50,14 +60,15 @@ class HbControlNode extends hbBaseNode {
50
60
  } else {
51
61
  // Handle other characteristics
52
62
  try {
63
+ // debug('Setting value for', message.payload);
53
64
  const result = await this.hbDevice.setCharacteristicsByTypes(filterIfOff(message.payload));
65
+ // debug('Result', result.values);
54
66
  results.push(result.values);
55
67
  } catch (error) {
56
- console.log(error)
57
- this.error(`Failed to set value for "${JSON.stringify(message.payload)}": ${error.message}`);
58
- results.push({ 'Error': `Error: ${error.message}` });
68
+ this.error(`${error.message} for ${JSON.stringify(message.payload)}`);
69
+ results.push({ Error: `${error.message} for ${JSON.stringify(message.payload)}` });
59
70
  fill = 'red';
60
- this.hbConfigNode.disconnectClientNodes(this.hbDevice.instance);
71
+ // this.hbConfigNode.disconnectClientNodes(this.hbDevice.instance);
61
72
  }
62
73
 
63
74
  /*