node-red-contrib-homebridge-automation 0.1.12-beta.0 → 0.1.12-beta.10

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/.eslintrc.js ADDED
@@ -0,0 +1,24 @@
1
+ module.exports = {
2
+ env: {
3
+ es6: true,
4
+ node: true,
5
+ mocha: true,
6
+ },
7
+ extends: ['eslint:recommended', 'plugin:mocha/recommended'],
8
+ globals: {
9
+ Atomics: 'readonly',
10
+ SharedArrayBuffer: 'readonly',
11
+ },
12
+ parserOptions: {
13
+ ecmaVersion: 2018,
14
+ sourceType: 'module',
15
+ },
16
+ plugins: ['mocha'],
17
+ rules: {
18
+ indent: ['error', 2, { SwitchCase: 1 }],
19
+ 'linebreak-style': ['error', 'unix'],
20
+ quotes: ['error', 'single'],
21
+ semi: ['error', 'always'],
22
+ 'no-console': 'off',
23
+ },
24
+ };
package/.nycrc.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "reporter": [
3
+ "lcov",
4
+ "text-summary"
5
+ ],
6
+ "lines": 100,
7
+ "statements": 100,
8
+ "functions": 100,
9
+ "branches": 100,
10
+ "check-coverage": true
11
+ }
package/.prettierrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "printWidth": 120,
3
+ "tabWidth": 2,
4
+ "trailingComma": "all",
5
+ "singleQuote": true
6
+ }
package/README.md CHANGED
@@ -60,7 +60,7 @@ The above Node-RED Flow, turns on my 'Outside Office' light when the powder room
60
60
  * [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)
61
61
 
62
62
  <!-- Created by https://github.com/ekalinin/github-markdown-toc -->
63
- <!-- Added by: runner, at: Fri Jul 5 02:40:07 UTC 2024 -->
63
+ <!-- Added by: runner, at: Mon Jul 8 00:34:52 UTC 2024 -->
64
64
 
65
65
  <!--te-->
66
66
 
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "node-red-contrib-homebridge-automation",
3
- "version": "0.1.12-beta.0",
3
+ "version": "0.1.12-beta.10",
4
4
  "description": "NodeRED Automation for HomeBridge",
5
5
  "main": "src/HAP-NodeRed.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1",
8
7
  "api": "documentation build HAP-NodeRed.js -f md --config docs/documentation.yml > docs/API.md",
9
- "document": "./gh-md-toc --insert README.md; rm README.md.orig.* README.md.toc.*",
10
- "watch": "nodemon"
8
+ "document": "./gh-md-toc --insert --no-backup --hide-footer README.md",
9
+ "watch": "nodemon",
10
+ "coverage": "nyc npm t",
11
+ "format": "prettier --write {.,test}/*.js *.html *.md",
12
+ "lint": "eslint {.,test}/*.js",
13
+ "test": "mocha \"test/**/*_spec.js\""
11
14
  },
12
15
  "keywords": [
13
16
  "node-red",
@@ -23,8 +26,22 @@
23
26
  "url": "git+https://github.com/NorthernMan54/node-red-contrib-homebridge-automation.git"
24
27
  },
25
28
  "devDependencies": {
26
- "documentation": "^14.0.3",
29
+ "@types/node-red": "^0.20.7",
30
+ "@types/jest": "^29.5.12",
31
+ "documentation": "14.0.3",
32
+ "eslint": "^8.57.0",
33
+ "eslint-config-airbnb-typescript": "^18.0.0",
34
+ "eslint-plugin-import": "^2.29.1",
35
+ "eslint-plugin-import-newlines": "^1.4.0",
36
+ "eslint-plugin-jest": "^28.6.0",
37
+ "eslint-plugin-sort-exports": "^0.9.1",
38
+ "mocha": "^10.6.0",
39
+ "node-red": "^4.0.2",
40
+ "node-red-node-test-helper": "^0.3.4",
27
41
  "nodemon": "^3.1.4",
42
+ "nyc": "^17.0.0",
43
+ "prettier": "^3.3.2",
44
+ "rimraf": "^5.0.7",
28
45
  "semver": "^7.6.2"
29
46
  },
30
47
  "dependencies": {
@@ -44,7 +61,7 @@
44
61
  ],
45
62
  "ext": "js,html",
46
63
  "ignore": [],
