Text To Speech - In The Cloud - Vera Verbal Notifications

I can’t wait to try it… !! Well Done You Two !

@teonebello - please can you past exactly what you code used as mine below fails, also did you address the delay issue that was mentioned by guessed?

local DEVICE_NO = 5 -- the official Sonos device number local LS_SID = "urn:micasaverde-com:serviceId:Sonos1" local MN_SID = "urn:micasaverde-com:serviceId:MediaNavigation1" local lul_temp = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature", 42)

luup.call_action(LS_SID, “SetURIToPlay”, {URIToPlay = “x-rincon-mp3radio://translate.google.com/translate_tts?tl=it&q=today+there+are+”…tostring( lul_temp )…“+Gradi”}, DEVICE_NO)
luup.call_action(MN_SID, “Play”, {}, DEVICE_NO)

@guessed, while we have your expertise on this post, one thing I’ve been trying to do is work out how to convert/encode the Device’s name (device params, name) so it can be placed within the TTS URL.

Ben’s Room Temperature = Ben%27s+Room+Temperature

Is there a Lua URL command/encoder or something that could be used for this?

Allowing us to achieve something like this - http://translate.google.com/translate_tts?tl=en&q=Ben's+Room+Temperature+Is+At+21+degrees+Celcius

URL Encoding can be done with this function:
LuaSocket: URL support

local url = require("socket.url")

but you’ll have to use the [tt]require[/tt] command to bring in the relevant part of the LuaSocket library that this lives in. LuaSocket is shipped with Vera.

The other thing you’ll find useful is [tt]string.format(…)[/tt]. It lets you do things like:

local out = string.format("The high temperature is %s, and the current temperature is %s", high_temp, curr_temp)

to format a string., which you can then feed into the url.escape(…) command, and then glue into the rest of your URL:

luup.call_action(LS_SID, "SetURIToPlay", {URIToPlay=string.format("x-rincon-mp3radio://translate.google.com/translate_tts?tl=%s&q=%s", "it", url.escape(out))}, DEVICE_NO)

where “it” is the parameter for language, so you’ll want to tweak that. It took me a while to work out what it was saying 8)

Thanks so much Guessed, although I feel reading your guidance I’m finding myself way out of my depth again, so much so I can’t even see the bottom. :wink:

So glad to see you’re onboard the Sonos bandwagon now, and hopefully with your input and huge experience, this TTS element can be worked into the actual Sonos plugin, so people (like me) can hopefully do this sort of thing via the scene/automation UI.

Ok, so putting it together, it’ll look something like:

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

local AV_DEV = 120
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 highTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 116)
local currTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 114)
local lang = “en”
local speak = url.escape(string.format(“The high will be %s degrees. Currently its %s degrees”, highTemp, currTemp))

luup.call_action(LS_SID, “SetAVTransportURI”,
{CurrentURI=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]

It takes we a little while to jump back-n-forth, as I’m currently re-doing the Sonos plugin to be more UPnP-generic. Then anything that’s UPnP AV could basically use the same code. As a result, I have different ServiceId’s etc, ones that are standard to AV-based UPnP calls (so I map my examples back to the current Sonos impl)

@guessed , Do you know what the difference is between the following…

urn:schemas-micasaverde-com:device:TemperatureSensor:1 and urn:upnp-org:serviceId:TemperatureSensor1

The temperature devices I have from the weather plugin or sensors linked to RFXcom, all seem to have the urn:schemas-micasaverde-com:device:TemperatureSensor:1 one?

Each device has a [single] deviceType, which [roughly] characterizes it as whatever it does.

Each device has multiple serviceId’s, one for each service it implements. All of the “state” (variables), and all of the “actions” (commands) are accessed, or called, via the serviceId’s on the device. This can definitely be a little confusing, but it’s all part of the UPnP spec.

Short version, if you want to “read a variable” from a device, or “call an action” of the device, then you need the appropriate ServiceId string for that variable. Typically these have :serviceId: in the middle of them, but what exactly that string looks like will be up to each [standardized] service.

We could add an action to the Sonos plugin that will take two arguments: the lang and the string to translate and then speech ?

Thanks guessed,

Looking at my temperature sensors, all of them have the device type down as urn:schemas-micasaverde-com:device:TemperatureSensor:1 which does not have a seviceId in it? Any idea what I am missing?

Also i’ve just tried your bit of code (under Aps/Develop Apps) and sadly for me it comes back as failed :frowning:

You won’t directly see the ServiceID’s. The most obvious thing you’ll see is the DeviceType.
If you hover your mouse over the Field/Variable titles for the block at the bottom, a bubble-hint will appear and show it’s serviceId.

My code about would need to be adapted to the DeviceID’s used in your system. I changed them all to point to those of my test system, notably Sonos (120), Weather High (116), Weather Current (114). If these aren’t correct, then it’ll error out, as “nil” values will come back and cause havoc.

[quote=“parkerc, post:28, topic:172156”]Thanks guessed,

Looking at my temperature sensors, all of them have the device type down as urn:schemas-micasaverde-com:device:TemperatureSensor:1 which does not have a seviceId in it? Any idea what I am missing?

Also i’ve just tried your bit of code (under Aps/Develop Apps) and sadly for me it comes back as failed :([/quote]

Exactly where I’m headed.

A new method, called “[tt]Say[/tt]”, which takes the [tt]Text[/tt] and [tt]Language[/tt] as parameters, and defaults if you don’t specify them. Before I do that, I need to finish up a boat load of other stuff I’ve been adding in to the API layer, so the “car is in bits” at the moment, and not ready to add this.

I’m trying to find all the API info I can to “queue” things up, and to play them immediately. It seems odd to have to use add, then play, then later stop. There has to be a better [UPnP] mechanism, and I’d need that before I add this stuff.

OK, that explains it, i was scratching my head as I’d already updated your device IDs to reflect mine;) In the end it looks like you were using your own test code, so when I referenced a few commands back to the current release etc. it worked…

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 highTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 35)
local currTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 33)
local lang = “en”
local speak = url.escape(string.format(“Good morning Chris, The high today will be %s degrees Celsius. Currently it’s %s degrees”, highTemp, currTemp))

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)

