homebridge-denon-tv 4.2.6 → 4.3.0-beta.2

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/CHANGELOG.md CHANGED
@@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## The properties *inputs.displayType* and *surrounds.displayType* are removed.
9
9
  ## Note - after update to 3.15.x need remove the accessory frome Home app and add it again.
10
10
 
11
+ ## [4.3.0] - (29.03.2023)
12
+ ## Note - after update to 4.1.x buttons need to be configure again using config UI.
13
+ ## Changes
14
+ - added RESTFul server
15
+ - code refactor and cleanup
16
+ - config.schema updated
17
+ - fixed some minor issues
18
+
11
19
  ## [4.1.0] - (02.03.2023)
12
20
  ## Note - after update to 4.1.x buttons need to be configure again using config UI.
13
21
  ## Changes
package/README.md CHANGED
@@ -35,8 +35,15 @@ Tested with Denon AVR-X6300H, Denon AVR-X2700H, Denon AVR-3311CI, Marantz SR8012
35
35
  * Digital Input Modes can be controlled by creating separate tile in the buttons section.
36
36
  * Siri can be used for all functions, some times need create legacy buttons/switches/sensors.
37
37
  * Automations can be used for all functions, some times need create legacy buttons/switches/sensors.
38
- * MQTT publisch topic *Info*, *State* *Picture* and *Surround* as payload JSON data.
39
38
  * This plugin is based upon the official documentation: [Denon Control Protocol 2020](https://github.com/grzegorz914/homebridge-denon-tv/blob/main/src/Denon%20Control%20Protocol.xlsx)
39
+ * RESTful server:
40
+ * Request: `http//homebridge_ip_address:port/path`.
41
+ * Path: `info`, `state`, `picture`, `surround`.
42
+ * Respone as JSON data.
43
+ * MQTT client:
44
+ * Topic: `Info`, `State`, `Picture`, `Surround`.
45
+ * Publish as JSON data.
46
+
40
47
 
41
48
  <p align="left">
42
49
  <a href="https://github.com/grzegorz914/homebridge-denon-tv"><img src="https://raw.githubusercontent.com/grzegorz914/homebridge-denon-tv/main/graphics/homekit.png" width="382"></a>
@@ -90,6 +97,9 @@ Tested with Denon AVR-X6300H, Denon AVR-X2700H, Denon AVR-3311CI, Marantz SR8012
90
97
  | `volumeControl` | Here choice what a additional volume control mode You want to use (`-1 - None/Disabled`, `0 - Lightbulb`, `1 - Fan`). |
91
98
  | `infoButtonCommand` | Here select the function of `I` button in RC app. |
92
99
  | `refreshInterval` | Here set the data refresh interval. |
100
+ | `enableRestFul` | If enabled, RESTful server will start automatically and respond to any path request. |
101
+ | `restFulPort` | Here set the listening `Port` for RESTful server, every zone need other port. |
102
+ | `restFulDebug` | If enabled, deep log will be present in homebridge console for RESTFul server. |
93
103
  | `enableMqtt` | If enabled, MQTT Broker will start automatically and publish all awailable PV installation data. |
94
104
  | `mqttDebug` | If enabled, deep log will be present in homebridge console for MQTT. |
95
105
  | `mqttHost` | Here set the *IP Address* or *Hostname* for MQTT Broker. |
@@ -4604,6 +4604,33 @@
4604
4604
  "description": "Here set the background data refresh interval.",
4605
4605
  "required": true
4606
4606
  },
