node-red-contrib-tts-ultimate 1.0.42 → 1.0.45

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
@@ -2,6 +2,20 @@
2
2
 
3
3
  [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
4
4
 
5
+ <p>
6
+ <b>Version 1.0.45</b> April 2022<br/>
7
+ - NEW: Additional players now resumes the queue as well (previously, only the main player was doing so).<br/>
8
+ </p>
9
+ <p>
10
+ <b>Version 1.0.44</b> April 2022<br/>
11
+ - NEW: you can now adjust the additional player's volume, adapting it to the main sonos player volume. This is useful in case you've some recessed speakers, "speaking" too low or some too near speakers, "speaking" too high. You can adapt the volume in the config window or dinamically via msg input.<br/>
12
+ - Updated the README.<br/>
13
+ </p>
14
+ <p>
15
+ <b>Version 1.0.43</b> March 2022<br/>
16
+ - Simplified the configuration by auto discover some IP.<br/>
17
+ - FIX: fixed minor glitches.<br/>
18
+ </p>
5
19
  <p>
6
20
  <b>Version 1.0.42</b> March 2022<br/>
7
21
  - FIX: fix purging option that wasn't working if you set to always purge the cached files at startup.<br/>
package/README.md CHANGED
@@ -112,7 +112,7 @@ For Google TTS Engine, you can choose pitch and speed rate of the voice.
112
112
 
113
113
 
114
114
  **Node-Red IP**<br/>
115
- set IP of your node-red machine. Sonos will connect to this address in order to play TTS. You can also write any value you want, for example 127.0.0.1 in this field (**don't leave this field blank in any case**), if you don't want to use Sonos as player. Please see below, the section **TTS-ULTIMATE NODE**, property **Player**.
115
+ set IP of your node-red machine. Write **AUTODISCOVER** to allow the node to auto discover your IP.
116
116
 
117
117
  **Host Port**<br/>
118
118
  Sonos will connect to this port in order to play TTS. Default 1980. Choose a free port. Do not use 1880 or any other port already in use on your computer.
@@ -169,7 +169,8 @@ Select your Sonos primary player. (It's strongly suggested to set a fixed IP for
169
169
  It's possibile to group players, so your announcement can be played on all selected players. For this to happen, you need to select your primary coordinator player. All other players will be then controlled by this coordinator.
170
170
 
171
171
  **Additional Players** <br/>
172
- Here you can add all additional players that will be grouped toghether to the *Main Sonos Player* coordinator group. You can add a player using the "ADD" button, below the list.
172
+ Here you can add all additional players that will be grouped toghether to the *Main Sonos Player* coordinator group. You can add a player using the "ADD" button, below the list.<br/>
173
+ For each additional player, you can adjust their volume, based on the **Main Sonos Player** volume -+100.
173
174
 
174
175
 
175
176
  ## INPUT MESSAGES TO THE NODE <br/>
@@ -287,7 +288,9 @@ The setting is retained until the node receives another msg.setConfig or until n
287
288
 
288
289
  > **property setPlayerGroupArray**<br/>
289
290
  Sets the array of players beloging to the group, if any.<br/>
290
- If you have only one additional player, you need to ever put it into an array. See below.
291
+ You can also specify the volume variation from the main volume player, to adapt the additional player's perceived volume to the main sonos player volume.<br/>
292
+ For example, if you have a speaker mounted in celiling, having less perceived volume, you can "push" the volume up, to match the whole perceived volume. Just add **#** after the IP and a number from -100 to 100 to subtract or add volume ***compared to the main sonos volume***. For example, if the sonos main player volume is 40, you can push this celing speaker's volume to further 10, so it'll have the real volume of 50. See below, the example.<br/>
293
+ Note: Even if you have only one additional player, you need to put it into an array.
291
294
 
292
295
  ```js
293
296
  // Set main player IP
@@ -300,14 +303,15 @@ return msg;
300
303
  ```
301
304
 
302
305
  ```js
303
- // Set player IP and additional players
306
+ // Set player IP and additional players with their optional adapted volume, relative to the main sonos player volume.
307
+ // You can specify the aditional player's volume adaptation
304
308
  // The setting is retained until the node receives another msg.setConfig or until node-red is restarted.
305
309
  var config= {
306
310
  setMainPlayerIP:"192.168.1.109",
307
311
  setPlayerGroupArray:[
308
- "192.168.1.110",
309
- "192.168.1.111",
310
- "192.168.1.112"
312
+ "192.168.1.110", // This additional player will use the same volume as the main sonos player.
313
+ "192.168.1.111#-10", // This additional player will use the main sonos player's volume, minus 10.
314
+ "192.168.1.112#20" // This additional player will use the main sonos player's volume, plus 20.
311
315
  ]
312
316
  };
313
317
  msg.setConfig = config;
@@ -315,7 +319,7 @@ return msg;
315
319
  ```
316
320
 
317
321
  ```js
318
- // If you have only one additional player
322
+ // If you have only one additional player, without setting their adjusted volume.
319
323
  // The setting is retained until the node receives another msg.setConfig or until node-red is restarted.
320
324
  var config= {
321
325
  setMainPlayerIP:"192.168.1.109",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-tts-ultimate",
3
- "version": "1.0.42",
3
+ "version": "1.0.45",
4
4
  "description": "Transforms the text in speech and hear it using Sonos player or generate an audio file to be used with third parties nodes. Works with voices from Amazon, Google (without credentials as well), Microsoft TTS Azure, or your own voice. You can also only create a TTS file to be read by third party nodes. Update of the popular SonosPollyTTS node.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,4 +1,5 @@
1
1
  <script type="text/javascript">
2
+
2
3
  RED.nodes.registerType("ttsultimate-config", {
3
4
  category: 'config',
4
5
  defaults:
@@ -6,7 +7,7 @@
6
7
  name: { value: "TTS Service" },
7
8
  noderedipaddress:
8
9
  {
9
- value: "",
10
+ value: "AUTODISCOVER",
10
11
  required: false,
11
12
  type: "text"
12
13
  },
@@ -32,17 +33,18 @@
32
33
  },
33
34
  oneditprepare: function () {
34
35
  var node = this;
35
-
36
+
36
37
  // 21/03/2020 Check if the node is the absolute first in the flow. In this case, it has no http server instatiaced
37
- $.getJSON('ttsultimateGetEthAddress', (data) => {
38
- $("#pleaseDeploy").hide();
38
+ // $.getJSON('ttsultimateGetEthAddress', (data) => {
39
+ $("#pleaseDeploy").hide();
39
40
  $("#allGUI").show();
40
- }).fail(function (jqxhr) {
41
- $("#pleaseDeploy").show();
42
- $("#allGUI").hide();
43
- });
41
+ // }).fail(function (jqxhr) {
42
+ // $("#pleaseDeploy").show();
43
+ // $("#allGUI").hide();
44
+ // });
44
45
 
45
- if (typeof node.noderedipaddress === "undefined") {
46
+
47
+ if (node.noderedipaddress === undefined) {
46
48
  // Put the default address of the machine
47
49
  $.getJSON('ttsultimateGetEthAddress', (data) => {
48
50
  $("#node-config-input-noderedipaddress").val(data);
@@ -157,7 +159,6 @@
157
159
  <label for="node-config-input-noderedipaddress"><i class="fa fa-globe"></i> Node-Red IP</label>
158
160
  <input type="text" id="node-config-input-noderedipaddress">
159
161
  </div>
160
- <div class="form-tips" style="margin-top: 8px;background-color:lightgrey;text-align:center">Above option: don't leave this field blank in any case. If you don't use Sonos players, set it to 127.0.0.1. See the README on gitHub.</div>
161
162
  <br/>
162
163
 
163
164
  <div class="form-row">
@@ -19,7 +19,11 @@ module.exports = function (RED) {
19
19
  function TTSConfigNode(config) {
20
20
  RED.nodes.createNode(this, config);
21
21
  var node = this;
22
- node.noderedipaddress = typeof config.noderedipaddress === "undefined" ? "" : config.noderedipaddress;
22
+ node.noderedipaddress = config.noderedipaddress;
23
+ if (node.noderedipaddress === undefined || node.noderedipaddress === "AUTODISCOVER") {
24
+ node.noderedipaddress = GetEthAddress();
25
+ RED.log.info('ttsultimate-config ' + node.id + ': Autodiscover current IP ' + node.noderedipaddress);
26
+ }
23
27
  node.whoIsUsingTheServer = ""; // Client node.id using the server, because only a ttsultimate node can use the serve at once.
24
28
  node.ttsservice = config.ttsservice || "googletranslate";
25
29
  node.TTSRootFolderPath = (config.TTSRootFolderPath === undefined || config.TTSRootFolderPath === "") ? path.join(RED.settings.userDir, "sonospollyttsstorage") : path.join(config.TTSRootFolderPath, "sonospollyttsstorage");
@@ -212,6 +216,9 @@ module.exports = function (RED) {
212
216
  // ######################################################
213
217
  // 21/03/2019 Endpoint for retrieving the default IP
214
218
  RED.httpAdmin.get("/ttsultimateGetEthAddress", RED.auth.needsPermission('TTSConfigNode.read'), function (req, res) {
219
+ res.json(GetEthAddress());
220
+ });
221
+ function GetEthAddress() {
215
222
  var oiFaces = oOS.networkInterfaces();
216
223
  var jListInterfaces = [];
217
224
  try {
@@ -229,13 +236,12 @@ module.exports = function (RED) {
229
236
  })
230
237
  } catch (error) { }
231
238
  if (jListInterfaces.length > 0) {
232
- res.json(jListInterfaces[0].address); // Retunr the first usable IP
239
+ return(jListInterfaces[0].address); // Retunr the first usable IP
233
240
  } else {
234
- res.json("NO ETH INTERFACE FOUND");
241
+ return("NO ETH INTERFACE FOUND");
235
242
  }
236
-
237
- });
238
-
243
+ }
244
+
239
245
  // 20/03/2020 in the middle of coronavirus, get the sonos groups
240
246
  RED.httpAdmin.get("/sonosgetAllGroups", RED.auth.needsPermission('TTSConfigNode.read'), function (req, res) {
241
247
  var jListGroups = [];
@@ -202,7 +202,7 @@
202
202
  // 24/12/2020 Check if the node is the absolute first in the flow. In this case, it has no http server instatiaced
203
203
  // !oNodeServer.hasOwnProperty("noderedipaddress") is when the config node exists, but not deployed
204
204
  // oNodeServer.noderedipaddress === "" is when the config node has been deployed, but not configured
205
- if (oNodeServer === null || !oNodeServer.hasOwnProperty("noderedipaddress")) {
205
+ if (oNodeServer === null || oNodeServer === undefined || !oNodeServer.hasOwnProperty("noderedipaddress")) {
206
206
  $("#pleaseDeploy").show();
207
207
  $("#allGUI").hide();
208
208
  } else if (oNodeServer.hasOwnProperty("noderedipaddress") && oNodeServer.noderedipaddress === "") {
@@ -402,16 +402,32 @@
402
402
  //#region ADDITIONAL PLAYERS
403
403
  // 20/03/2020 ADDITIONAL PLAYERS
404
404
  // ##########################################################
405
- var previousValueType = { value: "prev", label: this._("switch.previous"), hasValue: false };
405
+ //var previousValueType = { value: "prev", label: this._("switch.previous"), hasValue: false };
406
+
407
+ // Add Selectbox with the volume for the additional players
408
+ function addAdditionalPlayerVolumeUI(row, _currentVolume = 0) {
409
+ let oAdjustVolume = $('<select/>', { class: "rowRulePlayerHostAdjustVolume", type: "text", style: "width:200px; margin-left: 5px; text-align: left;" }).appendTo(row);
410
+ for (let index = -100; index < 100; index += 5) {
411
+ let sTesto = "";
412
+ if (index === 0) sTesto = "Same volume as Main Sonos Player";
413
+ if (index < 0) sTesto = "Decrease volume by " + Math.abs(index) ;
414
+ if (index > 0) sTesto = "Increase volume by " + index;
415
+ oAdjustVolume.append($("<option></option>")
416
+ .attr("value", index)
417
+ .text(sTesto)
418
+ )
419
+ }
420
+ oAdjustVolume.val(_currentVolume);
421
+ }
406
422
  function resizeRule(rule) { }
423
+
407
424
  $("#node-input-rule-container").css('min-height', '150px').css('min-width', '450px').editableList({
408
425
  addItem: function (container, i, opt) { // row, index, data
409
- // opt.r is: { topic: rowRuleTopic, devicename: rowRuleDeviceName, dpt:rowRuleDPT, send: rowRuleSend}
410
426
 
411
427
  if (!opt.hasOwnProperty('r')) {
412
428
  opt.r = {};
413
429
  }
414
- var rule = opt.r;
430
+ let rule = opt.r;
415
431
  if (!opt.hasOwnProperty('i')) {
416
432
  opt._i = Math.floor((0x99999 - 0x10000) * Math.random()).toString();
417
433
  }
@@ -422,8 +438,7 @@
422
438
 
423
439
 
424
440
  var row = $('<div class="form-row"/>').appendTo(container);
425
- var oPlayer = $('<label>Discovering.... wait...</label>', { class: "rowRulePlayerHost", type: "text", style: "width:300px; margin-left: 5px; text-align: left;" }).appendTo(row);
426
-
441
+ var oPlayer = $('<label>Discovering.... wait...</label>', { class: "rowRulePlayerHost", type: "text", style: "width:200px; margin-left: 5px; text-align: left;" }).appendTo(row);
427
442
  oPlayer.on("change", function () {
428
443
  resizeRule(container);
429
444
  });
@@ -432,16 +447,18 @@
432
447
  if (typeof data === "string" && data == "ERRORDISCOVERY") { // 10/04/2020 if error in discovery, fallback to manual IP input
433
448
  // Transform the dropdown to a simple input
434
449
  oPlayer.remove();
435
- oPlayer = $('<input/>', { class: "rowRulePlayerHost", type: "text", style: "width:300px; margin-left: 5px; text-align: left;" }).appendTo(row);
450
+ oPlayer = $('<input/>', { class: "rowRulePlayerHost", type: "text", style: "width:200px; margin-left: 5px; text-align: left;" }).appendTo(row);
451
+ addAdditionalPlayerVolumeUI(row, rule.hostVolumeAdjust);
436
452
  } else {
437
453
  oPlayer.remove();
438
- oPlayer = $('<select/>', { class: "rowRulePlayerHost", type: "text", style: "width:300px; margin-left: 5px; text-align: left;" }).appendTo(row);
454
+ oPlayer = $('<select/>', { class: "rowRulePlayerHost", type: "text", style: "width:200px; margin-left: 5px; text-align: left;" }).appendTo(row);
439
455
  data.sort().forEach(oGroup => {
440
456
  oPlayer.append($("<option></option>")
441
457
  .attr("value", oGroup.host)
442
458
  .text(oGroup.name + " (" + oGroup.host + ")")
443
459
  )
444
460
  });
461
+ addAdditionalPlayerVolumeUI(row, rule.hostVolumeAdjust);
445
462
  }
446
463
  oPlayer.val(rule.host);
447
464
  });
@@ -460,7 +477,7 @@
460
477
 
461
478
  // 20/03/2020 For each rule, create a row
462
479
  for (var i = 0; i < this.rules.length; i++) {
463
- var rule = this.rules[i];
480
+ let rule = this.rules[i];
464
481
  $("#node-input-rule-container").editableList('addItem', { r: rule, i: i });
465
482
  }
466
483
  // ##########################################################
@@ -469,14 +486,14 @@
469
486
  node.refreshHailingList();
470
487
 
471
488
  }, oneditsave: function () {
472
- var node = this;
473
-
474
- var rules = $("#node-input-rule-container").editableList('items');
489
+ let node = this;
490
+ let rules = $("#node-input-rule-container").editableList('items');
475
491
  node.rules = [];
476
492
  rules.each(function (i) {
477
- var rule = $(this);
478
- var rowRulePlayerHost = rule.find(".rowRulePlayerHost").val();
479
- node.rules.push({ host: rowRulePlayerHost });
493
+ let rule = $(this);
494
+ let rowRulePlayerHost = rule.find(".rowRulePlayerHost").val();
495
+ let rowRulePlayerHostAdjustVolume = rule.find(".rowRulePlayerHostAdjustVolume").val();
496
+ node.rules.push({ host: rowRulePlayerHost, hostVolumeAdjust: rowRulePlayerHostAdjustVolume });
480
497
  });
481
498
  this.propertyType = $("#node-input-property").typedInput('type');
482
499
  },
@@ -95,12 +95,12 @@ module.exports = function (RED) {
95
95
 
96
96
  //#region ASYNC DECLARATIONS
97
97
  // 30/12/2020 we are at the end of this crazy 2020
98
- function getMusicQueue() {
98
+ function getMusicQueue(_oPlayer = node.SonosClient) {
99
99
  return new Promise(function (resolve, reject) {
100
100
  var oRet = null;
101
- node.SonosClient.currentTrack().then(track => {
101
+ _oPlayer.currentTrack().then(track => {
102
102
  oRet = track;// .queuePosition || 1; // Get the current track in the queue.
103
- node.SonosClient.getCurrentState().then(state => {
103
+ _oPlayer.getCurrentState().then(state => {
104
104
  // A music queue is playing and no TTS is speaking?
105
105
  oRet.state = state;
106
106
  resolve(oRet);
@@ -115,11 +115,12 @@ module.exports = function (RED) {
115
115
  });
116
116
  };
117
117
 
118
+
118
119
  let iWaitAfterSync = 500;
119
120
  // 24/08/2021 Sync wrapper
120
- function PLAYSync(_toPlay) {
121
+ function PLAYSync(_toPlay, _oPlayer = node.SonosClient) {
121
122
  return new Promise((resolve, reject) => {
122
- node.SonosClient.play(_toPlay).then(result => {
123
+ _oPlayer.play(_toPlay).then(result => {
123
124
  if (iWaitAfterSync > 2000) console.log("PLAYSYNC")
124
125
  let t = setTimeout(() => {
125
126
  resolve(true);
@@ -132,9 +133,9 @@ module.exports = function (RED) {
132
133
  }
133
134
 
134
135
  // 24/08/2021 Sync wrapper
135
- function SEEKSync(_Position) {
136
+ function SEEKSync(_Position, _oPlayer = node.SonosClient) {
136
137
  return new Promise((resolve, reject) => {
137
- node.SonosClient.seek(_Position).then(result => {
138
+ _oPlayer.seek(_Position).then(result => {
138
139
  if (iWaitAfterSync > 2000) console.log("SEEKSync", _Position)
139
140
  let t = setTimeout(() => {
140
141
  resolve(true);
@@ -147,9 +148,9 @@ module.exports = function (RED) {
147
148
  }
148
149
 
149
150
  // 24/08/2021 Sync wrapper
150
- function SELECTQUEUESync() {
151
+ function SELECTQUEUESync(_oPlayer = node.SonosClient) {
151
152
  return new Promise((resolve, reject) => {
152
- node.SonosClient.selectQueue().then(result => {
153
+ _oPlayer.selectQueue().then(result => {
153
154
  if (iWaitAfterSync > 2000) console.log("SELECTQUEUESync")
154
155
  try {
155
156
  STOPSync(); // The SetQueue automatically starts playing, so i need to stop it now!
@@ -166,10 +167,10 @@ module.exports = function (RED) {
166
167
  }
167
168
 
168
169
  // 24/08/2021 Sync wrapper
169
- function SELECTTRACKSync(_queuePositiom) {
170
+ function SELECTTRACKSync(_queuePosition, _oPlayer = node.SonosClient) {
170
171
  return new Promise((resolve, reject) => {
171
- node.SonosClient.selectTrack(_queuePositiom).then(result => {
172
- if (iWaitAfterSync > 2000) console.log("SELECTTRACKSync", _queuePositiom)
172
+ _oPlayer.selectTrack(_queuePosition).then(result => {
173
+ if (iWaitAfterSync > 2000) console.log("SELECTTRACKSync", _queuePosition)
173
174
  let t = setTimeout(() => {
174
175
  resolve(true);
175
176
  }, iWaitAfterSync);
@@ -181,9 +182,9 @@ module.exports = function (RED) {
181
182
  }
182
183
 
183
184
  // 24/08/2021 Sync wrapper
184
- function STOPSync() {
185
+ function STOPSync(_oPlayer = node.SonosClient) {
185
186
  return new Promise((resolve, reject) => {
186
- node.SonosClient.stop().then(result => {
187
+ _oPlayer.stop().then(result => {
187
188
  if (iWaitAfterSync > 2000) console.log("STOPSync")
188
189
  let t = setTimeout(() => {
189
190
  resolve(true);
@@ -196,9 +197,9 @@ module.exports = function (RED) {
196
197
  }
197
198
 
198
199
  // 24/08/2021 Sync wrapper
199
- function GETVOLUMESync() {
200
+ function GETVOLUMESync(_oPlayer = node.SonosClient) {
200
201
  return new Promise((resolve, reject) => {
201
- node.SonosClient.getVolume().then(volume => {
202
+ _oPlayer.getVolume().then(volume => {
202
203
  if (iWaitAfterSync > 2000) console.log("GETVOLUMESync", volume)
203
204
  resolve(volume);
204
205
  }).catch(err => {
@@ -209,9 +210,9 @@ module.exports = function (RED) {
209
210
  }
210
211
 
211
212
  // 24/08/2021 Sync wrapper
212
- function SETVOLUMESync(_volume) {
213
+ function SETVOLUMESync(_volume, _oPlayer = node.SonosClient) {
213
214
  return new Promise((resolve, reject) => {
214
- node.SonosClient.setVolume(_volume).then(result => {
215
+ _oPlayer.setVolume(_volume).then(result => {
215
216
  if (iWaitAfterSync > 2000) console.log("SETVOLUMESync", _volume)
216
217
  resolve(true);
217
218
  }).catch(err => {
@@ -222,9 +223,9 @@ module.exports = function (RED) {
222
223
  }
223
224
 
224
225
  // 24/08/2021 Sync wrapper
225
- function setAVTransportURISync(_Uri) {
226
+ function setAVTransportURISync(_Uri, _oPlayer = node.SonosClient) {
226
227
  return new Promise((resolve, reject) => {
227
- node.SonosClient.setAVTransportURI(_Uri).then(volume => {
228
+ _oPlayer.setAVTransportURI(_Uri).then(volume => {
228
229
  if (iWaitAfterSync > 2000) console.log("setAVTransportURISync", _Uri)
229
230
  resolve(true);
230
231
  }).catch(err => {
@@ -235,9 +236,9 @@ module.exports = function (RED) {
235
236
  }
236
237
 
237
238
  // 24/08/2021 Sync wrapper
238
- function getCurrentStateSync() {
239
+ function getCurrentStateSync(_oPlayer = node.SonosClient) {
239
240
  return new Promise((resolve, reject) => {
240
- node.SonosClient.getCurrentState().then(state => {
241
+ _oPlayer.getCurrentState().then(state => {
241
242
  resolve(state);
242
243
  }).catch(err => {
243
244
  RED.log.error("ttsultimate: Error getCurrentStateSync: " + err.message);
@@ -247,9 +248,9 @@ module.exports = function (RED) {
247
248
  }
248
249
 
249
250
  // 21/10/2021 Sync wrapper
250
- function GETMutedSync() {
251
+ function GETMutedSync(_oPlayer = node.SonosClient) {
251
252
  return new Promise((resolve, reject) => {
252
- node.SonosClient.getMuted().then(state => {
253
+ _oPlayer.getMuted().then(state => {
253
254
  resolve(state);
254
255
  }).catch(err => {
255
256
  RED.log.error("ttsultimate: Error GETMutedSync: " + err.message);
@@ -259,9 +260,9 @@ module.exports = function (RED) {
259
260
  }
260
261
 
261
262
  // 21/10/2021 Sync wrapper
262
- function SETMutedSync(_muted) {
263
+ function SETMutedSync(_muted, _oPlayer = node.SonosClient) {
263
264
  return new Promise((resolve, reject) => {
264
- node.SonosClient.setMuted(_muted).then(state => {
265
+ _oPlayer.setMuted(_muted).then(state => {
265
266
  resolve(state);
266
267
  }).catch(err => {
267
268
  RED.log.error("ttsultimate: Error SETMutedSync: " + err.message);
@@ -283,15 +284,12 @@ module.exports = function (RED) {
283
284
  }
284
285
  // 30/03/2020 in the middle of coronavirus emergency. Group Speakers
285
286
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
286
- const element = node.oAdditionalSonosPlayers[index];
287
- try {
288
- await element.joinGroup(node.sonosCoordinatorGroupName);
289
- } catch (error) {
290
- RED.log.warn("ttsultimate: Error joining device " + error.message);
291
- }
292
- // 02/07/2021 Get the player's volume set by app, to be set again in ungroupspealers
287
+ let element = node.oAdditionalSonosPlayers[index].oPlayer;
288
+
289
+ // 02/07/2021 Get the additional's player's volume set by app and the current track, to be set again in ungroupspealers
293
290
  try {
294
291
  element.additionalPlayerPreviousVolumeSetByApp = await element.getVolume();
292
+ element.additionalPlayerCurrentTrack = await getMusicQueue(element);
295
293
  } catch (error) {
296
294
  RED.log.warn("ttsultimate: Error setting volume of joined device " + error.message);
297
295
  }
@@ -301,6 +299,12 @@ module.exports = function (RED) {
301
299
  } catch (error) {
302
300
  RED.log.warn("ttsultimate: Error getMuted of joined device " + error.message);
303
301
  }
302
+
303
+ try {
304
+ await element.joinGroup(node.sonosCoordinatorGroupName);
305
+ } catch (error) {
306
+ RED.log.warn("ttsultimate: Error joining device " + error.message);
307
+ }
304
308
  };
305
309
  }
306
310
 
@@ -322,7 +326,7 @@ module.exports = function (RED) {
322
326
  }
323
327
 
324
328
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
325
- const element = node.oAdditionalSonosPlayers[index];
329
+ let element = node.oAdditionalSonosPlayers[index].oPlayer;
326
330
  try {
327
331
  await element.leaveGroup();
328
332
  } catch (error) {
@@ -401,9 +405,10 @@ module.exports = function (RED) {
401
405
  });
402
406
  // Fill the node.oAdditionalSonosPlayers with all sonos object in the rules
403
407
  for (let index = 0; index < node.rules.length; index++) {
404
- const element = node.rules[index];
405
- node.oAdditionalSonosPlayers.push(new sonos.Sonos(element.host));
406
- RED.log.info("ttsultimate: FOUND ADDITIONAL PLAYER " + element.host);
408
+ let element = node.rules[index]; // Rule row is {host:"192.168.1.12,hostVolumeAdjust:0}
409
+ // 12/04/2022 Create an object containing the addidtional player and the adapted volume
410
+ node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: Number(element.hostVolumeAdjust) });
411
+ RED.log.info("ttsultimate: FOUND ADDITIONAL PLAYER " + element.host + " Adjusted volume: " + element.hostVolumeAdjust);
407
412
  }
408
413
 
409
414
 
@@ -432,7 +437,7 @@ module.exports = function (RED) {
432
437
 
433
438
 
434
439
  // 30/12/2020 Supergiovane resume queue for radio, queue music, TV in , line in etc.
435
- async function resumeMusicQueue(_oTrack) {
440
+ async function resumeMusicQueue(_oTrack, _oPlayer = node.SonosClient) {
436
441
 
437
442
  if (_oTrack !== null) {
438
443
  // Do some checks on the track.
@@ -457,12 +462,13 @@ module.exports = function (RED) {
457
462
  if (_oTrack.state === "playing") {
458
463
  // 03/09/2021 Play if it was playing
459
464
  try {
460
- await PLAYSync(_oTrack.uri);
465
+ await PLAYSync(_oTrack.uri, _oPlayer);
461
466
  } catch (error) {
462
467
  return error;
463
468
  }
464
469
  try {
465
- await SEEKSync(_oTrack.position);
470
+ await delay(1000);
471
+ await SEEKSync(_oTrack.position, _oPlayer);
466
472
  } catch (error) {
467
473
  // Don't care
468
474
  }
@@ -470,31 +476,33 @@ module.exports = function (RED) {
470
476
  } else {
471
477
  if (_oTrack.trackType === "musicqueue") { // This indicates that is an audio file or stream station
472
478
  try {
473
- await SELECTQUEUESync();
479
+ await SELECTQUEUESync(_oPlayer);
474
480
  } catch (error) {
475
481
  return error;
476
482
  }
477
483
  try {
478
- await SELECTTRACKSync(_oTrack.queuePosition);
484
+ await delay(1000);
485
+ await SELECTTRACKSync(_oTrack.queuePosition, _oPlayer);
479
486
  } catch (error) {
480
487
  return error;
481
488
  }
482
489
  try {
483
- await SEEKSync(_oTrack.position);
490
+ await delay(1000);
491
+ await SEEKSync(_oTrack.position, _oPlayer);
484
492
  } catch (error) {
485
493
  // Don't care
486
494
  }
487
495
  if (_oTrack.state === "playing") {
488
496
  // 24/08/2021 Play if it was playing
489
497
  try {
490
- await PLAYSync();
498
+ await PLAYSync(_oPlayer);
491
499
  } catch (error) {
492
500
  return error;
493
501
  }
494
502
  } else {
495
503
  /// 03/09/2021
496
504
  try {
497
- await STOPSync();
505
+ await STOPSync(_oPlayer);
498
506
  } catch (error) {
499
507
  return error;
500
508
  }
@@ -503,7 +511,7 @@ module.exports = function (RED) {
503
511
  // Line in, TV in, etc...
504
512
  if (_oTrack.state === "playing") {
505
513
  try {
506
- await setAVTransportURISync(_oTrack.uri);
514
+ await setAVTransportURISync(_oTrack.uri, _oPlayer);
507
515
  } catch (error) {
508
516
  return error;
509
517
  }
@@ -513,6 +521,7 @@ module.exports = function (RED) {
513
521
  let t = setTimeout(() => { return true; }, 5000); // Wait some seconds
514
522
  };
515
523
 
524
+
516
525
  // Handle the queue
517
526
  async function HandleQueue() {
518
527
  node.bBusyPlayingQueue = true;
@@ -523,6 +532,7 @@ module.exports = function (RED) {
523
532
  var oCurTrack = null;
524
533
  try {
525
534
  oCurTrack = await getMusicQueue();
535
+ // 19/04/2022 The current track of additional players is read in the groupSpeakerySync function
526
536
  } catch (error) {
527
537
  oCurTrack = null;
528
538
  }
@@ -530,7 +540,7 @@ module.exports = function (RED) {
530
540
  // 05/12/2020 Set "completed" to false and send it
531
541
  node.msg.completed = false;
532
542
  try {
533
- await groupSpeakersSync(); // 20/03/2020 Group Speakers toghether
543
+ await groupSpeakersSync(); // 20/03/2020 Group Speakers toghether and reads each current track
534
544
  } catch (error) {
535
545
  // Don't care.
536
546
  node.setNodeStatus({ fill: "red", shape: "ring", text: "Error grouping speakers: " + error.message });
@@ -655,21 +665,26 @@ module.exports = function (RED) {
655
665
 
656
666
  // Set Volume
657
667
  try {
658
- let volTemp = 0
668
+ let volTemp = 0;
659
669
  if (node.currentMSGbeingSpoken.hasOwnProperty("volume")) {
660
- volTemp = node.currentMSGbeingSpoken.volume;
670
+ volTemp = Number(node.currentMSGbeingSpoken.volume);
661
671
  } else {
662
- volTemp = node.sSonosVolume;
672
+ volTemp = Number(node.sSonosVolume);
663
673
  }
664
674
  await SETVOLUMESync(volTemp);
665
675
  if (node.unmuteIfMuted) await SETMutedSync(false); // 21/10/2021 Unmute
666
676
 
667
677
  if (node.oAdditionalSonosPlayers.length > 0) {
668
- // 05/07/2021 set the volume of additional coordinatores
678
+ // 05/07/2021 set the volume of additional coordinators
669
679
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
670
- const element = node.oAdditionalSonosPlayers[index];
680
+ let element = node.oAdditionalSonosPlayers[index].oPlayer;
681
+ //node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: element.hostVolumeAdjust });
671
682
  try {
672
- await element.setVolume(volTemp);
683
+ // 12/04/20222 Set the adjusted volume, based on the main player volume + the adjusted volume in %
684
+ let iAdjustedVol = Number(volTemp) + Number((node.oAdditionalSonosPlayers[index].hostVolumeAdjust || 0));
685
+ if (iAdjustedVol < 0) iAdjustedVol = 0;
686
+ if (iAdjustedVol > 100) iAdjustedVol = 100;
687
+ await element.setVolume(iAdjustedVol);
673
688
  if (node.unmuteIfMuted) await element.setMuted(false); // 21/10/2021 Unmute
674
689
  } catch (error) {
675
690
  RED.log.error("ttsultimate: Handlequeue: Unable to set the volume on additional player " + error.message);
@@ -760,7 +775,7 @@ module.exports = function (RED) {
760
775
 
761
776
  // Ungroup speaker
762
777
  try {
763
- await ungroupSpeakersSync();
778
+ await ungroupSpeakersSync(); // Ungroup speakers
764
779
  } catch (error) {
765
780
  // Don't care.
766
781
  node.setNodeStatus({ fill: "red", shape: "ring", text: "Error ungrouping speakers: " + error.message });
@@ -782,6 +797,24 @@ module.exports = function (RED) {
782
797
  node.setNodeStatus({ fill: 'red', shape: 'ring', text: "Error resuming queue: " + error.message });
783
798
  }
784
799
 
800
+
801
+ // 19/04/2022 Resume music queue of additional players
802
+ for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
803
+ let addPlayer = node.oAdditionalSonosPlayers[index].oPlayer;
804
+ let trackAddPlayer = addPlayer.additionalPlayerCurrentTrack;
805
+ if (trackAddPlayer !== null) {
806
+ try {
807
+ await resumeMusicQueue(trackAddPlayer, addPlayer);
808
+ node.setNodeStatus({ fill: 'green', shape: 'ring', text: "Done resuming queue additional player " + addPlayer.host || "" });
809
+ } catch (error) {
810
+ // Dont care
811
+ RED.log.warn("ttsultimate: Error resuming music queue of additional player " + error.message + " " + addPlayer.host || "");
812
+ }
813
+ } else {
814
+ node.setNodeStatus({ fill: 'green', shape: 'ring', text: "No queue to resume for " + addPlayer.host || "" });
815
+ }
816
+ }
817
+
785
818
  // Signal end playing
786
819
  let t = setTimeout(() => {
787
820
  node.msg.completed = true;
@@ -843,9 +876,18 @@ module.exports = function (RED) {
843
876
  // Fill the node.oAdditionalSonosPlayers with all sonos IPs in the setPlayerGroupArray
844
877
  node.oAdditionalSonosPlayers = [];
845
878
  for (let index = 0; index < msg.setConfig.setPlayerGroupArray.length; index++) {
846
- const element = msg.setConfig.setPlayerGroupArray[index];
847
- node.oAdditionalSonosPlayers.push(new sonos.Sonos(element));
848
- RED.log.info("ttsultimate: new group player set by msg: " + element);
879
+ const sRow = msg.setConfig.setPlayerGroupArray[index];
880
+ let host = "";
881
+ let hostVolumeAdjust = 0;
882
+ if (sRow.includes("#")) {
883
+ host = sRow.split("#")[0];
884
+ hostVolumeAdjust = sRow.split("#")[1];
885
+ } else {
886
+ host = sRow;
887
+ }
888
+ //node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: element.hostVolumeAdjust });
889
+ node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(host), hostVolumeAdjust: Number(hostVolumeAdjust) });
890
+ RED.log.info("ttsultimate: new group player set by msg: " + host + " adjusted volume: " + Number(hostVolumeAdjust));
849
891
  }
850
892
  };
851
893
  };