node-red-contrib-sonospollytts
Advanced tools
Comparing version 1.1.27 to 1.1.28
@@ -8,2 +8,8 @@ ![Sample Node](img/logo.png) | ||
<p> | ||
<b>Version 1.1.28</b> September 2020<br/> | ||
- Updated sonos API to 1.14.0<br/> | ||
- Fixed an issue where if you power cycle your sonos and it comes up too fast, the node won't detect the disconnection and won't reset the link to sonos.<br/> | ||
- Major code revision, to clean up old things.<br/> | ||
</p> | ||
<p> | ||
<b>Version 1.1.27</b> August 2020<br/> | ||
@@ -10,0 +16,0 @@ - Fixed an odd issue. Now "msg.nohailing=true" temporarly (and not permanently anymore) disables the hailing. If you send a new payload without msg.nohailing=true, the hailing will be heard again (that's the standard and intended behaviour.)<br/> |
{ | ||
"name": "node-red-contrib-sonospollytts", | ||
"version": "1.1.27", | ||
"version": "1.1.28", | ||
"description": "Node-Red TTS with Sonos and Amazon Polly or with your own local mp3 announcement files. Transforms the text in speech and hear it using Sonos player. Can work OFFLINE as well! This node is specific for security alarm announcement, doorbell, weather annoucement etc.", | ||
@@ -42,3 +42,3 @@ "main": "index.js", | ||
"path": "^0.12.7", | ||
"sonos": "1.13.0", | ||
"sonos": "1.14.0", | ||
"util": ">=0.10.1", | ||
@@ -45,0 +45,0 @@ "formidable": "1.2.2", |
@@ -611,6 +611,49 @@ module.exports = function (RED) { | ||
node.setNodeStatus = ({ fill, shape, text }) => { | ||
var dDate = new Date(); | ||
node.status({ fill: fill, shape: shape, text: text + " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" }); | ||
try { | ||
var dDate = new Date(); | ||
node.status({ fill: fill, shape: shape, text: text + " (" + dDate.getDate() + ", " + dDate.toLocaleTimeString() + ")" }); | ||
} catch (error) { } | ||
} | ||
// 27/11/2019 Check Sonos connection healt | ||
node.CheckSonosConnection = () => { | ||
node.SonosClient.getCurrentState().then(state => { | ||
node.SonosClient.currentTrack().then(track => { | ||
// 27/11/2019 Set node output to signal connectio error | ||
if (node.msg.connectionerror == true) { | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Sonos is connected." }); | ||
node.msg.connectionerror = false; | ||
node.send({ "connectionerror": node.msg.connectionerror }); | ||
} | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { node.CheckSonosConnection(); }, 2000); | ||
}).catch(err => { | ||
node.flushQueue(); | ||
// 27/11/2019 Set node output to signal connectio error | ||
if (node.msg.connectionerror == false) { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: "Sonos connection is DOWN: " + err }); | ||
node.msg.connectionerror = true; | ||
node.send({ "connectionerror": node.msg.connectionerror }); | ||
} | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { node.CheckSonosConnection(); }, 2000); | ||
}); | ||
}).catch(err => { | ||
node.flushQueue(); | ||
// 27/11/2019 Set node output to signal connectio error | ||
if (node.msg.connectionerror == false) { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: "Sonos connection is DOWN: " + err }); | ||
node.msg.connectionerror = true; | ||
node.send({ "connectionerror": node.msg.connectionerror }); | ||
} | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { node.CheckSonosConnection(); }, 2000); | ||
}); | ||
} | ||
// 03/06/2019 you can select the temp dir | ||
@@ -684,6 +727,6 @@ if (!setupDirectory(node.userDir)) { | ||
// Start the TTS queue timer | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 5000); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 5000); | ||
// 27/11/2019 Start the connection healty check | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { CheckSonosConnection(node); }, 5000); | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { node.CheckSonosConnection(); }, 5000); | ||
@@ -750,3 +793,3 @@ node.sonoshailing = config.sonoshailing; | ||
if (!msg.hasOwnProperty("payload")) { | ||
notifyError(node, msg, 'msg.payload must be of type String'); | ||
notifyError(msg, 'msg.payload must be of type String'); | ||
return; | ||
@@ -804,3 +847,3 @@ } | ||
node.aMessageQueue.push(msg.payload); | ||
node.setNodeStatus({ fill: 'yellow', shape: 'dot', text: 'Queued' + msg.payload }); | ||
node.setNodeStatus({ fill: 'yellow', shape: 'dot', text: 'Queued ' + msg.payload }); | ||
@@ -811,2 +854,3 @@ }); | ||
clearTimeout(node.oTimer); | ||
clearTimeout(node.oTimerSonosConnectionCheck); | ||
node.SonosClient.stop().then(() => { | ||
@@ -817,3 +861,4 @@ node.ungroupSpeakers(); | ||
node.send(node.msg); | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "" + node.sSonosPlayState }); | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Shutdown" }); | ||
node.flushQueue(); | ||
// 11/11/2019 Close the Webserver | ||
@@ -828,313 +873,303 @@ try { | ||
done(); | ||
}, 3000); | ||
}, 1000); | ||
}); | ||
// Handle the queue | ||
function HandleQueue() { | ||
// 06/05/2019 check if the SonosClient is already instantiate (an error can occur if a very slow PC is used) | ||
if (node.SonosClient == null) { | ||
RED.log.info('SonosPollyTTS: InfoHandleQueue0 SonosClient not instantiate. Retry later...'); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 5000); | ||
return; | ||
} | ||
// try { | ||
// node.setNodeStatus({ fill: "yellow", shape: "dot", text: "Queue: " + node.sPollyState }); | ||
// } catch (error) { } | ||
if (node.sPollyState == "transitioning") { | ||
node.iTimeoutPollyState += 1; // Increase Timeout | ||
if (node.iTimeoutPollyState > 15) { | ||
node.iTimeoutPollyState = 0; | ||
node.sPollyState = "idle"; | ||
RED.log.info('SonosPollyTTS: HandleQueue - Polly is in downloading Timeout'); | ||
node.setNodeStatus({ | ||
fill: 'yellow', | ||
shape: 'dot', | ||
text: 'SonosPollyTTS: HandleQueue - Polly is in downloading Timeout' | ||
}); | ||
node.sSonosPlayState = "stopped"; | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 1000); | ||
return; | ||
} | ||
} | ||
RED.nodes.registerType('sonospollytts', PollyNode); | ||
// Handle the queue | ||
function HandleQueue(node) { | ||
// Check if Polly is downloading the file (in case the phrase is very long) | ||
// 06/05/2019 check if the SonosClient is already instantiate (an error can occur if a very slow PC is used) | ||
if (node.SonosClient == null) { | ||
RED.log.info('SonosPollyTTS: InfoHandleQueue0 SonosClient not instantiate. Retry later...'); | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 5000); | ||
return; | ||
} | ||
if (node.sPollyState == "transitioning") { | ||
node.iTimeoutPollyState += 1; // Increase Timeout | ||
if (node.iTimeoutPollyState > 15) { | ||
node.iTimeoutPollyState = 0; | ||
node.sPollyState = "idle"; | ||
RED.log.info('SonosPollyTTS: HandleQueue - Polly is in downloading Timeout'); | ||
// Not cached | ||
node.setNodeStatus({ | ||
fill: 'yellow', | ||
shape: 'dot', | ||
text: 'SonosPollyTTS: HandleQueue - Polly is in downloading Timeout' | ||
text: 'downloading' | ||
}); | ||
node.sSonosPlayState = "stopped"; | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 1000); | ||
RED.log.info('SonosPollyTTS: HandleQueue - Polly is downloading the file, exit'); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 500); | ||
return; | ||
} | ||
// Not cached | ||
node.setNodeStatus({ | ||
fill: 'yellow', | ||
shape: 'dot', | ||
text: 'downloading' | ||
}); | ||
RED.log.info('SonosPollyTTS: HandleQueue - Polly is downloading the file, exit'); | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 1000); | ||
return; | ||
} else { | ||
node.iTimeoutPollyState = 0; // Reset Timer | ||
} | ||
} else { | ||
node.iTimeoutPollyState = 0; // Reset Timer | ||
} | ||
// 06/05/2019 moved the code into the "try" | ||
try { | ||
// 06/05/2019 moved the code into the "try" | ||
try { | ||
node.SonosClient.getCurrentState().then(state => { | ||
node.sSonosPlayState = state; | ||
node.SonosClient.currentTrack().then(track => { | ||
node.sSonosTrackTitle = track.uri; | ||
HandleQueue2(); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 500); | ||
return; | ||
}).catch(err => { | ||
node.flushQueue(); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 2000); | ||
}); // node.SonosClient.currentTrack().then(track=>{ | ||
node.SonosClient.getCurrentState().then(state => { | ||
node.sSonosPlayState = state; | ||
//RED.log.info('SonosPollyTTS: DEBUG HandleQueue - node.sSonosPlayState=' + node.sSonosPlayState); | ||
node.SonosClient.currentTrack().then(track => { | ||
node.sSonosTrackTitle = track.uri; | ||
HandleQueue2(node); | ||
}).catch(err => { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: "err currtrack: " + err + " " + node.sSonosPlayState }); | ||
node.sSonosTrackTitle = "stopped"; // force stopped | ||
HandleQueue2(node); | ||
}); // node.SonosClient.currentTrack().then(track=>{ | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: "err currstate: " + err }); | ||
node.flushQueue(); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 2000); | ||
}); // node.SonosClient.getCurrentState().then(state=>{ | ||
} catch (error) { | ||
// 06/05/2019 restart timer. To be removed if the try catch is removed as well. | ||
RED.log.info('SonosPollyTTS: errHandleQueue1 ' + error.toString()); | ||
node.flushQueue(); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 2000); | ||
} | ||
}).catch(err => { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: "err currstate: " + err }); | ||
node.sSonosTrackTitle = "stopped"; // force stopped | ||
} | ||
// 10/04/2018 Remove the TTS message from the queue | ||
if (node.aMessageQueue.length > 0) { | ||
node.aMessageQueue = []; | ||
RED.log.info('SonosPollyTTS: HandleQueue2 - error, flushed queue'); | ||
} | ||
// 22/09/2020 Flush Queue and set to stopped | ||
node.flushQueue = () => { | ||
// 10/04/2018 Remove the TTS message from the queue | ||
if (node.aMessageQueue.length > 0) { | ||
node.aMessageQueue = []; | ||
} | ||
node.sSonosPlayState = "stopped"; | ||
node.sSonosTrackTitle = "stopped"; | ||
node.sPollyState = "idle" | ||
} | ||
// Set timeout | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 500); | ||
// Handle queue 2 | ||
function HandleQueue2() { | ||
}); // node.SonosClient.getCurrentState().then(state=>{ | ||
} catch (error) { | ||
// Play next msg | ||
if (node.aMessageQueue.length > 0) { | ||
// 06/05/2019 restart timer. To be removed if the try catch is removed as well. | ||
// Set timeout | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 500); | ||
RED.log.info('SonosPollyTTS: errHandleQueue1 ' + error.toString()); | ||
try { | ||
node.setNodeStatus({ fill: "yellow", shape: "dot", text: "HandleQueue2: " + node.aMessageQueue.length }); | ||
} catch (error) { } | ||
} | ||
// It's playing something. Check what's playing. | ||
// If Music, then stop the music and play the TTS message | ||
// If playing TTS message, waits until it's finished. | ||
if (node.sSonosPlayState == "stopped" || node.sSonosPlayState == "paused") { | ||
var sMsg = node.aMessageQueue[0]; | ||
// Remove the TTS message from the queue | ||
node.aMessageQueue.splice(0, 1); | ||
} | ||
node.sPollyState = "transitioning"; | ||
node.sSonosPlayState = "transitioning"; | ||
node.setNodeStatus({ | ||
fill: 'yellow', | ||
shape: 'dot', | ||
text: 'preparing...' | ||
}); | ||
// Create the TTS mp3 with Polly | ||
Leggi(sMsg); | ||
// Handle queue 2 | ||
function HandleQueue2(node) { | ||
} else if (node.sSonosPlayState == "playing" && node.sSonosTrackTitle.toLocaleLowerCase().indexOf(".mp3") == -1) { | ||
// Play next msg | ||
if (node.aMessageQueue.length > 0) { | ||
//RED.log.info('SonosPollyTTS: HandleQueue2 - stopping: ' + node.sSonosPlayState + " Track:" + node.sSonosTrackTitle); | ||
// It's playing something. Check what's playing. | ||
// If Music, then stop the music and play the TTS message | ||
// If playing TTS message, waits until it's finished. | ||
if (node.sSonosPlayState == "stopped" || node.sSonosPlayState == "paused") { | ||
var sMsg = node.aMessageQueue[0]; | ||
// It's playing something. Stop | ||
node.SonosClient.pause().then(success => { | ||
//RED.log.info('SonosPollyTTS: HandleQueue2 - stopped: ' + success + " " + node.sSonosPlayState + " Track:" + node.sSonosTrackTitle); | ||
try { | ||
var sMsg = node.aMessageQueue[0]; | ||
node.aMessageQueue.splice(0, 1); // Remove the TTS message from the queue | ||
} catch (error) { | ||
} | ||
// Create the TTS mp3 with Polly | ||
node.sPollyState = "transitioning"; | ||
node.sSonosPlayState = "transitioning"; | ||
Leggi(sMsg); | ||
// Remove the TTS message from the queue | ||
node.aMessageQueue.splice(0, 1); | ||
}).catch(err => { | ||
try { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: node.sSonosIPAddress + " Error pausing: " + err }); | ||
} catch (error) { } | ||
node.sPollyState = "transitioning"; | ||
node.sSonosPlayState = "transitioning"; | ||
node.setNodeStatus({ | ||
fill: 'yellow', | ||
shape: 'dot', | ||
text: 'preparing...' | ||
}); | ||
// Create the TTS mp3 with Polly | ||
Leggi(sMsg, node); | ||
// Set timeout | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 500); | ||
// 15/11/2019 Workaround for grouping | ||
try { | ||
var sMsg = node.aMessageQueue[0]; | ||
node.aMessageQueue.splice(0, 1); // Remove the TTS message from the queue | ||
} catch (error) { | ||
} | ||
// Create the TTS mp3 with Polly | ||
node.sPollyState = "transitioning"; | ||
node.sSonosPlayState = "transitioning"; | ||
Leggi(sMsg); | ||
}); | ||
} else if (node.sSonosPlayState == "playing" && node.sSonosTrackTitle.toLocaleLowerCase().indexOf(".mp3") == -1) { | ||
} else { | ||
// Reset status | ||
node.setNodeStatus({ fill: "green", shape: "dot", text: "" + node.sSonosPlayState }); | ||
} | ||
RED.log.info('SonosPollyTTS: HandleQueue2 - stopping: ' + node.sSonosPlayState + " Track:" + node.sSonosTrackTitle); | ||
} else { | ||
// It's playing something. Stop | ||
node.SonosClient.pause().then(success => { | ||
RED.log.info('SonosPollyTTS: HandleQueue2 - stopped: ' + success + " " + node.sSonosPlayState + " Track:" + node.sSonosTrackTitle); | ||
try { | ||
var sMsg = node.aMessageQueue[0]; | ||
node.aMessageQueue.splice(0, 1); // Remove the TTS message from the queue | ||
} catch (error) { | ||
// 07/05/2019 Check if i have ended playing the queue as well | ||
try { | ||
if (node.msg.completed == false && node.sSonosPlayState == "stopped") { | ||
node.msg.completed = true; | ||
node.ungroupSpeakers(); // 20/03/2020 Ungroup Speakers | ||
node.send(node.msg); | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "" + node.sSonosPlayState }); | ||
} | ||
// Create the TTS mp3 with Polly | ||
node.sPollyState = "transitioning"; | ||
node.sSonosPlayState = "transitioning"; | ||
Leggi(sMsg, node); | ||
} catch (error) { } | ||
// Start the TTS queue timer | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 500); | ||
} | ||
} | ||
}).catch(err => { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: node.sSonosIPAddress + " Error pausing: " + err }); | ||
// 15/11/2019 Workaround for grouping | ||
try { | ||
var sMsg = node.aMessageQueue[0]; | ||
node.aMessageQueue.splice(0, 1); // Remove the TTS message from the queue | ||
} catch (error) { | ||
} | ||
// Create the TTS mp3 with Polly | ||
node.sPollyState = "transitioning"; | ||
node.sSonosPlayState = "transitioning"; | ||
Leggi(sMsg, node); | ||
// Reas the text via Polly | ||
function Leggi(msg) { | ||
// Set timeout | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 500); | ||
}); | ||
try { | ||
node.setNodeStatus({ fill: "yellow", shape: "dot", text: "Leggi: " + msg }); | ||
} catch (error) { } | ||
} else { | ||
// Reset status | ||
node.setNodeStatus({ fill: "green", shape: "dot", text: "" + node.sSonosPlayState }); | ||
// Start the TTS queue timer | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 500); | ||
// Play directly files starting with http:// | ||
if (msg.toLowerCase().startsWith("http://")) { | ||
RED.log.info('SonosPollyTTS: Leggi HTTP filename: ' + msg); | ||
PlaySonos(msg, node); | ||
return; | ||
} | ||
} else { | ||
// Start the TTS queue timer | ||
node.oTimer = setTimeout(function () { HandleQueue(node); }, 500); | ||
// 27/02/2020 Handling OwnFile | ||
if (msg.indexOf("OwnFile_") !== -1) { | ||
RED.log.info('SonosPollyTTS: OwnFile .MP3, skip polly, filename: ' + msg); | ||
var newPath = node.userDir + "/ttspermanentfiles/" + msg; | ||
PlaySonos(newPath, node); | ||
return; | ||
} | ||
// 07/05/2019 Check if i have ended playing the queue as well | ||
if (node.msg.completed == false && node.sSonosPlayState == "stopped") { | ||
node.msg.completed = true; | ||
node.ungroupSpeakers(); // 20/03/2020 Ungroup Speakers | ||
node.send(node.msg); | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "" + node.sSonosPlayState }); | ||
// 09/03/2020 Handling Hailing_ files | ||
if (msg.indexOf("Hailing_") !== -1) { | ||
RED.log.info('SonosPollyTTS: Hailing .MP3, skip polly, filename: ' + msg); | ||
var newPath = node.userDir + "/hailingpermanentfiles/" + msg; | ||
PlaySonos(newPath, node); | ||
return; | ||
} | ||
} | ||
} | ||
// Otherwise, it's a TTS | ||
var outputFormat = "mp3"; | ||
var filename = getFilename(msg, node.iVoice, node.ssml, outputFormat); | ||
// Get real filename, codified. | ||
filename = node.userDir + "/ttsfiles/" + filename; | ||
// Check if cached | ||
if (fs.existsSync(filename)) { | ||
node.setNodeStatus({ fill: 'green', shape: 'ring', text: 'from cache' }); | ||
RED.log.info('SonosPollyTTS: DEBUG - fromcache : ' + filename); | ||
PlaySonos(filename, node); | ||
return; | ||
} | ||
// Reas the text via Polly | ||
function Leggi(msg, node) { | ||
// Not cached | ||
node.setNodeStatus({ fill: 'yellow', shape: 'dot', text: 'asking online' }); | ||
// Play directly files starting with http:// | ||
if (msg.toLowerCase().startsWith("http://")) { | ||
RED.log.info('SonosPollyTTS: Leggi HTTP filename: ' + msg); | ||
PlaySonos(msg, node); | ||
return; | ||
} | ||
var params = { | ||
OutputFormat: outputFormat, | ||
SampleRate: '22050', | ||
Text: msg, | ||
TextType: node.ssml ? 'ssml' : 'text', | ||
VoiceId: node.iVoice | ||
}; | ||
// 27/02/2020 Handling OwnFile | ||
if (msg.indexOf("OwnFile_") !== -1) { | ||
RED.log.info('SonosPollyTTS: OwnFile .MP3, skip polly, filename: ' + msg); | ||
var newPath = node.userDir + "/ttspermanentfiles/" + msg; | ||
PlaySonos(newPath, node); | ||
return; | ||
} | ||
var polly = node.Pollyconfig.polly; | ||
synthesizeSpeech([polly, params]).then(data => { return [filename, data.AudioStream]; }).then(cacheSpeech).then(function () { | ||
// Play | ||
PlaySonos(filename, node); | ||
// 09/03/2020 Handling Hailing_ files | ||
if (msg.indexOf("Hailing_") !== -1) { | ||
RED.log.info('SonosPollyTTS: Hailing .MP3, skip polly, filename: ' + msg); | ||
var newPath = node.userDir + "/hailingpermanentfiles/" + msg; | ||
PlaySonos(newPath, node); | ||
return; | ||
} | ||
}).catch(error => { notifyError(filename, error); }); | ||
// Otherwise, it's a TTS | ||
var outputFormat = "mp3"; | ||
var filename = getFilename(msg, node.iVoice, node.ssml, outputFormat); | ||
// Get real filename, codified. | ||
filename = node.userDir + "/ttsfiles/" + filename; | ||
// Check if cached | ||
if (fs.existsSync(filename)) { | ||
node.setNodeStatus({ fill: 'green', shape: 'ring', text: 'from cache' }); | ||
RED.log.info('SonosPollyTTS: DEBUG - fromcache : ' + filename); | ||
PlaySonos(filename, node); | ||
return; | ||
} | ||
// Not cached | ||
node.setNodeStatus({ fill: 'yellow', shape: 'dot', text: 'asking online' }); | ||
var params = { | ||
OutputFormat: outputFormat, | ||
SampleRate: '22050', | ||
Text: msg, | ||
TextType: node.ssml ? 'ssml' : 'text', | ||
VoiceId: node.iVoice | ||
}; | ||
var polly = node.Pollyconfig.polly; | ||
synthesizeSpeech([polly, params]).then(data => { return [filename, data.AudioStream]; }).then(cacheSpeech).then(function () { | ||
// Play | ||
PlaySonos(filename, node); | ||
}).catch(error => { notifyError(node, filename, error); }); | ||
} | ||
function synthesizeSpeech([polly, params]) { | ||
return new Promise((resolve, reject) => { | ||
polly.synthesizeSpeech(params, function (err, data) { | ||
if (err !== null) { | ||
return reject(err); | ||
} | ||
resolve(data); | ||
function synthesizeSpeech([polly, params]) { | ||
return new Promise((resolve, reject) => { | ||
polly.synthesizeSpeech(params, function (err, data) { | ||
if (err !== null) { | ||
return reject(err); | ||
} | ||
resolve(data); | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
function cacheSpeech([path, data]) { | ||
return new Promise((resolve, reject) => { | ||
//RED.log.info("cacheSpeech path " + path); | ||
fs.writeFile(path, data, function (err) { | ||
if (err !== null) return reject(err); | ||
resolve(); | ||
function cacheSpeech([path, data]) { | ||
return new Promise((resolve, reject) => { | ||
//RED.log.info("cacheSpeech path " + path); | ||
fs.writeFile(path, data, function (err) { | ||
if (err !== null) return reject(err); | ||
resolve(); | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
function getFilename(text, _iVoice, isSSML, extension) { | ||
// Slug the text. | ||
var basename = slug(text); | ||
function getFilename(text, _iVoice, isSSML, extension) { | ||
// Slug the text. | ||
var basename = slug(text); | ||
var ssml_text = isSSML ? '_ssml' : ''; | ||
var ssml_text = isSSML ? '_ssml' : ''; | ||
// Filename format: "text_voice.mp3" | ||
var filename = util.format('%s_%s%s.%s', basename, _iVoice, ssml_text, extension); | ||
// Filename format: "text_voice.mp3" | ||
var filename = util.format('%s_%s%s.%s', basename, _iVoice, ssml_text, extension); | ||
// If filename is too long, cut it and add hash | ||
if (filename.length > 250) { | ||
var hash = MD5(basename); | ||
// If filename is too long, cut it and add hash | ||
if (filename.length > 250) { | ||
var hash = MD5(basename); | ||
// Filename format: "text_hash_voice.mp3" | ||
var ending = util.format('_%s_%s%s.%s', hash, _iVoice, ssml_text, extension); | ||
var beginning = basename.slice(0, 250 - ending.length); | ||
// Filename format: "text_hash_voice.mp3" | ||
var ending = util.format('_%s_%s%s.%s', hash, _iVoice, ssml_text, extension); | ||
var beginning = basename.slice(0, 250 - ending.length); | ||
filename = beginning + ending; | ||
filename = beginning + ending; | ||
} | ||
return filename; | ||
} | ||
return filename; | ||
} | ||
function notifyError(msg, err) { | ||
var errorMessage = err.message; | ||
// Output error to console | ||
//RED.log.error('SonosPollyTTS synthesizeSpeech: ' + errorMessage); | ||
// Mark node as errounous | ||
node.setNodeStatus({ | ||
fill: 'red', | ||
shape: 'dot', | ||
text: 'notifyError: ' + errorMessage | ||
}); | ||
//node.sPollyState == "criticalwriting"; | ||
// RED.log.error('SonosPollyTTS: notifyError - unable to write TTS file. Check user permissions'); | ||
//RED.log.error('SonosPollyTTS: notifyError - msg: ' + msg + ' error: ' + errorMessage); | ||
// Set error in message | ||
msg.error = errorMessage; | ||
function notifyError(node, msg, err) { | ||
var errorMessage = err.message; | ||
// Output error to console | ||
//RED.log.error('SonosPollyTTS synthesizeSpeech: ' + errorMessage); | ||
// Mark node as errounous | ||
node.setNodeStatus({ | ||
fill: 'red', | ||
shape: 'dot', | ||
text: 'Error: ' + errorMessage | ||
}); | ||
node.sPollyState == "criticalwriting"; | ||
// RED.log.error('SonosPollyTTS: notifyError - unable to write TTS file. Check user permissions'); | ||
RED.log.error('SonosPollyTTS: notifyError - msg: ' + msg + ' error: ' + errorMessage); | ||
// Set error in message | ||
msg.error = errorMessage; | ||
} | ||
} | ||
@@ -1144,27 +1179,36 @@ | ||
// ---------------------- SONOS ---------------------------- | ||
function PlaySonos(_songuri, node) { | ||
// ---------------------- SONOS ---------------------------- | ||
function PlaySonos(_songuri, node) { | ||
var sUrl = ""; | ||
var sUrl = ""; | ||
// Play directly files starting with http:// | ||
if (_songuri.toLowerCase().startsWith("http://")) { | ||
sUrl = _songuri; | ||
} else { | ||
sUrl = node.sNoderedURL + "/tts/tts.mp3?f=" + encodeURIComponent(_songuri); | ||
} | ||
// Play directly files starting with http:// | ||
if (_songuri.toLowerCase().startsWith("http://")) { | ||
sUrl = _songuri; | ||
} else { | ||
sUrl = node.sNoderedURL + "/tts/tts.mp3?f=" + encodeURIComponent(_songuri); | ||
} | ||
node.SonosClient.setVolume(node.sSonosVolume).then(success => { | ||
node.SonosClient.setAVTransportURI(sUrl).then(playing => { | ||
node.SonosClient.setVolume(node.sSonosVolume).then(success => { | ||
node.SonosClient.setAVTransportURI(sUrl).then(playing => { | ||
// Polly has ended downloading file | ||
node.sPollyState = "done"; | ||
// Signalling | ||
node.setNodeStatus({ | ||
fill: 'green', | ||
shape: 'dot', | ||
text: 'Playing' | ||
}); | ||
// Polly has ended downloading file | ||
node.sPollyState = "done"; | ||
// Signalling | ||
node.setNodeStatus({ | ||
fill: 'green', | ||
shape: 'dot', | ||
text: 'Playing' | ||
}).catch(err => { | ||
// Polly has ended downloading file | ||
node.sPollyState = "done"; | ||
// Signalling | ||
node.setNodeStatus({ | ||
fill: 'red', | ||
shape: 'dot', | ||
text: 'Error Transport' | ||
}); | ||
}); | ||
}).catch(err => { | ||
@@ -1177,55 +1221,19 @@ // Polly has ended downloading file | ||
shape: 'dot', | ||
text: 'Error Transport' | ||
text: 'Error SetVolume' | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
// 27/11/2019 Check Sonos connection healt | ||
function CheckSonosConnection(node) { | ||
node.SonosClient.getCurrentState().then(state => { | ||
node.SonosClient.currentTrack().then(track => { | ||
// 27/11/2019 Set node output to signal connectio error | ||
if (node.msg.connectionerror == true) { | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Sonos is connected." }); | ||
node.msg.connectionerror = false; | ||
node.send({ "connectionerror": node.msg.connectionerror }); | ||
} | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { CheckSonosConnection(node); }, 2000); | ||
}).catch(err => { | ||
// 27/11/2019 Set node output to signal connectio error | ||
if (node.msg.connectionerror == false) { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: "Sonos connection is DOWN: " + err }); | ||
node.msg.connectionerror = true; | ||
node.send({ "connectionerror": node.msg.connectionerror }); | ||
} | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { CheckSonosConnection(node); }, 2000); | ||
}); | ||
}).catch(err => { | ||
// 27/11/2019 Set node output to signal connectio error | ||
if (node.msg.connectionerror == false) { | ||
node.setNodeStatus({ fill: "red", shape: "dot", text: "Sonos connection is DOWN: " + err }); | ||
node.msg.connectionerror = true; | ||
node.send({ "connectionerror": node.msg.connectionerror }); | ||
} | ||
node.oTimerSonosConnectionCheck = setTimeout(function () { CheckSonosConnection(node); }, 2000); | ||
}); | ||
} | ||
RED.nodes.registerType('sonospollytts', PollyNode); | ||
} |
702617
119
1283
+ Addedfor-each@0.3.4(transitive)
+ Addedsonos@1.14.0(transitive)
- Removedfor-each@0.3.3(transitive)
- Removedsonos@1.13.0(transitive)
Updatedsonos@1.14.0