47
- "exec": "DEBUG=hapNodeRed ~/npm/bin/node-red",
64
+ "exec": "DEBUG=hapNodeRed,hapNodeJSClient ~/npm/bin/node-red",
48
65
  "signal": "SIGTERM",
49
66
  "env": {
50
67
  "NODE_OPTIONS": "--trace-warnings"
@@ -180,7 +180,7 @@ module.exports = function (RED) {
180
180
  node.command = function (event) {
181
181
  // False messages can be received from accessories with multiple services
182
182
  // if (Object.keys(_convertHBcharactericToNode(event, node)).length > 0) {
183
- debug("hbEvent", node.name, event);
183
+ // debug("hbEvent", node.name, event);
184
184
  if (event.status === true && event.value !== undefined) {
185
185
  node.state = Object.assign(node.state, _convertHBcharactericToNode([event], node));
186
186
  var msg = {
@@ -194,7 +194,7 @@ module.exports = function (RED) {
194
194
  _rawEvent: event
195
195
  };
196
196
  node.status({
197
- text: JSON.stringify(msg.payload),
197
+ text: JSON.stringify(msg.payload).slice(0, 30) + '...',
198
198
  shape: 'dot',
199
199
  fill: 'green'
200
200
  });
@@ -233,7 +233,7 @@ module.exports = function (RED) {
233
233
  }, function (err, message) {
234
234
  if (!err) {
235
235
  node.state = _convertHBcharactericToNode(message.characteristics, node);
236
- debug("hbEvent received: %s = %s", node.fullName, JSON.stringify(message.characteristics), node.state);
236
+ debug("hbEvent received: %s = %s", node.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
237
237
  if (node.sendInitialState) {
238
238
  var msg = {
239
239
  name: node.name,
@@ -246,7 +246,7 @@ module.exports = function (RED) {
246
246
  _rawMessage: message,
247
247
  };
248
248
  node.status({
249
- text: JSON.stringify(msg.payload),
249
+ text: JSON.stringify(msg.payload).slice(0, 30) + '...',
250
250
  shape: 'dot',
251
251
  fill: 'green'
252
252
  });
@@ -366,7 +366,7 @@ module.exports = function (RED) {
366
366
  }, newMsg));
367
367
  debug("hbResume.input: %s output", node.fullName, JSON.stringify(newMsg));
368
368
  node.status({
369
- text: JSON.stringify(newMsg.payload),
369
+ text: JSON.stringify(newMsg.payload).slice(0, 30) + '...',
370
370
  shape: 'dot',
371
371
  fill: 'green'
372
372
  });
@@ -380,9 +380,9 @@ module.exports = function (RED) {
380
380
  node.lastPayload = JSON.parse(JSON.stringify(msg.payload)); // store value not reference
381
381
  }
382
382
  } else {
383
- node.error("Homebridge not initialized", this.msg);
383
+ node.error("Homebridge not initialized - 1", this.msg);
384
384
  node.status({
385
- text: 'Homebridge not initialized',
385
+ text: 'Homebridge not initialized -1',
386
386
  shape: 'ring',
387
387
  fill: 'red'
388
388
  });
@@ -448,7 +448,7 @@ module.exports = function (RED) {
448
448
  }, function (err, message) {
449
449
  if (!err) {
450
450
  node.state = _convertHBcharactericToNode(message.characteristics, node);
451
- debug("hbResume received: %s = %s", node.fullName, JSON.stringify(message.characteristics), node.state);
451
+ debug("hbResume received: %s = %s", node.fullName, JSON.stringify(message.characteristics).slice(0, 80) + '...');
452
452
  } else {
453
453
  node.error(err);
454
454
  }
@@ -505,37 +505,23 @@ module.exports = function (RED) {
505
505
 
506
506
  node.on('input', function (msg) {
507
507
  this.msg = msg;
508
- var device;
509
-
510
- try {
511
- if (msg.name) {
512
- device = hbDevices.findDeviceByName(msg.name, {
513
- perms: 'pw'
514
- });
515
- } else {
516
- device = hbDevices.findDevice(node.device, {
517
- perms: 'pw'
518
- });
519
- }
520
- } catch (err) {
521
- var error = "Homebridge not initialized";
522
- node.status({
523
- text: error,
524
- shape: 'ring',
525
- fill: 'red'
526
- });
527
- node.error(error, this.msg);
528
- return;
529
- }
530
-
531
- _control.call(this, node, device, msg.payload, function (err, data) {
532
- // debug('hbControl [%s] - [%s]', err, data); // Images produce alot of noise
533
- if (!err && data && (device.type == '00000110' || device.type == '00000111')) {
534
- // debug('hbControl', err, data); // Images produce alot of noise
535
- const msg = {};
508
+ _control.call(this, node, msg.payload, function (err, data) {
509
+ // debug('hbControl complete [%s] - [%s]', node, node.hbDevice); // Images produce alot of noise
510
+ if (!err && data && (node.deviceType == '00000110' || node.deviceType == '00000111')) {
511
+ const msg = {
512
+ name: node.name,
513
+ payload: node.state,
514
+ _device: node.device,
515
+ _confId: node.confId
516
+ };
517
+ if (node.hbDevice) {
518
+ msg.Homebridge = node.hbDevice.homebridge;
519
+ msg.Manufacturer = node.hbDevice.manufacturer;
520
+ msg.Service = node.hbDevice.deviceType;
521
+ }
536
522
  msg.payload = data;
537
523
  node.send(msg);
538
- } else {
524
+ } else if (err) {
539
525
  node.error(err, this.msg);
540
526
  }
541
527
  }.bind(this));
@@ -547,13 +533,18 @@ module.exports = function (RED) {
547
533
  });
548
534
 
549
535
  node.conf.register(node, function () {
550
- // debug("hbControl.register:", node.fullName, node);
551
- switch (node.service) {
552
- case "Camera Control": // Camera Control
553
- debug("hbControl camera");
554
- break;
555
- default:
556
- debug("node.conf.register", node.service);
536
+ debug("hbControl.register:", node.fullName);
537
+ this.hbDevice = hbDevices.findDevice(node.device);
538
+ // console.log('hbControl Register', this.hbDevice)
539
+ if (this.hbDevice) {
540
+ node.hbDevice = this.hbDevice;
541
+ node.deviceType = this.hbDevice.type;
542
+ // Register for events
543
+ node.listener = node.command;
544
+ // node.eventName = this.hbDevice.host + this.hbDevice.port + this.hbDevice.aid;
545
+ } else {
546
+ node.error("437:Can't find device " + node.device, null);
547
+ // this.error("Missing device " + node.device);
557
548
  }
558
549
  });
559
550
  }
@@ -599,7 +590,7 @@ module.exports = function (RED) {
599
590
  perms: 'pr'
600
591
  }, function (err, message) {
601
592
  if (!err) {
602
- debug("hbStatus received: %s = %s", JSON.stringify(node.fullName), JSON.stringify(message), JSON.stringify(node.hbDevice));
593
+ debug("hbStatus received: %s = %s", JSON.stringify(node.fullName), JSON.stringify(message).slice(0, 80) + '...', JSON.stringify(node.hbDevice));
603
594
  this.msg.name = node.name;
604
595
  this.msg._rawMessage = message;
605
596
  this.msg.payload = _convertHBcharactericToNode(message.characteristics, node);
@@ -612,7 +603,7 @@ module.exports = function (RED) {
612
603
  this.msg._confId = node.confId;
613
604
  }
614
605
  node.status({
615
- text: JSON.stringify(this.msg.payload),
606
+ text: JSON.stringify(this.msg.payload).slice(0, 30) + '...',
616
607
  shape: 'dot',
617
608
  fill: 'green'
618
609
  });
@@ -656,7 +647,7 @@ module.exports = function (RED) {
656
647
  });
657
648
 
658
649
  RED.httpAdmin.get('/hap-device/evDevices/:id', RED.auth.needsPermission('hb-event.read'), function (req, res) {
659
- if (evDevices) {
650
+ if (evDevices && hbDevices) {
660
651
  debug("evDevices", hbDevices.toList({
661
652
  perms: 'ev'
662
653
  }).length);
@@ -815,6 +806,9 @@ module.exports = function (RED) {
815
806
  // debug("_status", new Error(), hbDevices);
816
807
  var error;
817
808
  try {
809
+ if (!hbDevices) {
810
+ throw new Error('hbDevices not initialized');
811
+ }
818
812
  var device = hbDevices.findDevice(node.device, perms);
819
813
  if (device) {
820
814
  // debug("device.type", device.type);
@@ -828,7 +822,7 @@ module.exports = function (RED) {
828
822
  };
829
823
  debug("_status Control %s -> %s", device.id, JSON.stringify(message));
830
824
  homebridge.HAPresourceByDeviceID(device.id, JSON.stringify(message), function (err, status) {
831
- debug("status", err);
825
+ // debug("status", err);
832
826
  if (!err) {
833
827
  debug("_status Controlled %s:%s ->", device.host, device.port);
834
828
  node.status({
@@ -894,8 +888,8 @@ module.exports = function (RED) {
894
888
  callback(error);
895
889
  } // end of device if
896
890
  } catch (err) {
897
- debug('_status', err);
898
- error = "Homebridge not initialized";
891
+ // debug('_status', err);
892
+ error = "Homebridge not initialized -2";
899
893
  node.status({
900
894
  text: error,
901
895
  shape: 'ring',
@@ -915,25 +909,33 @@ module.exports = function (RED) {
915
909
  * @return {type} description
916
910
  */
917
911
 
918
- function _control(node, device, payload, callback) {
912
+ function _control(node, payload, callback) {
919
913
  try {
914
+ if (!hbDevices) {
915
+ throw new Error('hbDevices not initialized');
916
+ }
917
+ var device = hbDevices.findDevice(node.device, {
918
+ perms: 'pw'
919
+ });
920
920
  if (device) {
921
921
  var message;
922
+ // console.log('device.type', device.type)
922
923
  switch (device.type) {
923
924
  case "00000110": // Camera RTPStream Management
924
925
  case "00000111": // Camera Control
925
926
  message = {
926
927
  "resource-type": "image",
927
928
  "image-width": 1920,
928
- "image-height": 1080
929
+ "image-height": 1080,
930
+ "aid": node.hbDevice.aid
929
931
  };
930
- debug("Control %s ->", device.id, JSON.stringify(message));
932
+ debug("Control %s ->", device.id, node.fullName, JSON.stringify(message));
931
933
  homebridge.HAPresourceByDeviceID(device.id, JSON.stringify(message), function (err, status) {
932
934
  if (!err) {
933
- debug("Controlled %s ->", device.id, JSON.stringify(payload));
934
- debug("Payload %s ->", device.id, status);
935
+ // debug("Controlled %s ->", device.id, JSON.stringify(payload));
936
+ // debug("Payload %s ->", device.id, status);
935
937
  node.status({
936
- text: JSON.stringify(payload),
938
+ text: JSON.stringify(payload).slice(0, 30) + '...',
937
939
  shape: 'dot',
938
940
  fill: 'green'
939
941
  });
@@ -963,7 +965,7 @@ module.exports = function (RED) {
963
965
  if (!err && status && status.characteristics[0].status === 0) {
964
966
  debug("Controlled %s ->", device.id, JSON.stringify(status));
965
967
  node.status({
966
- text: JSON.stringify(payload),
968
+ text: JSON.stringify(payload).slice(0, 30) + '...',
967
969
  shape: 'dot',
968
970
  fill: 'green'
969
971
  });
@@ -975,7 +977,7 @@ module.exports = function (RED) {
975
977
  } else if (!err) {
976
978
  debug("Controlled %s ->", device.id, payload);
977
979
  node.status({
978
- text: JSON.stringify(payload),
980
+ text: JSON.stringify(payload).slice(0, 30) + '...',
979
981
  shape: 'dot',
980
982
  fill: 'green'
981
983
  });
@@ -1028,7 +1030,7 @@ module.exports = function (RED) {
1028
1030
  callback(error);
1029
1031
  }
1030
1032
  } catch (err) {
1031
- var error = "Homebridge not initialized";
1033
+ var error = "Homebridge not initialized - 3 "+ err;
1032
1034
  node.status({
1033
1035
  text: error,
1034
1036
  shape: 'ring',
@@ -17,14 +17,21 @@ function Accessory(devices, context) {
17
17
  this.homebridge = context.homebridge;
18
18
  this.id = context.id;
19
19
  this.services = [];
20
- devices.services.forEach(function(element) {
20
+ devices.services.forEach(function (element) {
21
21
  // debug("Service", element);
22
22
  switch (element.type.substring(0, 8)) {
23
23
  case "0000003E": // Accessory Information
24
24
  this.info = information(element.characteristics);
25
25
  break;
26
- // case "00000110": // Camera RTPStream Management generates duplicates
27
- // break;
26
+ case "00000110": // Camera RTPStream Management generates duplicates
27
+ var service = new Service(element, this);
28
+ // console.log('services', this.services);
29
+ if (this.services.some(e => e.type === '00000110')) {
30
+
31
+ } else {
32
+ this.services.push(service);
33
+ }
34
+ break;
28
35
  case "000000D9": // Input Source from webosTV has a dummy input source
29
36
  var service = new Service(element, this);
30
37
  if (service.name !== "dummy") {
@@ -39,7 +46,7 @@ function Accessory(devices, context) {
39
46
  // debug("Info", this.info);
40
47
  }
41
48
 
42
- Accessory.prototype.toList = function(context) {
49
+ Accessory.prototype.toList = function (context) {
43
50
  var list = [];
44
51
  // debug("toList", context);
45
52
  context.aid = this.aid;
@@ -109,7 +116,7 @@ Accessory.prototype.toList = function(context) {
109
116
 
110
117
  function information(characteristics) {
111
118
  var result = {};
112
- characteristics.forEach(function(characteristic) {
119
+ characteristics.forEach(function (characteristic) {
113
120
  if (characteristic.description) {
114
121
  var key = characteristic.description.replace(/ /g, '').replace(/\./g, '_');
115
122
  result[key] = characteristic.value;
package/test/flows.js ADDED
@@ -0,0 +1,42 @@
1
+ const Flows = {
2
+ getDefault: function (options) {
3
+ let defaultFlow = [
4
+ {
5
+ id: 'status1',
6
+ type: 'hb-status',
7
+ name: 'StatusNode1',
8
+ Homebridge: 'homebridge',
9
+ Manufacturer: 'TestModule',
10
+ Service: 'Switch',
11
+ device: 'homebridgeAA:BB:CC:DD:EE:FF0StatusNode100000049',
12
+ conf: 'conf1',
13
+ wires: [['n2']],
14
+ },
15
+ {
16
+ id: 'control1',
17
+ type: 'hb-control',
18
+ name: 'ControlNode1',
19
+ Homebridge: 'homebridge',
20
+ Manufacturer: 'TestModule',
21
+ Service: 'Outlet',
22
+ device: 'homebridgeAA:BB:CC:DD:EE:FF0ControlNode1h00000047',
23
+ conf: 'conf1',
24
+ outputs: 0,
25
+ wires: [],
26
+ },
27
+ {
28
+ id: 'conf1',
29
+ type: 'hb-conf',
30
+ username: '031-45-154',
31
+ macAddress: 'AA:BB:CC:DD:EE:FF',
32
+ },
33
+ { id: 'n2', type: 'helper' },
34
+ { id: 'n3', type: 'helper' },
35
+ ];
36
+
37
+ defaultFlow[0] = Object.assign(defaultFlow[0], options);
38
+ return defaultFlow;
39
+ },
40
+ };
41
+
42
+ module.exports = Flows;
@@ -0,0 +1,40 @@
1
+ // var should = require('should');
2
+ var os = require('os');
3
+ var path = require('path');
4
+ var helper = require('node-red-node-test-helper');
5
+ helper.init(require.resolve('node-red'), { userDir: os.tmpdir() });
6
+
7
+ var flows = require('./flows');
8
+ var hapNode = require('../src/HAP-NodeRed.js');
9
+
10
+ describe('HAP node', function () {
11
+ before(function (done) {
12
+ helper.startServer(done);
13
+ });
14
+
15
+ after(function (done) {
16
+ helper.stopServer(done);
17
+ });
18
+
19
+ afterEach(function () {
20
+ helper.unload();
21
+ });
22
+
23
+ it('should be loaded', function (done) {
24
+ var flow = flows.getDefault();
25
+
26
+ helper.load(hapNode, flow, function () {
27
+ var conf1 = helper.getNode('conf1');
28
+ conf1.should.have.property('macAddress', 'AA:BB:CC:DD:EE:FF');
29
+ var stat1 = helper.getNode('status1');
30
+ stat1.should.have.property('name', 'StatusNode1');
31
+ var ctl1 = helper.getNode('control1');
32
+ ctl1.should.have.property('name', 'ControlNode1');
33
+ var logEvents = helper.log().args.filter(function (evt) {
34
+ return evt[0].type == 'hb-conf' || evt[0].type == 'hb-status';
35
+ });
36
+ logEvents.should.have.length(0);
37
+ done();
38
+ });
39
+ });
40
+ });