Text To Speech (TTS) With Sonos - Creating Dynamic Messages


As i was keen to give me Veralite the image of some verbal intelligence @Guessed helped me with an earlier version of the Sonos Plugin to create some dynamic speech (See here http://forum.micasaverde.com/index.php/topic,11269.0.html)

I’m going to update this first section with any new dynamic ‘say’ commands i’ve pulled together.

The Talking Sonos Clock

Period Of The Day Welcome

Check The Status Of The Lights

Random Words

Simon Says Game

All Zones Message


What’s The Temperature ?

This was my first one and testing it again, it still works, and it’s great how Google & Sonos can tell me the temperatures of my kids rooms now…

[code]url = require(“socket.url”)

local AV_DEV = 5
local LS_SID = “urn:micasaverde-com:serviceId:Sonos1”
local MN_SID = “urn:micasaverde-com:serviceId:MediaNavigation1”

local TEMP_SID = “urn:upnp-org:serviceId:TemperatureSensor1”
local benTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 42)
local emmaTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 48)
local lang = “en”
local speak = url.escape(string.format("Ben’s Room is currently at %s degrees, and emmers is at %s ", benTemp, emmaTemp))

luup.call_action(LS_SID, “SetURIToPlay”, {URIToPlay = string.format(“x-rincon-mp3radio://translate.google.com/translate_tts?tl=%s&q=%s”, lang, speak)},AV_DEV)
luup.call_action(MN_SID, “Play”, {},AV_DEV)[/code]

Feel free to evolve this script and share any examples you might create here for others to use.

You might want to try the “[tt]Say[/tt]” action, of the [tt]Sonos1[/tt] SID, directly. It’ll handle building the URL, the appropriate URL value encoding, and the “[tt]Play[/tt]” action all internally. You’ll just need to tell it to stop, since it will play in a loop…

The last 3 lines will be replaced with something akin to:

luup.call_action(LS_SID, "Say", {Text = string.format("Ben's Room is currently at %s degrees, and emmers is at %s ", benTemp, emmaTemp)}, AV_DEV)

Thanks @Guessed

Very Nice ! (And much less cluttered now…)

[code]url = require(“socket.url”)

local AV_DEV = 5
local LS_SID = “urn:micasaverde-com:serviceId:Sonos1”

local TEMP_SID = “urn:upnp-org:serviceId:TemperatureSensor1”
local benTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 42)
local emmaTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 48)

luup.call_action(LS_SID, “Say”, {Text = string.format("Ben’s Room is currently at %s degrees, and Emmers is at %s ", benTemp, emmaTemp)}, AV_DEV)[/code]

@Guessed I’ve had a quick go at getting dates, but I only can get the unix ones. To help build dynamic message, shoul someone want their Songle* to “say” that a particular device was “last updated on 6th of November”, are you aware of anything that could convert the unix clock to words and not numerical values that could then be spoken?

A search gave me this, but it’s not something I can work out. http://forum.micasaverde.com/index.php/topic,5381.msg30660.html#msg30660

Also to do this I assume I would need to create some kind of conversion table etc. e.g 4 = April?

* = I’m testing out names for the Sonos/Google TTS set up :wink:


I found this online - which seems interesting - http://lua-users.org/wiki/DateFormattingFunctions

For the first one, Lua’s [tt]os.date[/tt] accepts a string based format “pattern” that is used to convert the number into something of your choice.

A more complete list of the format elements can be found in pages like:

You’d have to test to see which ones are really available.

Thanks to Guessed for the os.date link, and now here’s another TTS to share. This one is called…

The Talking Sonos Clock:wink:

[code]url = require(“socket.url”)

local AV_DEV = 5
local LS_SID = “urn:micasaverde-com:serviceId:Sonos1”

local TEMP_SID = “urn:upnp-org:serviceId:TemperatureSensor1”
local time = os.date(’%M minutes past %I:’)

luup.call_action(LS_SID, “Say”, {Text = string.format(“The time is currently %s”, time)}, AV_DEV)[/code]

More to follow.

Now that you’re using the built-in [tt]Say[/tt] action, you can strip out the need for the “require” call at the top (it’s effectively inside the Sonos implementation)

For the use-case of the talking clock, you can also strip out the temperature declarations, so it’ll get even smaller.

All in all, it gets to be fairly small:

[code]local AV_DEV = 5
local LS_SID = “urn:micasaverde-com:serviceId:Sonos1”
local time = os.date(’%M minutes past %I:’)

luup.call_action(LS_SID, “Say”, {Text = string.format(“The time is currently %s”, time)}, AV_DEV)[/code]

Thanks @guessed, that’s what happens when you do things late at night, I missed the obvious :wink:

My next challenge is to try and create a script that will carry out a series of checks on something, and then report back on it’s findings. This way the resulting TTS could potentially be a different message depending on the results.

Sonos Status Reporting

For example

i) Check all temperature sensors are over 20c

If they are then say “all sensors are reporting 20c or above” but if they’re not, then say “not all sensors are reporting 20c or above”. - it might even be possible for me to get it to say which one is not so it’s more helpful and say “John’s Bedroom is reporting 18.5c”

Others could be to

ii) Check all security devices are armed etc …
iii) Check all lights are off etc…

To save the number of Google TTS requests needed, it might be better to just play an MP3 or WAV file from a NAS when everything is fine and just use the TTS for information/error verbal messages.

As always, all thoughts/ideas/input welcome…