This is going to freak my girlfriend out, but using this with the Alarm Clock device, I have my wake up call for tomorrow now, oh and if you can work out what bit of code is need to reference the device name too, that would be amazing :wink:

In my latest checkin of the Sonos code, I’ve embedded this hack. No guarantee’s how long it will be before Google shut’s it down, but you can now do this:

[code]local AV_DEV = 121
local AV_SID = “urn:micasaverde-com:serviceId:Sonos1”

luup.call_action(AV_SID, “Say”, {Text=“Hello! Welcome home!”, Language=“en”, Volume=50}, AV_DEV)[/code]

And it’ll do what you expect. All of the encoding (etc) is done inside of the [tt]Say[/tt] action in order to simplify the callers. The [tt]Language[/tt] will default to “[tt]en[/tt]” if not specified, and the [tt]Volume[/tt] will internally default as well… so the only required parameter is “[tt]Text[/tt]” if you want english output.

Highly experimental, subject to change and all that.

Currently, it repeats over and over, which I have to work out how to address.

A revised version of the temperature reader would look like:

[code]local AV_DEV = 121
local AV_SID = “urn:micasaverde-com:serviceId:Sonos1”
local TEMP_SID = “urn:upnp-org:serviceId:TemperatureSensor1”

local highTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 116)
local currTemp = luup.variable_get(TEMP_SID,“CurrentTemperature”, 114)
luup.call_action(AV_SID, “Say”, {Text=string.format(“Good morning, The high will be %s degrees. Currently it’s %s degrees”, highTemp, currTemp)}, AV_DEV)[/code]

In my unit, it’s a little slow before this starts up.

NOTE: I’ve probably broken a LOT of other things in this rev of the Sonos code, since it’s getting an overhaul, so no promises there either 8)

If you look at the “Now Playing” display on your Sonos client when playing a radio/or tts stream, it has an ‘Information’ section, which for the TTS shows it will show “waiting to reconnect” for a second or two after it’s first played the TTS stream.

This (I think) is because Google only do a one time creation of your TTS audio request, therefore the Sonos tries to reconnect as it thinks the stream has gone dead. So if we can find the place where that value is presented via Device Spy etc. the TTS could be stopped at the point of reconnect…

A positive of this is that it would save loads of reconnects and unnecassary TTS requests to the Google TTS/translate service.

Sure, but we need an autonomous, and preferably upfront, way to specify that the URL run just once. It won’t work to wait for it to finish, since we don’t get events (see other thread) even if they could be delivered.

I wouldn’t be surprised if we could use a different protocol prefix, or supporting metadata, to get it to do the right thing with this URL.

Have you played around with different protocol components to see if they finish?

In case Google stop its service, you could find alternatives.
For example, there is this site allowing to choose amongst several voices: http://www.oddcast.com/home/demos/tts/tts_example.php?sitepal
But can it be called through a simple URL ?

Regarding SitePal:

JavaScript API available to trigger real-time dynamic TTS. By using the API you may achieve integration with any database or other source of online data.
http://www.oddcast.com/var/texttospeech.php
SitePal text-to-speech is easy to use for anyone, with or without programming skills. SitePal also provides a powerful API extension for more advanced users who want to use additional text-to-voice features. Here are a few cool examples:
http://www.sitepal.com/text-to-speech/

Edit: unfortunately, the API is not free.
http://www.sitepal.com/packages/

So, is the demo page usable ?

As a back up to the Google TTS, I am looking at the Microsoft Translator one.

https://datamarket.azure.com/dataset/bing/microsofttranslator

Which has a free API for upto 2,000,000 characters.

So far I’ve tried a few variation of the URL with my API key, but sadly I’m not having much luck getting it to work.

http://api.microsofttranslator.com/V2/Http.svc/Speak?language=en&format=audio/mp3&options=MaxQuality&appid=YOUR-KEY-HERE&text=Text+to+say

Microsoft’s Speak Method = Azure Cognitive Services Translator documentation - quickstarts, tutorials, API reference - Azure Cognitive Services | Microsoft Learn

Hi Everybody!

I have not a Sonos!
How to use is the simple this action code without Sonos , any devices with??

“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)”

(I’m using the Ipad ))

Hi slajgaj

The code you listed is specifically for the Sonos, if you have the Sonos Plugin installed on you Vera unit. If you just want to do text to speech the the normal Google URL is what you would use.

http://translate.google.com/translate_tts?tl=en&q=kitchen+lights+off

As far as i can recall, there are two main plugins for Vera that can do text to speech - one is for Squeezebox and the other is for the Sonos.

Thank the answer!
And this lua code is working with Karotz rabbit maybe?