4607
+ "enableRestFul": {
4608
+ "title": "Enable",
4609
+ "type": "boolean",
4610
+ "default": false,
4611
+ "required": false,
4612
+ "description": "This enable RESTful server."
4613
+ },
4614
+ "restFulDebug": {
4615
+ "title": "Debug",
4616
+ "type": "boolean",
4617
+ "default": false,
4618
+ "required": false,
4619
+ "description": "This enable debug mode for RESTFul.",
4620
+ "condition": {
4621
+ "functionBody": "return model.devices[arrayIndices].enableRestFul === true;"
4622
+ }
4623
+ },
4624
+ "restFulPort": {
4625
+ "title": "Port",
4626
+ "type": "integer",
4627
+ "placeholder": 3000,
4628
+ "required": true,
4629
+ "description": "Here set the listening Port for RESTful server.",
4630
+ "condition": {
4631
+ "functionBody": "return model.devices[arrayIndices].enableRestFul === true;"
4632
+ }
4633
+ },
4607
4634
  "enableMqtt": {
4608
4635
  "title": "Enable",
4609
4636
  "type": "boolean",
@@ -4814,6 +4841,18 @@
4814
4841
  "devices[].refreshInterval"
4815
4842
  ]
4816
4843
  },
4844
+ {
4845
+ "key": "devices[]",
4846
+ "type": "section",
4847
+ "title": "RESTful",
4848
+ "expandable": true,
4849
+ "expanded": false,
4850
+ "items": [
4851
+ "devices[].enableRestFul",
4852
+ "devices[].restFulDebug",
4853
+ "devices[].restFulPort"
4854
+ ]
4855
+ },
4817
4856
  {
4818
4857
  "key": "devices[]",
4819
4858
  "type": "section",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Denon TV",
3
3
  "name": "homebridge-denon-tv",
4
- "version": "4.2.6",
4
+ "version": "4.3.0-beta.2",
5
5
  "description": "Homebridge plugin (https://github.com/homebridge/homebridge) to control Denon/Marantz AV Receivers.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -30,7 +30,8 @@
30
30
  "dependencies": {
31
31
  "async-mqtt": "^2.6.3",
32
32
  "axios": "^1.4.0",
33
- "fast-xml-parser": "^4.2.6"
33
+ "fast-xml-parser": "^4.2.6",
34
+ "express": "^4.18.2"
34
35
  },
35
36
  "keywords": [
36
37
  "homebridge",
package/src/denon.js CHANGED
@@ -13,6 +13,7 @@ class DENON extends EventEmitter {
13
13
  const zoneControl = config.zoneControl;
14
14
  const debugLog = config.debugLog;
15
15
  const disableLogConnectError = config.disableLogConnectError;
16
+ const restFulEnabled = config.restFulEnabled;
16
17
  const mqttEnabled = config.mqttEnabled;
17
18
  this.refreshInterval = config.refreshInterval;
18
19
 
@@ -157,8 +158,15 @@ class DENON extends EventEmitter {
157
158
  return;
158
159
  }
159
160
 
161
+
160
162
  this.emit('deviceInfo', devInfo, manufacturer, modelName, serialNumber, firmwareRevision, zones, apiVersion, supportPictureMode, supportFavorites, supportShortcut, supportInputSource, supportQuickSmartSelect);
161
- const mqtt = mqttEnabled ? this.emit('mqtt', 'Info', JSON.stringify(devInfo, null, 2)) : false;
163
+
164
+ //restFul
165
+ const restFul = restFulEnabled ? this.emit('restFul', 'info', devInfo) : false;
166
+
167
+ //mqtt
168
+ const mqtt = mqttEnabled ? this.emit('mqtt', 'Info', devInfo) : false;
169
+
162
170
  await new Promise(resolve => setTimeout(resolve, 2000));
163
171
  this.supportPictureMode = supportPictureMode;
164
172
  this.supportSoundMode = supportSoundMode;
@@ -225,9 +233,16 @@ class DENON extends EventEmitter {
225
233
  this.soundMode = soundMode;
226
234
 
227
235
  this.emit('stateChanged', power, reference, volume, volumeDisplay, mute, pictureMode);
228
- const mqtt1 = mqttEnabled ? this.emit('mqtt', 'State', JSON.stringify(devState, null, 2)) : false;
229
- const mqtt2 = mqttEnabled && checkPictureMode ? this.emit('mqtt', 'Picture', JSON.stringify({ 'Picture Mode': CONSTANS.PictureModesDenonNumber[pictureMode] }, null, 2)) : false;
230
- const mqtt3 = mqttEnabled && checkSoundeMode ? this.emit('mqtt', 'Surround', JSON.stringify({ 'Sound Mode': CONSTANS.SoundModeConversion[soundMode] }, null, 2)) : false;
236
+
237
+ //restFul
238
+ const restFul = restFulEnabled ? this.emit('restFul', 'state', devState) : false;
239
+ const restFul1 = restFulEnabled && checkPictureMode ? this.emit('restFul', 'picture', { 'Picture Mode': CONSTANS.PictureModesDenonNumber[pictureMode] }) : false;
240
+ const restFul2 = restFulEnabled && checkSoundeMode ? this.emit('restFul', 'surround', { 'Sound Mode': CONSTANS.SoundModeConversion[soundMode] }) : false;
241
+
242
+ //mqtt
243
+ const mqtt1 = mqttEnabled ? this.emit('mqtt', 'State', devState) : false;
244
+ const mqtt2 = mqttEnabled && checkPictureMode ? this.emit('mqtt', 'Picture', { 'Picture Mode': CONSTANS.PictureModesDenonNumber[pictureMode] }) : false;
245
+ const mqtt3 = mqttEnabled && checkSoundeMode ? this.emit('mqtt', 'Surround', { 'Sound Mode': CONSTANS.SoundModeConversion[soundMode] }) : false;
231
246
  this.checkState();
232
247
  } catch (error) {
233
248
  const debug = disableLogConnectError ? false : this.emit('error', `State error: ${error}, reconnect in 15s.`);
@@ -2,6 +2,7 @@
2
2
  const fs = require('fs');
3
3
  const fsPromises = fs.promises;
4
4
  const EventEmitter = require('events');
5
+ const RestFul = require('./restful.js');
5
6
  const Mqtt = require('./mqtt.js');
6
7
  const Denon = require('./denon.js');
7
8
  const CONSTANS = require('./constans.json');
@@ -44,6 +45,9 @@ class DenonDevice extends EventEmitter {
44
45
  this.infoButtonCommand = config.infoButtonCommand || 'MNINF';
45
46
  this.volumeControl = config.volumeControl >= 0 ? config.volumeControl : -1;
46
47
  this.refreshInterval = config.refreshInterval || 5;
48
+ this.restFulEnabled = config.enableRestFul || false;
49
+ this.restFulPort = config.restFulPort || 3000;
50
+ this.restFulDebug = config.restFulDebug || false;
47
51
  this.mqttEnabled = config.enableMqtt || false;
48
52
  this.mqttDebug = config.mqttDebug || false;
49
53
  this.mqttHost = config.mqttHost;
@@ -60,6 +64,7 @@ class DenonDevice extends EventEmitter {
60
64
 
61
65
  //setup variables
62
66
  this.startPrepareAccessory = true;
67
+ this.restFulConnected = false;
63
68
  this.mqttConnected = false;
64
69
  this.services = [];
65
70
  this.inputsReference = [];
@@ -89,7 +94,26 @@ class DenonDevice extends EventEmitter {
89
94
  this.inputsNamesFile = `${prefDir}/inputsNames_${this.sZoneName}${this.host.split('.').join('')}`;
90
95
  this.inputsTargetVisibilityFile = `${prefDir}/inputsTargetVisibility_${this.sZoneName}${this.host.split('.').join('')}`;
91
96
 
92
- //mqtt client
97
+ //RESTFul server
98
+ if (this.restFulEnabled) {
99
+ this.restFul = new RestFul({
100
+ port: this.restFulPort,
101
+ debug: this.restFulDebug
102
+ });
103
+
104
+ this.restFul.on('connected', (message) => {
105
+ this.emit('message', `${message}`);
106
+ this.restFulConnected = true;
107
+ })
108
+ .on('error', (error) => {
109
+ this.emit('error', error);
110
+ })
111
+ .on('debug', (debug) => {
112
+ this.emit('debug', debug);
113
+ });
114
+ }
115
+
116
+ //MQTT client
93
117
  if (this.mqttEnabled) {
94
118
  this.mqtt = new Mqtt({
95
119
  host: this.mqttHost,
@@ -122,6 +146,7 @@ class DenonDevice extends EventEmitter {
122
146
  disableLogConnectError: this.disableLogConnectError,
123
147
  zoneControl: this.zoneControl,
124
148
  refreshInterval: this.refreshInterval,
149
+ restFulEnabled: this.restFulEnabled,
125
150
  mqttEnabled: this.mqttEnabled
126
151
  });
127
152
 
@@ -383,6 +408,9 @@ class DenonDevice extends EventEmitter {
383
408
  .on('error', (error) => {
384
409
  this.emit('error', error);
385
410
  })
411
+ .on('restFul', (path, data) => {
412
+ const restFul = this.restFulConnected ? this.restFul.update(path, data) : false;
413
+ })
386
414
  .on('mqtt', (topic, message) => {
387
415
  const mqtt = this.mqttConnected ? this.mqtt.send(topic, message) : false;
388
416
  })
package/src/mqtt.js CHANGED
@@ -41,7 +41,8 @@ class MQTTCLIENT extends EventEmitter {
41
41
 
42
42
  try {
43
43
  const fullTopic = `${this.mqttPrefix}/${topic}`;
44
- await this.mqttClient.publish(fullTopic, message);
44
+ const publishMessage = JSON.stringify(message, null, 2);
45
+ await this.mqttClient.publish(fullTopic, publishMessage);
45
46
  const debug = this.mqttDebug ? this.emit('debug', `MQTT publish: ${fullTopic}: ${message}`) : false;
46
47
  } catch (error) {
47
48
  this.emit('error', `MQTT Publish error: ${error}`);
package/src/restful.js ADDED
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ const express = require('express');
3
+ const EventEmitter = require('events');
4
+
5
+ class RestFul extends EventEmitter {
6
+ constructor(config) {
7
+ super();
8
+ this.restFulPort = config.port;
9
+ this.restFulDebug = config.debug;
10
+
11
+ this.restFulData = {
12
+ info: 'This data is not available in this zone.',
13
+ state: 'This data is not available in this zone.',
14
+ picture: 'This data is not available in this zone.',
15
+ surround: 'This data is not available in this zone.'
16
+ };
17
+
18
+ this.connect();
19
+ };
20
+
21
+ connect() {
22
+ try {
23
+ const restFul = express();
24
+ restFul.get('/info', (req, res) => { res.json(this.restFulData.info) });
25
+ restFul.get('/state', (req, res) => { res.json(this.restFulData.state) });
26
+ restFul.get('/picture', (req, res) => { res.json(this.restFulData.picture) });
27
+ restFul.get('/surround', (req, res) => { res.json(this.restFulData.surround) });
28
+
29
+ restFul.listen(this.restFulPort, () => {
30
+ this.emit('connected', `RESTful started on port: ${this.restFulPort}`)
31
+ });
32
+
33
+ } catch (error) {
34
+ this.emit('error', `RESTful error: ${error}`)
35
+ }
36
+ };
37
+
38
+ update(path, data) {
39
+ switch (path) {
40
+ case 'info':
41
+ this.restFulData.info = data;
42
+ break;
43
+ case 'state':
44
+ this.restFulData.state = data;
45
+ break;
46
+ case 'picture':
47
+ this.restFulData.picture = data;
48
+ break;
49
+ case 'surround':
50
+ this.restFulData.surround = data;
51
+ break;
52
+ default:
53
+ break;
54
+ };
55
+ const emitDebug = this.restFulDebug ? this.emit('debug', `RESTFul update path: ${path}, data: ${data}`) : false;
56
+ };
57
+ };
58
+ module.exports = RestFul;