| 
		
	
	
	
		
	Posts: 477 
	Threads: 100 
	Joined: Jun 2015
	
 Reputation: 
6 
	
	
		Has anyone a working solution for text to speech (and send it to sonos)? 
Before i used: translate.google.com, but now its banned by google.
 Code: function sonosSay(ip, lang, speak)local url = require("socket.url")
 local speak = url.escape(speak)
 uri = string.format("x-rincon-mp3radio://translate.google.com/translate_tts?tl=%s&q=%s", lang, speak)
 upnpavcmd(ip, 1400, 'SetAVTransportURI', '<CurrentURI>'..uri..'</CurrentURI><CurrentURIMetaData/>')
 upnpavcmd(ip, 1400, 'Play', '<Speed>1</Speed>')
 end
I like to use the dutch language.
	
		
	 
	
	
	
		
	Posts: 477 
	Threads: 100 
	Joined: Jun 2015
	
 Reputation: 
6 
	
	
	
		
	Posts: 57 
	Threads: 7 
	Joined: Jul 2016
	
 Reputation: 
1 
	
	
		 (07.12.2016, 19:20)gjniewenhuijse Wrote:  Has anyone a working solution for text to speech (and send it to sonos)?
 Before i used: translate.google.com, but now its banned by google.
 
 Code: function sonosSay(ip, lang, speak)local url = require("socket.url")
 local speak = url.escape(speak)
 uri = string.format("x-rincon-mp3radio://translate.google.com/translate_tts?tl=%s&q=%s", lang, speak)
 upnpavcmd(ip, 1400, 'SetAVTransportURI', '<CurrentURI>'..uri..'</CurrentURI><CurrentURIMetaData/>')
 upnpavcmd(ip, 1400, 'Play', '<Speed>1</Speed>')
 end
I like to use the dutch language.
 
Hi, 
I am looking into this also...thinking of using VoiceRSS since it will require minimal change to use. 
See weblink - http://www.voicerss.org/api/documentation.aspx 
I already have my code available to continue playing music/playlist after playing my doorbell sound or tts. 
Mvg, B 
B
	 
		
	 
	
	
	
		
	Posts: 477 
	Threads: 100 
	Joined: Jun 2015
	
 Reputation: 
6 
	
	
		I tested also with voicerss and that doesn't work for me. I think it failed because:- i opened a direct mp3 stream into my sonos from google/voicerss
 - but voicerss failed because it doesn't use mp3 as extension from the returning url
 
 dirty workaround:
 save voicerss file into lm
 rename to .mp3
 play with sonos
 remove mp3 file
 
		
	 
	
	
	
		
	Posts: 57 
	Threads: 7 
	Joined: Jul 2016
	
 Reputation: 
1 
	
	
		As requested my code using voicerss. 
