node-red-contrib-sonospollytts
Advanced tools
Comparing version 1.1.37 to 1.1.38
@@ -6,6 +6,11 @@ ![Sample Node](img/logo.png) | ||
<p> | ||
<b>Version 1.1.37</b> November 2020<br/> | ||
<b>Version 1.1.38</b> December 2020<br/> | ||
- NEW: resume music queue after TTS speech. Once finished playing the voice speak, the music queue restart at the exact position, at the exact track time.<br/> | ||
- BUGFIX: fixing some issue when inbound msg to the node are very frequent. Should resume the music correctly.<br/> | ||
</p> | ||
<p> | ||
<b>Version 1.1.37</b> December 2020<br/> | ||
- NEW: resume music queue after TTS speech. Once finished playing the voice speak, the music queue restart at the exact position, at the exact track time.<br/> | ||
</p> | ||
<p> | ||
<b>Version 1.1.36</b> November 2020<br/> | ||
@@ -12,0 +17,0 @@ - Whenever node-red is restarted or you make a deploy while Sonos is playing music, the node won't stop Sonos players anymore.<br/> |
{ | ||
"name": "node-red-contrib-sonospollytts", | ||
"version": "1.1.37", | ||
"version": "1.1.38", | ||
"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.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -0,1 +1,4 @@ | ||
const { clear } = require('console'); | ||
const { clearInterval } = require('timers'); | ||
module.exports = function (RED) { | ||
@@ -78,3 +81,9 @@ 'use strict'; | ||
node.sNoderedURL = ""; | ||
node.oTimerGetCurrentQueue = null; // 04/12/2020 | ||
node.oTimerResumeCurrentQueue = null; // 04/12/2020 | ||
node.currMusicTrack = null; // 04/12/2020 Current position of the currently playing music in the queue | ||
node.busyGettingMusicQueue = false; // 04/12/2020 Signal busy getting usic queue | ||
node.busyResumingMusicQueue = false; // 04/12/2020 Signal busy in resuming music queue | ||
node.tempMSGStorage = []; // 04/12/2020 Temporary stores the flow messages | ||
if (typeof node.server !== "undefined" && node.server !== null) { | ||
@@ -670,4 +679,13 @@ node.sNoderedURL = node.server.sNoderedURL || ""; | ||
// Start the TTS queue timer | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 5000); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 2000); | ||
// 04/12/2020 Start timer that read the music queue | ||
node.oTimerGetCurrentQueue = setInterval(function () { | ||
getMusicQueue().then(resolve => { | ||
}).catch(err => { | ||
}); | ||
}, 5000); | ||
// 27/11/2019 Start the connection healty check | ||
@@ -735,3 +753,2 @@ node.oTimerSonosConnectionCheck = setTimeout(function () { node.CheckSonosConnection(); }, 5000); | ||
node.curTrack; | ||
node.on('input', function (msg) { | ||
@@ -744,43 +761,16 @@ | ||
// 04/12/2020 Handle music queue | ||
//#region "HANDLE MUSIC QUEUE" | ||
node.SonosClient.getCurrentState().then(state => { | ||
//console.log("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" + node.msg.completed) | ||
// A music queue is playing and no TTS is speaking? | ||
if (state.toString().toLowerCase() === "playing" && node.msg.completed === true) { | ||
//console.log("AAaaaaAAaaaaAAaaaaAAaaaaAAaaaaAAaaaaAAaaaa") | ||
// Get current track | ||
node.SonosClient.currentTrack().then(track => { | ||
//console.log("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") | ||
node.currMusicTrack = track;// .queuePosition || 1; // Get the current track in the queue. | ||
node.SonosClient.getVolume().then(volume => { | ||
//console.log("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC") | ||
node.currMusicTrack.currentVolume = volume; // Get the current volume | ||
console.log("VOLUMA " + volume); | ||
continueProcessingInputMSG(); | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
console.log('SonospollyTTS: getVolume Error occurred %j', err); | ||
continueProcessingInputMSG(); | ||
}) | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
console.log('SonospollyTTS: Error currentTrackoccurred %j', err); | ||
continueProcessingInputMSG(); | ||
}) | ||
} else { | ||
node.currMusicTrack = null; | ||
continueProcessingInputMSG(); | ||
}; | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
console.log('SonospollyTTS: getCurrentState: Error occurred %j', err); | ||
continueProcessingInputMSG(); | ||
}) | ||
//#endregion | ||
// 04/12/2020 | ||
// If the queue management is still in process (starting or stopping the queue), store the input messages and send it upon end managing the queue | ||
if (node.busyGettingMusicQueue || node.busyResumingMusicQueue) { | ||
node.tempMSGStorage.push(msg.payload.toString()); | ||
if (node.busyGettingMusicQueue) node.setNodeStatus({ fill: 'grey', shape: 'dot', text: 'Busy in reading music queue...retry' }); | ||
if (node.busyResumingMusicQueue) node.setNodeStatus({ fill: 'grey', shape: 'dot', text: 'Busy in resuming music queue...retry' }); | ||
return; | ||
} | ||
// Clear the read music queue timer | ||
if (node.oTimerGetCurrentQueue !== null) clearTimeout(node.oTimerGetCurrentQueue); | ||
node.busyGettingMusicQueue = false; // Resetting the glaf indicating busy in read music queue | ||
// Continue processing the input command | ||
function continueProcessingInputMSG() { | ||
// 12/06/2018 Controllo se il payload è un'impostazione del volume | ||
// 12/06/2018 Controllo se il payload è un'impostazione del volume | ||
if (msg.hasOwnProperty("volume")) { | ||
@@ -803,16 +793,2 @@ node.sSonosVolume = msg.volume; | ||
node.sonoshailing = config.sonoshailing; | ||
// Backwart compatibiliyy, to remove with the next Version | ||
// ################ | ||
if (node.sonoshailing == "0") { | ||
// Remove the hailing.mp3 default file | ||
RED.log.info('SonosPollyTTS: Hailing disabled'); | ||
} else if (node.sonoshailing == "1") { | ||
node.sonoshailing = "Hailing_Hailing.mp3"; | ||
} else if (node.sonoshailing == "2") { | ||
node.sonoshailing = "Hailing_ComputerCall.mp3"; | ||
} else if (node.sonoshailing == "3") { | ||
node.sonoshailing = "Hailing_VintageSpace.mp3"; | ||
} | ||
// ################ | ||
} | ||
@@ -842,4 +818,2 @@ | ||
node.setNodeStatus({ fill: 'yellow', shape: 'dot', text: 'Queued ' + msg.payload }); | ||
} | ||
@@ -866,2 +840,117 @@ }); | ||
// 04/12/2020 | ||
function getMusicQueue() { | ||
return new Promise(function (resolve, reject) { | ||
// 04/12/2020 Get music queue | ||
if (!node.msg.completed) { | ||
resolve(true); | ||
return; // Is playing TTS, exit. | ||
} | ||
node.busyGettingMusicQueue = true; // Avoid enterint here while the promises below are not resolved | ||
node.SonosClient.getCurrentState().then(state => { | ||
// A music queue is playing and no TTS is speaking? | ||
if (state.toString().toLowerCase() === "playing") { | ||
if (!node.msg.completed) { | ||
// Is playing TTS, exit. | ||
node.currMusicTrack = null; | ||
node.busyGettingMusicQueue = false; // Avoid enterint here while the promises below are not resolved | ||
reject(true); | ||
return; | ||
} | ||
// Get current track | ||
node.SonosClient.currentTrack().then(track => { | ||
if (!node.msg.completed) { | ||
// Is playing TTS, exit. | ||
node.currMusicTrack = null; | ||
node.busyGettingMusicQueue = false; // Avoid enterint here while the promises below are not resolved | ||
reject(true); | ||
return; | ||
} | ||
node.currMusicTrack = track;// .queuePosition || 1; // Get the current track in the queue. | ||
node.SonosClient.getVolume().then(volume => { | ||
if (!node.msg.completed) { | ||
// Is playing TTS, exit. | ||
node.currMusicTrack = null; | ||
node.busyGettingMusicQueue = false; // Avoid enterint here while the promises below are not resolved | ||
reject(true); | ||
return; | ||
} | ||
node.currMusicTrack.currentVolume = volume; // Get the current volume | ||
node.busyGettingMusicQueue = false; // finish handling music queue | ||
//console.log("TRACK MUSIC: " + JSON.stringify(node.currMusicTrack)); | ||
node.setNodeStatus({ fill: 'grey', shape: 'dot', text: 'Playing music queue pos: ' + node.currMusicTrack.queuePosition }); | ||
resolve(true); | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
node.busyGettingMusicQueue = false; // finish handling music queue | ||
//console.log('SonospollyTTS: getVolume Error occurred %j', err); | ||
reject(err); | ||
}) | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
node.busyGettingMusicQueue = false; // finish handling music queue | ||
reject(err); | ||
//console.log('SonospollyTTS: Error currentTrackoccurred %j', err); | ||
}) | ||
} else { | ||
node.currMusicTrack = null; // Avoid play last queue at end of the tts speech | ||
node.busyGettingMusicQueue = false; // finish handling music queue | ||
resolve(true); | ||
}; | ||
}).catch(err => { | ||
//console.log('SonospollyTTS: getCurrentState: Error occurred %j', err); | ||
node.busyGettingMusicQueue = false; // finish handling music queue | ||
reject(err); | ||
}) | ||
}); | ||
} | ||
// 04/12/2020 | ||
function resumeMusicQueue() { | ||
return new Promise(function (resolve, reject) { | ||
if (node.currMusicTrack !== null) { | ||
node.SonosClient.selectQueue().then(success => { | ||
node.SonosClient.selectTrack(node.currMusicTrack.queuePosition).then(success => { | ||
node.SonosClient.seek(node.currMusicTrack.position).then(success => { | ||
node.SonosClient.setVolume(node.currMusicTrack.currentVolume).then(success => { | ||
node.SonosClient.play().then(success => { | ||
node.busyResumingMusicQueue = false; | ||
resolve(true); | ||
}).catch(err => { | ||
//console.log('Error occurred PLAY %j', err) | ||
node.busyResumingMusicQueue = false; | ||
reject(Err); | ||
}) | ||
}).catch(err => { | ||
//console.log('Error occurred setVolume %j', err) | ||
snode.busyResumingMusicQueue = false; | ||
reject(Err); | ||
}) | ||
}).catch(err => { | ||
//console.log('Error occurred SEEK %j', err) | ||
node.busyResumingMusicQueue = false; | ||
reject(Err); | ||
}) | ||
}).catch(err => { | ||
//console.log('Error occurred SELECTTRACK %j', err); | ||
node.busyResumingMusicQueue = false; | ||
reject(Err); | ||
}) | ||
}).catch(err => { | ||
//console.log('Error occurred %j', err); | ||
node.busyResumingMusicQueue = false; | ||
reject(Err); | ||
}) | ||
} else { | ||
node.busyResumingMusicQueue = false; // 04/12/2020 Signal end in resuming queue | ||
resolve(true); | ||
} | ||
}); | ||
} | ||
// Handle the queue | ||
@@ -877,6 +966,22 @@ function HandleQueue() { | ||
// try { | ||
// node.setNodeStatus({ fill: "yellow", shape: "dot", text: "Queue: " + node.sPollyState }); | ||
// } catch (error) { } | ||
// 04/12/220 Busy handling music queue? | ||
if (node.busyGettingMusicQueue || node.busyResumingMusicQueue) { | ||
if (node.busyGettingMusicQueue) node.setNodeStatus({ fill: 'grey', shape: 'dot', text: 'Busy in reading music queue...retry' }); | ||
if (node.busyResumingMusicQueue) node.setNodeStatus({ fill: 'grey', shape: 'dot', text: 'Busy in resuming music queue...retry' }); | ||
node.oTimer = setTimeout(function () { HandleQueue(); }, 2000); | ||
return; | ||
} | ||
// 04/12/2020 if there are flows messages to be handled, handles it | ||
if (node.tempMSGStorage.length > 0) { | ||
for (let index = 0; index < node.tempMSGStorage.length; index++) { | ||
const element = node.tempMSGStorage[index]; | ||
node.aMessageQueue.push(element); | ||
setTimeout(() => { | ||
node.setNodeStatus({ fill: 'green', shape: 'dot', text: 'Queued from flow: ' + element }); | ||
}, 1000); | ||
} | ||
node.tempMSGStorage = []; // Flush the array | ||
} | ||
if (node.sPollyState == "transitioning") { | ||
@@ -1028,39 +1133,55 @@ node.iTimeoutPollyState += 1; // Increase Timeout | ||
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 }); | ||
if (node.currMusicTrack === null) { | ||
node.ungroupSpeakers(); // 20/03/2020 Ungroup Speakers | ||
node.msg.completed = true; | ||
node.send(node.msg); | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Done." }); | ||
return; | ||
} | ||
//console.log("PANAMA PANAMA PANAMA PANAMA PANAMA PANAMA " + node.busyGettingMusicQueue + " " + node.busyResumingMusicQueue) | ||
if (node.busyGettingMusicQueue || node.busyResumingMusicQueue) { | ||
if (node.oTimerResumeCurrentQueue !== null) clearTimeout(node.oTimerResumeCurrentQueue); | ||
// Resume the music queue at exact position and seek to exact time | ||
if (node.currMusicTrack !== null) { | ||
node.SonosClient.selectQueue().then(success => { | ||
node.SonosClient.selectTrack(node.currMusicTrack.queuePosition).then(success => { | ||
node.SonosClient.seek(node.currMusicTrack.position).then(success => { | ||
node.SonosClient.setVolume(node.currMusicTrack.currentVolume).then(success => { | ||
node.SonosClient.play().then(success => { | ||
node.currMusicTrack = null; | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
console.log('Error occurred PLAY %j', err) | ||
}) | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
console.log('Error occurred setVolume %j', err) | ||
}) | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
console.log('Error occurred SEEK %j', err) | ||
}) | ||
node.oTimerResumeCurrentQueue = setTimeou(() => { | ||
resumeMusicQueue().then(success => { | ||
if (node.oTimerResumeCurrentQueue !== null) clearInterval(node.oTimerResumeCurrentQueue); | ||
node.msg.completed = true; | ||
node.send(node.msg); | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Done." }); | ||
}).catch(err => { | ||
node.currMusicTrack = null; | ||
console.log('Error occurred SELECTTRACK %j', err) | ||
}) | ||
if (node.oTimerResumeCurrentQueue !== null) clearInterval(node.oTimerResumeCurrentQueue); | ||
node.msg.completed = true; | ||
node.send(node.msg); | ||
node.busyResumingMusicQueue = false; | ||
node.busyGettingMusicQueue = false; | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Done." }); | ||
}).catch(err => { console.log('Error occurred %j', err) }) | ||
}); | ||
}, 3000) | ||
} else { | ||
if (node.oTimerResumeCurrentQueue !== null) clearTimeout(node.oTimerResumeCurrentQueue); | ||
resumeMusicQueue().then(success => { | ||
node.msg.completed = true; | ||
node.send(node.msg); | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Resuming queue." }); | ||
}).catch(err => { | ||
node.msg.completed = true; | ||
node.send(node.msg); | ||
node.busyResumingMusicQueue = false; | ||
node.busyGettingMusicQueue = false; | ||
node.setNodeStatus({ fill: "green", shape: "ring", text: "Error resuming queue." }); | ||
}); | ||
} | ||
} | ||
} catch (error) { } | ||
} catch (error) { | ||
} | ||
// Resume Queue | ||
@@ -1067,0 +1188,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
866939
1589