It seems my new found coding skills have come up against a brick wall - I’m trying to check the status of 3 lights and depending on the results it should play a particular message. The logs seem to point to the very first line, I’m sure it’s obvious but I just can’t see it. :frowning:

[code]local LIGHT_ID1= 22
local LIGHT_ID2= 82
local LIGHT_ID3= 44
local AV_DEV = 5
local LS_SID = “urn:micasaverde-com:serviceId:Sonos1”
local SEC_SERV = “urn:upnp-org:serviceId:SwitchPower1”

local v = local function checkLightsOnOff()

local bedroomlight = luup.variable_get(SEC_SERV, "Status",LIGHT_ID1)
local halllight = luup.variable_get(SEC_SERV, "Status",LIGHT_ID2)
local loungelight = luup.variable_get(SEC_SERV, "Status",LIGHT_ID3)

if(bedroomlight == "0")then
if(halllight == "0")then
if(loungelight == "0")then

luup.log("v is " … v)
luup.call_action(LS_SID, “Say”, {Text = string.format(“All Lights Are Off”)}, AV_DEV)

return true


luup.call_action(LS_SID, “Say”, {Text = string.format(“One Or More Lights Are On”)}, AV_DEV)

return false



For one, there’s more if’s than end’s.

Did you try it for a single light first (i.e. start with fewer code lines), then expand from there?

Hi OTi

Thanks for responding.

Yes I tried it with just the one light and it still failed at the same place , the first line.
I pasted everything in just so people could see the whole thing that I’m trying to do and point out anything else that might be wrong …

Log shows…

01 11/10/12 15:06:29.897 LuaInterface::LoadCode: [string “local LIGHT_ID1= 22…”]:6: unexpected symbol near ‘local’ <0x2fadc680>
01 11/10/12 15:06:29.899 JobHandler_LuaUPnP::RunLua failed: local LIGHT_ID1= 22

If you have two voice command, how can we separate? Luup.sleep?

Any example to avoid long waiting to confirm the execution of a scene?

Thank you

Hi Matteo

My goal was to have Vera check 3 lights in the house, if one or more was on it would ‘say’ one type of message, but if no lights were on then it should say a different type of message.

I was just looking for it to just run the once, but I guess it could have luup.sleep added so it repeats the check every xxxx seconds?

I’m just not sure what I have done wrong that it seems to fail on the first line, no matter which local value I choose to put there.

If I try a piece of code that I know works, it seems to go through ok…
Which makes this even more strange??


If music or radio is already playing and I invoke a “say” command, is there a way or resuming whatever was playing?

Its not built in at this time, but its possible to do it if you read back through the main post.

Here you go, finally! Thanks for all the help from the Scene Scripting forum on getting this one to work in the end.

Check The Status Of The Lights

This one will check and tell you if all the lights are off or if one or more are still on.

[code]local LI_DEV1 = 82
local LI_DEV2 = 22
local LI_DEV3 = 53
local AV_DEV = 5
local LS_SID = “urn:micasaverde-com:serviceId:Sonos1”
local SEC_SERV = “urn:upnp-org:serviceId:SwitchPower1”

local livingroomlight = luup.variable_get(SEC_SERV, “Status”,LI_DEV2)
local bedroomlight = luup.variable_get(SEC_SERV, “Status”,LI_DEV1)
local halllight = luup.variable_get(SEC_SERV, “Status”,LI_DEV3)

if ((livingroomlight == “0”) and (bedroomlight == “0”) and (halllight == “0”)) then
luup.log(“All light are off”)
luup.call_action(LS_SID, “Say”, {Text = string.format("All lights are off ")}, AV_DEV)
luup.log(“One or more lights is on”)
luup.call_action(LS_SID, “Say”, {Text = string.format("One or more lights are on ")}, AV_DEV)

Another new one for anyone who is interested…

Sonos Period Of The Day Greetings

This one will check the time of day and then great you with either Good Morning, Good Afternoon or Good Evening

[code]url = require(“socket.url”)
local t = os.date(’*t’)

local AV_DEV = 5
local LS_SID = “urn:micasaverde-com:serviceId:Sonos1”
local current_second = t.hour * 3600 + t.min * 60 – number of sec since midnight
local morning = 0 * 3600 + 0 * 60 – 00:00
local afternoon = 12 * 3600 + 0 * 60 – 12:00
local evening = 18 * 3600 + 0 * 60 – 18:00
local name = “Chris”

if (current_second > morning) and (current_second < afternoon) then
luup.call_action(LS_SID, “Say”, {Text = string.format("Good Morning %s ", name)}, AV_DEV)

    if (current_second > afternoon) and (current_second < evening) then
         luup.call_action(LS_SID, "Say", {Text = string.format("Good Afternoon %s ", name)}, AV_DEV)

         luup.call_action(LS_SID, "Say", {Text = string.format("Good Evening %s ", name)}, AV_DEV)


Can it speak in Russian?

This suggests it does, http://googletranslate.blogspot.co.uk/2010/05/giving-voice-to-more-languages-on.html) - so have a go with the Sonos Plugin and try changing the language values in the ‘say’ command

If I give it this string:
Я хотел бы немного водки

It makes the attached TTS version of it. I have no idea if this is the correct translation, but the original english string, prior to auto-translation, was:
I would like some vodka

(rename attachment from Say.jpg to Say.mp3 to play it)

;D for the example.

Best Home Automation shopping experience. Shop at getvera!

© 2020 Vera Control Ltd., All Rights Reserved. Terms of Use | Privacy Policy | Forum Rules