First the user lib I use for controlling Sonos based on what existed - added some new functions (track info)
 Code: function upnpavcmd(host, port, cmd, param)local client, soap, reqs, service, res, err
 require('socket')
 client = socket.tcp()
 client:settimeout(3)
 -- try connecting to upnp endpoint
 res, err = client:connect(host, port)
 if not res then
 return nil, err
 end
 
 -- guess service name based on command
 if cmd =='SetGroupVolume' or cmd == 'SetGroupMute' or cmd == 'SnapshotGroupVolume' then
 service = 'GroupRenderingControl'
 elseif cmd == 'SetVolume' or cmd == 'GetVolume' or cmd == 'SetMute' or cmd == 'GetMute' then
 service = 'RenderingControl'
 else
 service = 'AVTransport'
 end
 
 -- soap envelope
 soap = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' ..
 '<s:Body>' ..
 '<u:' .. cmd .. ' xmlns:u="urn:schemas-upnp-org:service:' .. service .. ':1">' ..
 '<InstanceID>0</InstanceID>' ..
 (param or '') ..
 '</u:' .. cmd .. '>' ..
 '</s:Body>' ..
 '</s:Envelope>'
 
 -- http request
 reqs = 'POST /MediaRenderer/' .. service .. '/Control HTTP/1.1\r\n' ..
 'CONNECTION: close\r\n' ..
 'HOST: ' .. host .. ':' ..port.. '\r\n' ..
 'CONTENT-LENGTH: ' .. soap:len() .. '\r\n' ..
 'CONTENT-TYPE: text/xml; charset="utf-8"\r\n' ..
 'SOAPACTION: "urn:schemas-upnp-org:service:' .. service .. ':1#' .. cmd .. '"\r\n' ..
 '\r\n' .. soap
 
 -- send http request
 res, err = client:send(reqs)
 if not res then
 return nil, err
 end
 
 -- get reply and close connection
 res, err = client:receive('*a')
 client:close()
 
 return res, err
 end
 
 function Sonos_Find(sonos_name)
 require("socket")
 require('socket.http')
 local udp = socket.udp()
 if udp == nil then
 return
 else
 local result, message
 local datagram = "M-SEARCH * HTTP/1.1\r\n"
 .. "HOST: 239.255.255.250:1900\r\n"
 .. "ST: urn:schemas-upnp-org:device:ZonePlayer:1\r\n"
 .. "\r\n"
 result, message = udp:sendto(datagram, "239.255.255.250", 1900)
 for i = 1, 254 do
 datagram, message = udp:receivefrom()
 ip = datagram:match("http://(.-):1400")
 if datagram == nil or ip == nil then
 break
 else
 data = socket.http.request("http://" .. ip .. ":1400/status/zp")
 zonename = data:match([[<ZoneName>(.-)</ZoneName>]])
 ipaddress = data:match([[<IPAddress>(.-)</IPAddress>]])
 macaddress = data:match([[<MACAddress>(.-)</MACAddress>]])
 rincon = data:match([[<LocalUID>(.-)</LocalUID>]])
 if zonename == sonos_name then
 return {sonos_name = zonename, sonos_ipaddress = ipaddress, sonos_macaddress = macaddress, sonos_rincon = rincon}
 end
 end
 end
 end
 udp:close()
 end
 
 
 function Sonos_Getpositioninfo(sonos_ipaddress)
 -- Get Sonos Player Position Info
 position_info = upnpavcmd(sonos_ipaddress,1400,'GetPositionInfo')
 -- Get Track
 for i in string.gmatch(position_info, '<Track.-</Track>') do
 if i ~= nil then
 track_nr =  i:match([[<Track>(.-)</Track>]])
 end
 end  -- Get Track
 for i in string.gmatch(position_info, '<TrackDuration.-</TrackDuration>') do
 if i ~= nil then
 track_duration =  i:match([[<TrackDuration>(.-)</TrackDuration>]])
 end
 end
 -- Get Track Playing Time
 for i in string.gmatch(position_info, '<RelTime.-</RelTime>') do
 if i ~= nil then
 track_playing_time =  i:match([[<RelTime>(.-)</RelTime>]])
 end
 end
 -- Get Track Title
 for i in string.gmatch(position_info, 'dc:title.-/dc:title') do
 if i ~= nil then
 track_title =  i:match([[dc:title>(.-)</dc:title]])
 end
 end
 -- Get Track Artist
 for i in string.gmatch(position_info, 'dc:creator.-/dc:creator') do
 if i ~= nil then
 creator =  i:match([[dc:creator>(.-)</dc:creator]])
 end
 end
 if creator == nil then
 for i in string.gmatch(position_info, '<TrackMetaData.-</TrackMetaData>') do
 if i ~= nil then
 creator =  i:match([[streamContent>(.-)</r:streamContent]])
 end
 end
 end
 -- Get Track Album
 for i in string.gmatch(position_info, 'upnp:album.-/upnp:album') do
 if i ~= nil then
 album =  i:match([[upnp:album>(.-)</upnp:album]])
 end
 end
 --  -- Get Track Image URL
 --  for i in string.gmatch(position_info, 'upnp:albumArtURI.-/upnp:albumArtURI') do
 --      if i ~= nil then
 --      albumart =  i:match([[upnp:albumArtURI>(.-)</upnp:albumArtURI]])
 --      end
 --    end
 --  if albumart == nil then
 --    for i in string.gmatch(position_info, '<TrackURI.-</TrackURI>') do
 --      if i ~= nil then
 --        albumart =  i:match([[<TrackURI>(.-)</TrackURI>]])
 --      end
 --    end
 --  end
 return {TrackNr = track_nr, TrackDuration = track_duration, TrackRelTime = track_playing_time, TrackTitle = track_title, TrackCreator = creator, TrackAlbum = album}
 
 end
 
 function Sonos_Gettransportinfo(sonos_ipaddress)
 -- Get Sonos Player Transport Info
 reply = upnpavcmd(sonos_ipaddress,1400,'GetTransportInfo')
 for i in string.gmatch(reply, '<CurrentTransportState.-</CurrentTransportState>') do
 if i ~= nil then
 play_pause =  i:match([[<CurrentTransportState>(.-)</CurrentTransportState>]])
 end
 end
 -- 'TRANSITIONING' , 'PLAYING' , 'PAUSED_PLAYBACK' or 'STOPPED'
 return {Transportmode = play_pause}
 end
 
 function Sonos_GetVolume(sonos_ipaddress)
 -- Get Sonos Player Volume Info
 reply = upnpavcmd(sonos_ipaddress,1400,'GetVolume','<Channel>Master</Channel>')
 for i in string.gmatch(reply, '<CurrentVolume.-</CurrentVolume>') do
 if i ~= nil then
 volume =  i:match([[<CurrentVolume>(.-)</CurrentVolume>]])
 end
 end
 return {Volume = volume}
 end
 
 function Sonos_Getmediainfo(sonos_ipaddress)
 -- Get Sonos Player Media Info
 reply = upnpavcmd(sonos_ipaddress,1400,'GetMediaInfo')
 for i in string.gmatch(reply, '<CurrentURI.-</CurrentURI>') do
 if i ~= nil then
 currenturi =  i:match([[<CurrentURI>(.-)</CurrentURI>]])
 end
 end
 -- 'TRANSITIONING' , 'PLAYING' , 'PAUSED_PLAYBACK' or 'STOPPED'
 return {CurrentURI = currenturi}
 end
