node-red-contrib-tts-ultimate 1.0.41 → 1.0.44

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,21 @@
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.44</b> April 2022<br/>
7
+ - 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/>
8
+ - Updated the README.<br/>
9
+ </p>
10
+ <p>
11
+ <b>Version 1.0.43</b> March 2022<br/>
12
+ - Simplified the configuration by auto discover some IP.<br/>
13
+ - FIX: fixed minor glitches.<br/>
14
+ </p>
15
+ <p>
16
+ <b>Version 1.0.42</b> March 2022<br/>
17
+ - FIX: fix purging option that wasn't working if you set to always purge the cached files at startup.<br/>
18
+ - FIX: invalid code in some sync functions.<br/>
19
+ </p>
5
20
  <p>
6
21
  <b>Version 1.0.41</b> March 2022<br/>
7
22
  - NEW: for Polly TTS, you can choose between neural and standard engine.<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.41",
3
+ "version": "1.0.44",
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": {
@@ -81,7 +81,7 @@
81
81
  return;
82
82
  }
83
83
  // The only way is to wait some time, then refresh
84
- setTimeout(function () {
84
+ let t = setTimeout(function () {
85
85
  node.refreshListaFiles().then((success, error) => {
86
86
  $("#ownFileUpload").val("");// Otherwise will not re-upload a file with the same name
87
87
  $("#node-input-selectedFile").val("OwnFile_" + file.name);
@@ -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 = [];
@@ -538,22 +544,20 @@ module.exports = function (RED) {
538
544
  if (node.purgediratrestart === "purge") {
539
545
  // Delete all files, that are'nt OwnFiles_
540
546
  try {
541
- fs.readdirSync(path.join(node.TTSRootFolderPath, "ttsfiles"), (err, files) => {
542
- try {
543
- if (files.length > 0) {
544
- files.forEach(function (file) {
545
- RED.log.info("ttsultimate-config " + node.id + ": Deleted TTS file " + path.join(node.TTSRootFolderPath, "ttsfiles", file));
546
- try {
547
- fs.unlinkSync(path.join(node.TTSRootFolderPath, "ttsfiles", file));
548
- } catch (error) {
549
- }
550
- });
551
- };
552
- } catch (error) {
547
+ let files = fs.readdirSync(path.join(node.TTSRootFolderPath, "ttsfiles"));
548
+ try {
549
+ if (files.length > 0) {
550
+ files.forEach(function (file) {
551
+ RED.log.info("ttsultimate-config " + node.id + ": Deleted TTS file " + path.join(node.TTSRootFolderPath, "ttsfiles", file));
552
+ try {
553
+ fs.unlinkSync(path.join(node.TTSRootFolderPath, "ttsfiles", file));
554
+ } catch (error) {
555
+ }
556
+ });
557
+ };
558
+ } catch (error) { }
553
559
 
554
- }
555
560
 
556
- });
557
561
  } catch (error) { }
558
562
  };
559
563
 
@@ -572,13 +576,13 @@ module.exports = function (RED) {
572
576
  var query = url_parts.query;
573
577
 
574
578
  res.setHeader('Content-Disposition', 'attachment; filename=tts.mp3')
575
- if (fs.existsSync(query.f)) {
579
+ if (fs.existsSync(query.f.toString())) {
576
580
  // 26/01/2021 security check
577
581
  // File should be something like mydocs/.node-red/sonospollyttsstorage/ttsfiles/Hello_de-DE.mp3
578
- if (path.extname(query.f) === ".mp3" && path.dirname(path.dirname(query.f)).endsWith("sonospollyttsstorage")) {
579
- var readStream = fs.createReadStream(query.f);
582
+ if (path.extname(query.f.toString()) === ".mp3" && path.dirname(path.dirname(query.f.toString())).endsWith("sonospollyttsstorage")) {
583
+ var readStream = fs.createReadStream(query.f.toString());
580
584
  readStream.on("error", function (error) {
581
- RED.log.error("ttsultimate-config " + node.id + ": Playsonos error opening stream : " + query.f + ' : ' + error);
585
+ RED.log.error("ttsultimate-config " + node.id + ": Playsonos error opening stream : " + query.f.toString() + ' : ' + error);
582
586
  res.end();
583
587
  return;
584
588
  });
@@ -637,7 +641,7 @@ module.exports = function (RED) {
637
641
  } catch (error) {
638
642
 
639
643
  }
640
- setTimeout(function () {
644
+ let t = setTimeout(function () {
641
645
  // Wait some time to allow time to do promises.
642
646
  done();
643
647
  }, 500);
@@ -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 === "") {
@@ -336,7 +336,7 @@
336
336
  }
337
337
  // Refresh the combo
338
338
  // The only way is to wait some time, then refresh
339
- setTimeout(function () {
339
+ let t = setTimeout(function () {
340
340
  node.refreshHailingList().then((success, error) => {
341
341
  $("#ownFileUpload").val("");// Otherwise will not re-upload a file with the same name
342
342
  $("#node-input-sonoshailing").val("Hailing_" + file.name);
@@ -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
  },
@@ -52,7 +52,7 @@ module.exports = function (RED) {
52
52
  return;
53
53
  }
54
54
  node.ssml = config.ssml;
55
- node.oTimerSonosConnectionCheck;
55
+ node.oTimerSonosConnectionCheck = null;
56
56
  node.sSonosIPAddress = "";
57
57
  node.sonosCoordinatorGroupName = "";
58
58
  node.sonoshailing = "0"; // Hailing file
@@ -78,6 +78,7 @@ module.exports = function (RED) {
78
78
  node.unmuteIfMuted = config.unmuteIfMuted === undefined ? false : config.unmuteIfMuted; // 21/10/2021 Unmute if previiously muted.
79
79
  node.sonosCoordinatorIsPreviouslyMuted = false;
80
80
  node.passThroughMessage = {};
81
+ node.bTimeOutPlay = false;
81
82
 
82
83
  if (typeof node.server !== "undefined" && node.server !== null) {
83
84
  node.sNoderedURL = node.server.sNoderedURL || "";
@@ -120,7 +121,7 @@ module.exports = function (RED) {
120
121
  return new Promise((resolve, reject) => {
121
122
  node.SonosClient.play(_toPlay).then(result => {
122
123
  if (iWaitAfterSync > 2000) console.log("PLAYSYNC")
123
- setTimeout(() => {
124
+ let t = setTimeout(() => {
124
125
  resolve(true);
125
126
  }, iWaitAfterSync);
126
127
  }).catch(err => {
@@ -135,7 +136,7 @@ module.exports = function (RED) {
135
136
  return new Promise((resolve, reject) => {
136
137
  node.SonosClient.seek(_Position).then(result => {
137
138
  if (iWaitAfterSync > 2000) console.log("SEEKSync", _Position)
138
- setTimeout(() => {
139
+ let t = setTimeout(() => {
139
140
  resolve(true);
140
141
  }, iWaitAfterSync);
141
142
  }).catch(err => {
@@ -154,7 +155,7 @@ module.exports = function (RED) {
154
155
  STOPSync(); // The SetQueue automatically starts playing, so i need to stop it now!
155
156
  } catch (error) {
156
157
  }
157
- setTimeout(() => {
158
+ let t = setTimeout(() => {
158
159
  resolve(true);
159
160
  }, iWaitAfterSync);
160
161
  }).catch(err => {
@@ -169,7 +170,7 @@ module.exports = function (RED) {
169
170
  return new Promise((resolve, reject) => {
170
171
  node.SonosClient.selectTrack(_queuePositiom).then(result => {
171
172
  if (iWaitAfterSync > 2000) console.log("SELECTTRACKSync", _queuePositiom)
172
- setTimeout(() => {
173
+ let t = setTimeout(() => {
173
174
  resolve(true);
174
175
  }, iWaitAfterSync);
175
176
  }).catch(err => {
@@ -184,7 +185,7 @@ module.exports = function (RED) {
184
185
  return new Promise((resolve, reject) => {
185
186
  node.SonosClient.stop().then(result => {
186
187
  if (iWaitAfterSync > 2000) console.log("STOPSync")
187
- setTimeout(() => {
188
+ let t = setTimeout(() => {
188
189
  resolve(true);
189
190
  }, iWaitAfterSync);
190
191
  }).catch(err => {
@@ -282,7 +283,7 @@ module.exports = function (RED) {
282
283
  }
283
284
  // 30/03/2020 in the middle of coronavirus emergency. Group Speakers
284
285
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
285
- const element = node.oAdditionalSonosPlayers[index];
286
+ const element = node.oAdditionalSonosPlayers[index].oPlayer;
286
287
  try {
287
288
  await element.joinGroup(node.sonosCoordinatorGroupName);
288
289
  } catch (error) {
@@ -321,7 +322,7 @@ module.exports = function (RED) {
321
322
  }
322
323
 
323
324
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
324
- const element = node.oAdditionalSonosPlayers[index];
325
+ const element = node.oAdditionalSonosPlayers[index].oPlayer;
325
326
  try {
326
327
  await element.leaveGroup();
327
328
  } catch (error) {
@@ -400,9 +401,10 @@ module.exports = function (RED) {
400
401
  });
401
402
  // Fill the node.oAdditionalSonosPlayers with all sonos object in the rules
402
403
  for (let index = 0; index < node.rules.length; index++) {
403
- const element = node.rules[index];
404
- node.oAdditionalSonosPlayers.push(new sonos.Sonos(element.host));
405
- RED.log.info("ttsultimate: FOUND ADDITIONAL PLAYER " + element.host);
404
+ const element = node.rules[index]; // Rule row is {host:"192.168.1.12,hostVolumeAdjust:0}
405
+ // 12/04/2022 Create an object containing the addidtional player and the adapted volume
406
+ node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: Number(element.hostVolumeAdjust) });
407
+ RED.log.info("ttsultimate: FOUND ADDITIONAL PLAYER " + element.host + " Adjusted volume: " + element.hostVolumeAdjust);
406
408
  }
407
409
 
408
410
 
@@ -509,7 +511,7 @@ module.exports = function (RED) {
509
511
  }
510
512
  }
511
513
  }
512
- setTimeout(() => { return true; }, 5000); // Wait some seconds
514
+ let t = setTimeout(() => { return true; }, 5000); // Wait some seconds
513
515
  };
514
516
 
515
517
  // Handle the queue
@@ -619,13 +621,14 @@ module.exports = function (RED) {
619
621
  data = await synthesizeSpeechMicrosoftAzureTTS(node.server.microsoftAzureTTS, params);
620
622
  }
621
623
  // Save the downloaded file into the cache
622
- fs.writeFileSync(sFileToBePlayed, data, function (error, result) {
623
- if (error) {
624
- RED.log.error("ttsultimate: node id: " + node.id + " Unable to save the file " + error.message);
625
- node.setNodeStatus({ fill: "red", shape: "ring", text: "Unable to save the file " + sFileToBePlayed + " " + error.message });
626
- throw (error);
627
- }
628
- });
624
+ try {
625
+ fs.writeFileSync(sFileToBePlayed, data);
626
+ } catch (error) {
627
+ RED.log.error("ttsultimate: node id: " + node.id + " Unable to save the file " + error.message);
628
+ node.setNodeStatus({ fill: "red", shape: "ring", text: "Unable to save the file " + sFileToBePlayed + " " + error.message });
629
+ throw (error);
630
+ }
631
+
629
632
  } catch (error) {
630
633
  RED.log.error("ttsultimate: node id: " + node.id + " Error Downloading TTS: " + error.message + ". THE TTS SERVICE MAY BE DOWN.");
631
634
  node.setNodeStatus({ fill: 'red', shape: 'ring', text: 'Error Downloading TTS:' + error.message });
@@ -653,21 +656,26 @@ module.exports = function (RED) {
653
656
 
654
657
  // Set Volume
655
658
  try {
656
- let volTemp = 0
659
+ let volTemp = 0;
657
660
  if (node.currentMSGbeingSpoken.hasOwnProperty("volume")) {
658
- volTemp = node.currentMSGbeingSpoken.volume;
661
+ volTemp = Number(node.currentMSGbeingSpoken.volume);
659
662
  } else {
660
- volTemp = node.sSonosVolume;
663
+ volTemp = Number(node.sSonosVolume);
661
664
  }
662
665
  await SETVOLUMESync(volTemp);
663
666
  if (node.unmuteIfMuted) await SETMutedSync(false); // 21/10/2021 Unmute
664
667
 
665
668
  if (node.oAdditionalSonosPlayers.length > 0) {
666
- // 05/07/2021 set the volume of additional coordinatores
669
+ // 05/07/2021 set the volume of additional coordinators
667
670
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
668
- const element = node.oAdditionalSonosPlayers[index];
671
+ const element = node.oAdditionalSonosPlayers[index].oPlayer;
672
+ //node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: element.hostVolumeAdjust });
669
673
  try {
670
- await element.setVolume(volTemp);
674
+ // 12/04/20222 Set the adjusted volume, based on the main player volume + the adjusted volume in %
675
+ let iAdjustedVol = Number(volTemp) + Number((node.oAdditionalSonosPlayers[index].hostVolumeAdjust || 0));
676
+ if (iAdjustedVol < 0) iAdjustedVol = 0;
677
+ if (iAdjustedVol > 100) iAdjustedVol = 100;
678
+ await element.setVolume(iAdjustedVol);
671
679
  if (node.unmuteIfMuted) await element.setMuted(false); // 21/10/2021 Unmute
672
680
  } catch (error) {
673
681
  RED.log.error("ttsultimate: Handlequeue: Unable to set the volume on additional player " + error.message);
@@ -781,7 +789,7 @@ module.exports = function (RED) {
781
789
  }
782
790
 
783
791
  // Signal end playing
784
- setTimeout(() => {
792
+ let t = setTimeout(() => {
785
793
  node.msg.completed = true;
786
794
  node.currentMSGbeingSpoken = {};
787
795
  node.send([{ passThroughMessage: node.passThroughMessage, payload: node.msg.completed }, null]);
@@ -794,7 +802,7 @@ module.exports = function (RED) {
794
802
  // Output the array of files
795
803
 
796
804
  // Signal end playing
797
- setTimeout(() => {
805
+ let t = setTimeout(() => {
798
806
  node.msg.completed = true;
799
807
  node.currentMSGbeingSpoken = {};
800
808
  node.send([{ passThroughMessage: node.passThroughMessage, payload: node.msg.completed, filesArray: noPlayerFileArray }, null]);
@@ -841,9 +849,18 @@ module.exports = function (RED) {
841
849
  // Fill the node.oAdditionalSonosPlayers with all sonos IPs in the setPlayerGroupArray
842
850
  node.oAdditionalSonosPlayers = [];
843
851
  for (let index = 0; index < msg.setConfig.setPlayerGroupArray.length; index++) {
844
- const element = msg.setConfig.setPlayerGroupArray[index];
845
- node.oAdditionalSonosPlayers.push(new sonos.Sonos(element));
846
- RED.log.info("ttsultimate: new group player set by msg: " + element);
852
+ const sRow = msg.setConfig.setPlayerGroupArray[index];
853
+ let host = "";
854
+ let hostVolumeAdjust = 0;
855
+ if (sRow.includes("#")) {
856
+ host = sRow.split("#")[0];
857
+ hostVolumeAdjust = sRow.split("#")[1];
858
+ } else {
859
+ host = sRow;
860
+ }
861
+ //node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: element.hostVolumeAdjust });
862
+ node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(host), hostVolumeAdjust: Number(hostVolumeAdjust) });
863
+ RED.log.info("ttsultimate: new group player set by msg: " + host + " adjusted volume: " + Number(hostVolumeAdjust));
847
864
  }
848
865
  };
849
866
  };