Below the actual script: 
It gets te mp3info and saves it on my synologyNas so the sonos can play it. 
Before pausing the music (if playing), I save the tracknum and relpos so I can continue afterwards - works as expected. 
My code only makes a distinction between playlist and radio station stubru - either one of them can be playing. 
Since currently testing - it is not yet a function to call with a custom text - easy to make it that way. 
The following weblink details the parameters for voicerss. http://www.voicerss.org/api/documentation.aspx 
Have fun, Bart
 Code: -- Load modulesrequire('json')
 require('socket.http')
 require('socket.ftp')
 require('user.Sonos')
 
 -- Set SONOS Variables
 Sonos_Player_Name = 'Eetkamer'
 action = 'none'
 
 -- Read IP address from controller
 data = io.readproc('if-json')
 data = json.decode(data)
 LMip = data.eth0.inetaddr
 -- Set timout
 socket.http.TIMEOUT = 5
 
 -- Set volumes
 TTS_Volume = 80
 Default_Volume = 25
 
 --Check if player exists
 reply = Sonos_Find(Sonos_Player_Name)
 current_sonosname = reply.sonos_name
 current_sonosplayerip = reply.sonos_ipaddress
 current_sonosmacaddress = reply.sonos_macaddress
 current_sonosrincon = reply.sonos_rincon
 
 -- Check if Sonos is playing?
 reply = Sonos_Gettransportinfo(current_sonosplayerip)
 previous_transportmode = reply.Transportmode
 
 -- Get VolumeInfo
 reply = Sonos_GetVolume(current_sonosplayerip)
 current_volume = reply.Volume
 
 -- Get VolumeInfo
 reply = Sonos_Getmediainfo(current_sonosplayerip)
 current_URI = reply.CurrentURI
 
 local MP3_data = socket.http.request("http://api.voicerss.org/?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx&hl=nl-nl&f=16khz_16bit_stereo&src=Mooi%20weer%20vandaag%20ik%20doe%20alles%20vandaag%20een%20beetje%20traag")
 
 --Store on synology
 res, err = socket.ftp.put({
 host = '192.168.1.200',
 user = 'xxx',
 password = 'xxx',
 path = '/music/Domotica/MP3/TTS.mp3',
 source = ltn12.source.string(MP3_data)
 })
 
 -- Get trackinfo
 reply=Sonos_Getpositioninfo(current_sonosplayerip)
 current_mediatype = reply.TrackTitle
 current_TrackRelTime = reply.TrackRelTime
 current_TrackNr = reply.TrackNr
 
 if previous_transportmode == 'PLAYING' then
 upnpavcmd(current_sonosplayerip, 1400, 'Pause')
 end
 upnpavcmd(current_sonosplayerip, 1400, 'SetVolume', '<Channel>Master</Channel><DesiredVolume>' ..TTS_Volume.. '</DesiredVolume>')
 upnpavcmd(current_sonosplayerip, 1400, 'SetAVTransportURI', '<CurrentURI>x-file-cifs://DISKSTATION/music/Domotica/MP3/TTS.mp3</CurrentURI><CurrentURIMetaData/>')
 os.sleep(1)
 
 -- Check if Sonos is playing?
 upnpavcmd(current_sonosplayerip, 1400, 'Play', '<Speed>1</Speed>')
 os.sleep(1)
 reply = Sonos_Gettransportinfo(current_sonosplayerip)
 current_transportmode = reply.Transportmode
 --Wait until playing TTS-MP3 is finished
 while (current_transportmode == 'PLAYING') do
 os.sleep(1)
 reply = Sonos_Gettransportinfo(current_sonosplayerip)
 current_transportmode = reply.Transportmode
 end
 
 upnpavcmd(current_sonosplayerip, 1400, 'Pause')
 if current_mediatype == 'stubru.aac' then
 Sonos_Action = 'playuri'
 Sonos_Listtype = 'favorites'
 Sonos_Listnumber = 3
 local reply = socket.http.request('http://' .. LMip .. '/apps/data/sonos/sonos.lp?action=' .. Sonos_Action .. '&name=' .. Sonos_Player_Name .. '&listtype=' .. Sonos_Listtype.. '&listnumber=' ..Sonos_Listnumber.. '')
 else
 upnpavcmd(current_sonosplayerip, 1400, 'SetAVTransportURI', '<CurrentURI>' ..current_URI.. '</CurrentURI><CurrentURIMetaData/>')
 upnpavcmd(current_sonosplayerip, 1400, 'Seek', '<InstanceID>0</InstanceID><Unit>TRACK_NR</Unit><Target>' ..current_TrackNr.. '</Target>')
 upnpavcmd(current_sonosplayerip, 1400, 'Seek', '<InstanceID>0</InstanceID><Unit>REL_TIME</Unit><Target>' ..current_TrackRelTime.. '</Target>')
 end
 upnpavcmd(current_sonosplayerip, 1400, 'SetVolume', '<Channel>Master</Channel><DesiredVolume>' ..current_volume.. '</DesiredVolume>')
 upnpavcmd(current_sonosplayerip, 1400, 'Pause')
 if previous_transportmode == 'PLAYING' then
 upnpavcmd(current_sonosplayerip, 1400, 'Play', '<Speed>1</Speed>')
 end
 
 script.disable(_SCRIPTNAME)
 -- loop while condition is met
		
	 |