Sonos ???

Zeile 11, welches Modul hat bei Dir die 144?
Da kommt die Temoeratur her.

Moin Hoggle,

das ist das Z Weather draußen, ich habe nun mal einen anderen Temparatur Sensor genommen, da bekomme ich das folgende Debug:

[DEBUG] 19:12:31: 23
[DEBUG] 19:12:31: nil
[DEBUG] 19:12:31: nil
[DEBUG] 19:12:31: line 94: attempt to concatenate local 'weather_de' (a nil value)

die erste Zeile von Deinem Debug ist die Temperatur.
Also geht das mit deiner zweiten Variante besser, weil mit dem Z-Weather kommt nichts an.

Die zweite Debug-Zeile ist das Wetter, Zeile 54.
Die wird in Zeile 53 durch die globale Var “OW_Weather” aufgefüllt.
Hast Du diese Variable?
Oder du kommentierst Zeile 53 aus und macht statt dessen in Zeile 52 die “–” weg.

Hi Hoggle,

danke für Deine ganze Hilfe, ich muss sagen, dass ich noch ganz am Anfang stecke mit den ganzen Lua Sachen… gerne schreibe ich aber alles nachher zusammen als How to für die Leute die nach mir kommen…

Es wird besser:

[DEBUG] 19:55:59: 23
[DEBUG] 19:55:59: nil
[DEBUG] 19:55:59: nil
[DEBUG] 19:55:59: line 94: attempt to concatenate local 'weather_de' (a nil value)

sprechen tut es aber immer noch nicht…

Wie sieht Deine Szene jetzt aus?

bzw. der Abruf hat sich geändert…
mach mal in Zeile 52:


local weather = api.get('/weather')['WeatherCondition']

So zusammen,

es wird schon besser.

Das ist aktuell mein Script:

--[[
%% properties
%% globals
--]]
 
 
-- 112 is the Virtual Device ID
-- 28 is the Process button ID
 
local sid, bid = 112, 28
local temp = tostring(fibaro:getValue(114, "value"))
 
--local temp=tostring(fibaro:getGlobalValue("OWM_Temp"));
--fibaro:debug(temp);
tempcurr=string.gsub(temp, "%.", ",", 1);
fibaro:debug(tempcurr);
 
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-- Funktion zur Ermittlung des Datums.
-- Version 1.0
-- Copyright (c) 2015 Nicolas Dörig
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
 
function germanDate( _type ) -- l=longDate("Montag, 12.September"), s=shortDate("Mo, 12.Sep 2011"), N=numericLong("12.09.2011"), n=numericShort("12.09.11")
	local longWochentag  = {'Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'}
	local shortWochentag = {'So','Mo','Di','Mi','Do','Fr','Sa'}
	local longMon        = {'Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'}
	local shortMon       = {'Jan','Feb','Mrz','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'}
	local indexTag       = os.date( "%w" ) +1
	local indexMon       = math.abs( os.date( "%m" ) )
	if _type == 's' then
		return os.date( shortWochentag[indexTag]..", %d."..shortMon[indexMon].." %Y" )
	elseif _type == 'l' then
		return os.date( longWochentag[indexTag]..", %d."..longMon[indexMon] )
	elseif _type == 'N' then
		return os.date( "%d.%m.%Y" )
	elseif _type == 'n' then
		return os.date( "%d.%m.%y" )
	end
end
--print ( 'Kurzdatum mit Wochentag\t' .. germanDate('s') )
--print ( 'Langdatum mit Wochentag\t' .. germanDate('l') )
--print ( 'Datum numerisch lang\t' .. germanDate('N') )
--print ( 'Datum numerisch kurz\t' .. germanDate('n') )
 
-------------------------------------------------------------------------------------------
 
--playTTS("de", "Guten Morgen. Es ist ".. germanDate('l') .." und draussen sind es " .. getWeather( 't' ) .." Grad " .. getWeather( 'c' ) ..". Ich starte das Radio fuer dich.", 13000, 15);
 
local weather = api.get('/weather')['WeatherCondition']
--local weather = fibaro:getGlobal("OW_Weather")
local weather_de
fibaro:debug(weather) 
 
  if weather == "overcast clouds" then 
      weather_de = "und es ist bewoelkt."
	elseif weather == "few clouds" then 
      weather_de = "mit ein paar Wolken."
	elseif weather == "scattered clouds" then 
      weather_de = "bei aufgelockerter Bewölkung."
	elseif weather == "broken clouds" then 
      weather_de = "bei durchbrochener Bewölkung."
	elseif weather == "light rain" then 
      weather_de = "es regnet leicht, vergiss den Schirm nicht!"
	elseif weather == "moderate rain" then 
      weather_de = "es regnet, vergiss den Schirm nicht!"
  	elseif weather == "heavy intensity rain" then 
      weather_de = "es regnet stark, vergiss den Schirm nicht!"
    elseif weather == "clear sky" then 
      weather_de = "und der Himmel ist klar."
    elseif weather == "snow" then 
      weather_de = ". Es schenit."
    elseif weather == "sleet" then 
      weather_de = ". Schneeregen."
    elseif weather == "drizzle" then 
      weather_de = ". Nieselregen."
    else
      --weather_de = "."
  	  weather_de = weather
    end
 
fibaro:debug(weather_de) 
---------------------------------------------------------------------------
-- Create TTS params object
 
--message = "Die aktuelle Temperatur draußen beträgt "..tempcurr.."°Celsius.",  
 
local params = {
 
--TTS Message
 
message = "Es ist ".. germanDate('l') .." und draussen sind "..tempcurr.." Grad."..weather_de..".";
 
--message = "Es ist ".. germanDate('l') .." und draussen sind "..tempcurr.."°Celsius.".. getWeather( 'c' ) ..". Ich starte das Radio fuer dich.";
 
duration = 'auto';  -- Duration: "auto", xx seconds
language = "de-DE"; -- Language: see http://www.voicerss.org/api/documentation.aspx to get your language code
volume = 18;         -- Volume
}
 
local _f = fibaro
local _x ={root="x_sonos_object",load=function(b)local c=_f:getGlobalValue(b.root)if string.len(c)>0 then local d=json.decode(c)if d and type(d)=="table"then return d else _f:debug("Unable to process data, check variable")end else _f:debug("No data found!")end end,set=function(b,e,d)local f=b:load()if f[e]then for g,h in pairs(d)do f[e][g]=h end else f[e]=d end;_f:setGlobal(b.root,json.encode(f))end,get=function(b,e)local f=b:load()if f and type(f)=="table"then for g,h in pairs(f)do if tostring(g)==tostring(e or"")then return h end end end;return nil end}
-- Make a request to the remote to process params object instantly
_x:set(tostring(sid), { tts = params })
_f:call(sid, "pressButton", bid)

Was noch nicht geht, dass die Stimme auch “Guten Morgen” sagt, es wird aktuell nur die Temparatur ausgegeben. Also scheinbar alles was ab Zeile 50 drin steht, wird ignoriert.

Hast Du dazu noch eine Idee?

Zeile 50 ist auskommentiert, die kannst Du dort auch löschen.

Angesagt wird, was in Zeile 94 steht.
Ggf. musst Du den Text dort anpassen.

Also:


message = "Guten Morgen. Es ist ".. germanDate('l') .." und draussen sind "..tempcurr.." Grad."..weather_de..".";

Wie sagt man immer so schön? Langsam nähert sich das Eichhörnchen :slight_smile:

Also, die Textausgabe funktioniert jetzt schon mal. Perfekt, danke.

al weather = api.get('/weather')['WeatherCondition']
--local weather = fibaro:getGlobal("OW_Weather")
local weather_de
fibaro:debug(weather) 
 
  if weather == "overcast clouds" then 
      weather_de = "und es ist bewoelkt."
	elseif weather == "few clouds" then 
      weather_de = "mit ein paar Wolken."
	elseif weather == "scattered clouds" then 
      weather_de = "bei aufgelockerter Bewölkung."
	elseif weather == "broken clouds" then 
      weather_de = "bei durchbrochener Bewölkung."
	elseif weather == "light rain" then 
      weather_de = "es regnet leicht, vergiss den Schirm nicht!"
	elseif weather == "moderate rain" then 
      weather_de = "es regnet, vergiss den Schirm nicht!"
  	elseif weather == "heavy intensity rain" then 
      weather_de = "es regnet stark, vergiss den Schirm nicht!"
    elseif weather == "clear sky" then 
      weather_de = "und der Himmel ist klar."
    elseif weather == "snow" then 
      weather_de = ". Es schenit."
    elseif weather == "sleet" then 
      weather_de = ". Schneeregen."
    elseif weather == "drizzle" then 
      weather_de = ". Nieselregen."
    else
      --weather_de = "."
  	  weather_de = weather
    end

wird dieser Teil des Code´s irgendwie auch angesprochen?

ja.
Der Baustein dienst dazu den englischen Text (weather) des Wetters zu übersetzen (weather_de).
Das Wetter beleibt englisch, wenn der englische Begriff nicht (richtig) in den Bedingungen aufgeführt wird.
In Zeile 84 wir der übersetzte Begriff des Wetters debuggt.

So, wenn ich dann nochmal auf das aktuelle Script schaue,

--[[
%% properties
%% globals
--]]
 
 
-- 112 is the Virtual Device ID
-- 28 is the Process button ID
 
local sid, bid = 112, 28
local temp = tostring(fibaro:getValue(114, "value"))
 
--local temp=tostring(fibaro:getGlobalValue("OWM_Temp"));
--fibaro:debug(temp);
tempcurr=string.gsub(temp, "%.", ",", 1);
fibaro:debug(tempcurr);
 
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-- Funktion zur Ermittlung des Datums.
-- Version 1.0
-- Copyright (c) 2015 Nicolas Dörig
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
 
function germanDate( _type ) -- l=longDate("Montag, 12.September"), s=shortDate("Mo, 12.Sep 2011"), N=numericLong("12.09.2011"), n=numericShort("12.09.11")
	local longWochentag  = {'Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'}
	local shortWochentag = {'So','Mo','Di','Mi','Do','Fr','Sa'}
	local longMon        = {'Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'}
	local shortMon       = {'Jan','Feb','Mrz','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'}
	local indexTag       = os.date( "%w" ) +1
	local indexMon       = math.abs( os.date( "%m" ) )
	if _type == 's' then
		return os.date( shortWochentag[indexTag]..", %d."..shortMon[indexMon].." %Y" )
	elseif _type == 'l' then
		return os.date( longWochentag[indexTag]..", %d."..longMon[indexMon] )
	elseif _type == 'N' then
		return os.date( "%d.%m.%Y" )
	elseif _type == 'n' then
		return os.date( "%d.%m.%y" )
	end
end
--print ( 'Kurzdatum mit Wochentag\t' .. germanDate('s') )
--print ( 'Langdatum mit Wochentag\t' .. germanDate('l') )
--print ( 'Datum numerisch lang\t' .. germanDate('N') )
--print ( 'Datum numerisch kurz\t' .. germanDate('n') )
 
-------------------------------------------------------------------------------------------

 
local weather = api.get('/weather')['WeatherCondition']
local weather_de
fibaro:debug(weather) 
 
  if weather == "overcast clouds" then 
      weather_de = "bewoelkt."
	elseif weather == "few clouds" then 
      weather_de = "mit ein paar Wolken."
	elseif weather == "scattered clouds" then 
      weather_de = "mit aufgelockerter Bewölkung."
	elseif weather == "broken clouds" then 
      weather_de = "mit durchbrochener Bewölkung."
	elseif weather == "light rain" then 
      weather_de = "mit leichtem regen, vergiss den Schirm nicht!"
	elseif weather == "moderate rain" then 
      weather_de = "es regnet, vergiss den Schirm nicht!"
  	elseif weather == "heavy intensity rain" then 
      weather_de = "es regnet stark, vergiss den Schirm nicht!"
    elseif weather == "clear sky" then 
      weather_de = "mit klarem Himmel."
    elseif weather == "snow" then 
      weather_de = "mit Schnee."
    elseif weather == "sleet" then 
      weather_de = "mit Schneeregen."
    elseif weather == "drizzle" then 
      weather_de = "mit Nieselregen."
    else
      --weather_de = "."
  	  weather_de = "nicht vorhersagbar, schaue kurz aus dem Fenster."
    end
 
fibaro:debug(weather_de) 
---------------------------------------------------------------------------
-- Create TTS params object
 
--message = "Die aktuelle Temperatur draußen beträgt "..tempcurr.."°Celsius.",  
 
local params = {
 
--TTS Message
 
message = "Guten Morgen. Es ist ".. germanDate('l') .." und draussen sind "..tempcurr.." Grad. Das Wetter ist "..weather_de.."";
 
--message = "Es ist ".. germanDate('l') .." und draussen sind "..tempcurr.."°Celsius.".. getWeather( 'c' ) ..". Ich starte das Radio fuer dich.";
 
duration = 'auto';  -- Duration: "auto", xx seconds
language = "de-DE"; -- Language: see http://www.voicerss.org/api/documentation.aspx to get your language code
volume = 40;         -- Volume
}
 
local _f = fibaro
local _x ={root="x_sonos_object",load=function(b)local c=_f:getGlobalValue(b.root)if string.len(c)>0 then local d=json.decode(c)if d and type(d)=="table"then return d else _f:debug("Unable to process data, check variable")end else _f:debug("No data found!")end end,set=function(b,e,d)local f=b:load()if f[e]then for g,h in pairs(d)do f[e][g]=h end else f[e]=d end;_f:setGlobal(b.root,json.encode(f))end,get=function(b,e)local f=b:load()if f and type(f)=="table"then for g,h in pairs(f)do if tostring(g)==tostring(e or"")then return h end end end;return nil end}
-- Make a request to the remote to process params object instantly
_x:set(tostring(sid), { tts = params })
_f:call(sid, "pressButton", bid)
 

War in Zeile 79 noch ein “Clear” das sagt er wohl, wenn er nichts ermitteln kann, dass habe ich nun nochmal angepasst.

Wenn ich nun das Script manuell starte klappt es, allerdings nicht über den Process Boutton in der SONOS Remote 1.0.1.
Wenn ich mir den Process Boutton anschaue finde ich den folgenden Code:

UserParams = {
  -- Voice RSS API Key (Free Registration: http://www.voicerss.org/registration.aspx)
  voiceRssApiKey = "HiermussDeinCoderein",
  -- Sound quality: low, medium, high
  voiceRssSoundQuality = "medium"
}

-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
local _f = fibaro;

if not Toolkit then Toolkit={
    __header="Toolkit",
    __version="1.0.6",
    __luaBase="5.2.0",
    __copyright="Jean-Christophe Vermandé",
    __licence=[[
    Minified version : Compression ratio 50.90%
    
	Copyright (C) 2013-2015 Jean-Christophe Vermandé

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses></http:>.
]],__frameworkHeader=function(self)self:traceEx("green","-------------------------------------------------------------------------")self:traceEx("green","-- HC2 Toolkit Framework version %s",self.__version)self:traceEx("green","-- Current interpreter version is %s",self.getInterpreterVersion())self:traceEx("green","-- Total memory in use by Lua: %.2f Kbytes",self.getCurrentMemoryUsed())self:traceEx("green","-------------------------------------------------------------------------")end,chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",hex="0123456789abcdef",now=os.date,toUnixTimestamp=function(a)return os.time(a)end,fromUnixTimestamp=function(b)return os.date("%c",ts)end,currentTime=function()return tonumber(os.date("%H%M%S"))end,comparableTime=function(d,e,f)return tonumber(string.format("%02d%02d%02d",d,e,f))end,isTraceEnabled=true,isAutostartTrigger=function()local a=fibaro:getSourceTrigger()return a["type"]=="autostart"end,isOtherTrigger=function()local a=fibaro:getSourceTrigger()return a["type"]=="other"end,raiseError=function(g,h)error(g,h)end,colorSetToRgbw=function(self,j)self.assertArg("colorSet",j,"string")local a,i={},1;for k in string.gmatch(j,"(%d+)")do a[i]=k;i=i+1 end;return a[1],a[2],a[3],a[4]end,isValidJson=function(self,l,m)self.assertArg("data",l,"string")self.assertArg("raise",m,"boolean")if string.len(l)>0 then if pcall(function()return json.decode(l)end)then return true else if m then self.raiseError("invalid json",2)end end end;return false end,assertArg=function(name,value,n)if type(value)~=n then Tk.raiseError("argument "..name.." must be "..n,2)end end,trace=function(self,value,...)if self.isTraceEnabled then if value~=nil then return fibaro:debug(string.format(value,...))end end end,traceEx=function(self,o,value,...)self:trace(string.format('<%s style="color:%s;">%s</%s>',"span",o,string.format(value,...),"span"))end,getInterpreterVersion=function()return _VERSION end,getCurrentMemoryUsed=function()return collectgarbage("count")end,trim=function(b)Tk.assertArg("value",b,"string")return string.gsub(b,"^%s*(.-)%s*$","%1")end,isNaN=function(p)return p~=p end,filterByPredicate=function(table,q)Tk.assertArg("table",table,"table")Tk.assertArg("predicate",q,"function")local r,s=1,{}for i=1,#table do local k=table[i]if k~=nil then if q(k)then s[r]=k;r=r+1 end end end;return s,#s end}Toolkit:__frameworkHeader()Tk=Toolkit end;if not Toolkit.Debug then Toolkit.Debug={__header="Toolkit.Debug",__version="1.0.1",__clocks={["fragment"]=os.clock(),["all"]=os.clock()},benchmarkPoint=function(self,name)__clocks[name]=os.clock()end,benchmark=function(self,g,t,name,u)Toolkit.assertArg("message",g,"string")Toolkit.assertArg("template",g,"string")if u~=nil then Toolkit.assertArg("reset",u,type(true))end;Toolkit:traceEx("yellow","Benchmark ["..g.."]: "..string.format(t,os.clock()-self.__clocks[name]))if u==true then self.__clocks[name]=os.clock()end end}Toolkit:traceEx("red",Toolkit.Debug.__header.." loaded in memory...")if Toolkit.Debug then Toolkit.Debug:benchmark(Toolkit.Debug.__header.." lib","elapsed time: %.3f cpu secs\n","fragment",true)end end;if not Toolkit then error("You must add Toolkit",2)end;if not Toolkit.Collections then Toolkit.Collections={}end;if not Toolkit.Collections.Queue then Toolkit.Collections.Queue={__header="Toolkit.Collections.Queue",__version="1.0.0",__base={__first=0,__count=0,toArray=function(self)local v={}for i=1,self:count()do v[i]=self[i]end;return v end,clear=function(self)for i=1,self:count()do self[i]=nil end;self.__first=0;self.__count=0 end,enqueue=function(self,value)assert(value~=nil)local r=self.__first+1;self.__count=self.__count+1;self.__first=r;self[r]=value;Tk:trace("add at pos %d value with %s type",r,tostring(self[r]))end,dequeue=function(self)local w=self:peek()self[1]=nil;self.__first=self.__first-1;self.__count=self.__count-1;for i=1,self:count()do self[i]=self[i+1]end;return w end,contains=function(self,value)assert(value~=nil)for i=1,self:count()do if self[i]==value then return true end end;return false end,peek=function(self)return self[1]end,count=function(self)return tonumber(self.__count)end,clone=function(self)return self end},new=function()collectgarbage("collect")return Toolkit.Collections.Queue.__base end,version=function()return Toolkit.Collections.Queue.__version end}Toolkit:traceEx("red",Toolkit.Collections.Queue.__header.." loaded in memory...")if Toolkit.Debug then Toolkit.Debug:benchmark(Toolkit.Collections.Queue.__header.." lib","elapsed time: %.3f cpu secs\n","fragment",true)end end;if not Toolkit then error("You must add Toolkit",2)end;if not Toolkit.Net then Toolkit.Net={__header="Toolkit.Net",__version="1.0.3",__cr=string.char(13),__lf=string.char(10),__crLf=string.char(13,10),__host=nil,__port=nil,__trace=function(k,...)if Toolkit.Net.isTraceEnabled then Toolkit:trace(k,...)end end,__writeHeader=function(x,l)assert(tostring(l)or l==nil or l=="","Invalid header found: "..l)local y=tostring(l)x:write(y..Toolkit.Net.__crLf)Toolkit.Net.__trace("%s.%s::request > Add header [%s]",Toolkit.Net.__header,Toolkit.Net.__Http.__header,y)end,__decodeChunks=function(z)resp=""line="0"lenline=0;len=string.len(z)i=1;while i<=len do c=string.sub(z,i,i)if lenline==0 then if c==Toolkit.Net.__lf then lenline=tonumber(line,16)if lenline==null then lenline=0 end;line=0 elseif c==Toolkit.Net.__cr then lenline=0 else line=line..c end else resp=resp..c;lenline=lenline-1 end;i=i+1 end;return resp end,__readHeader=function(l)if l==nil then error("Couldn't find header")end;local A=""local B={}local i,len=1,string.len(l)while i<=len do local z=l:sub(i,i)or""local C=l:sub(i+1,i+1)or""if z..C==Toolkit.Net.__crLf then i=i+1;table.insert(B,A)A=""else A=A..z end;i=i+1 end;return B end,__readSocket=function(x)local D,len=0,1;local A,l="",""while D==0 and len>0 do l,D=x:read()len=string.len(l)A=A..l end;return A,D end,__Http={__header="HttpRequest",__version="1.0.3",__tcpSocket=nil,__timeout=250,__waitBeforeReadMs=25,__isConnected=false,__isChunked=false,__url=nil,__method="GET",__headers={},__body=nil,__authorization=nil,setBasicAuthentication=function(self,E,F)Toolkit.assertArg("username",E,"string")Toolkit.assertArg("password",F,"string")self.__authorization=Toolkit.Crypto.Base64:encode(tostring(E..":"..F))end,setBasicAuthenticationEncoded=function(self,G)Toolkit.assertArg("base64String",G,"string")self.__authorization=G end,setWaitBeforeReadMs=function(self,H)Toolkit.assertArg("ms",H,"integer")self.__waitBeforeReadMs=H;Toolkit.Net.__trace("%s.%s::setWaitBeforeReadMs > set to %d ms",Toolkit.Net.__header,Toolkit.Net.__Http.__header,H)end,getWaitBeforeReadMs=function(self)return self.__waitBeforeReadMs end,setReadTimeout=function(self,H)Toolkit.assertArg("ms",H,"number")self.__timeout=H;Toolkit.Net.__trace("%s.%s::setReadTimeout > Timeout set to %d ms",Toolkit.Net.__header,Toolkit.Net.__Http.__header,H)end,getReadTimeout=function(self)return self.__timeout end,disconnect=function(self)self.__tcpSocket:disconnect()self.__isConnected=false;Toolkit.Net.__trace("%s.%s::disconnect > Connected: %s",Toolkit.Net.__header,Toolkit.Net.__Http.__header,tostring(self.__isConnected))end,request=function(self,I,J,B,K)Toolkit.assertArg("method",I,"string")assert(I=="GET"or I=="POST"or I=="PUT"or I=="DELETE"or I=="SUBSCRIBE")assert(J~=nil or J=="")self.__isChunked=false;self.__tcpSocket:setReadTimeout(self.__timeout)self.__url=J;self.__method=I;self.__headers=B or{}self.__body=K or nil;local v=self.__method.." "..self.__url.." HTTP/1.1"Toolkit.Net.__trace("%s.%s::request > %s with method %s",Toolkit.Net.__header,Toolkit.Net.__Http.__header,self.__url,self.__method)local L=""if Toolkit.Net.__port~=nil then L=":"..tostring(Toolkit.Net.__port)end;local M="Host: "..Toolkit.Net.__host..L;Toolkit.Net.__writeHeader(self.__tcpSocket,v)Toolkit.Net.__writeHeader(self.__tcpSocket,M)for i=1,#self.__headers do Toolkit.Net.__writeHeader(self.__tcpSocket,self.__headers[i])end;if self.__authorization~=nil then Toolkit.Net.__writeHeader(self.__tcpSocket,"Authorization: Basic "..self.__authorization)end;if self.__body~=nil then Toolkit.Net.__writeHeader(self.__tcpSocket,"Content-Length: "..string.len(self.__body))Toolkit.Net.__trace("%s.%s::request > Body length is %d",Toolkit.Net.__header,Toolkit.Net.__Http.__header,string.len(self.__body))end;self.__tcpSocket:write(Toolkit.Net.__crLf..Toolkit.Net.__crLf)if self.__body~=nil then self.__tcpSocket:write(self.__body)end;fibaro:sleep(self.__waitBeforeReadMs)local N,D=Toolkit.Net.__readSocket(self.__tcpSocket)Toolkit.Net.__trace("%s.%s::receive > Length of result: %d",Toolkit.Net.__header,Toolkit.Net.__Http.__header,string.len(N))local O,P;if string.len(N)>0 then local Q=string.find(N,Toolkit.Net.__crLf..Toolkit.Net.__crLf)local R=string.sub(N,1,Q+2)if string.len(R)then P=string.sub(R,10,13)Toolkit.Net.__trace("%s.%s::receive > Status %s",Toolkit.Net.__header,Toolkit.Net.__Http.__header,P)Toolkit.Net.__trace("%s.%s::receive > Length of headers reponse %d",Toolkit.Net.__header,Toolkit.Net.__Http.__header,string.len(R))__headers=Toolkit.Net.__readHeader(R)for S,k in pairs(__headers)do if string.find(string.lower(k or""),"chunked")then self.__isChunked=true;Toolkit.Net.__trace("%s.%s::receive > Transfer-Encoding: chunked",Toolkit.Net.__header,Toolkit.Net.__Http.__header,string.len(N))end end end;local T=string.sub(N,Q+4)if self.__isChunked then O=Toolkit.Net.__decodeChunks(T)D=0 else O=T;D=0 end end;return O,P,D end,version=function()return Toolkit.Net.__Http.__version end,dispose=function(self)if self.__isConnected then self.__tcpSocket:disconnect()end;self.__tcpSocket=nil;self.__url=nil;self.__headers=nil;self.__body=nil;self.__method=nil;if pcall(function()assert(self.__tcpSocket~=Net.FTcpSocket)end)then Toolkit.Net.__trace("%s.%s::dispose > Successfully disposed",Toolkit.Net.__header,Toolkit.Net.__Http.__header)end;collectgarbage("collect")Toolkit.Net.__trace("%s.%s::dispose > Total memory in use by Lua: %.2f Kbytes",Toolkit.Net.__header,Toolkit.Net.__Http.__header,collectgarbage("count"))end},isTraceEnabled=false,HttpRequest=function(U,V)assert(U~=Toolkit.Net,"Cannot call HttpRequest like that!")assert(U~=nil,"host invalid input")assert(V==nil or tonumber(V),"port invalid input")collectgarbage("collect")Toolkit.Net.__host=U;Toolkit.Net.__port=V;local W=Toolkit.Net.__Http;W.__tcpSocket=Net.FTcpSocket(U,V)W.__isConnected=true;Toolkit.Net.__trace("%s.%s > Total memory in use by Lua: %.2f Kbytes",Toolkit.Net.__header,Toolkit.Net.__Http.__header,collectgarbage("count"))Toolkit.Net.__trace("%s.%s > Create Session on port: %d, host: %s",Toolkit.Net.__header,Toolkit.Net.__Http.__header,V,U)return W end,version=function()return Toolkit.Net.__version end}Toolkit:traceEx("red",Toolkit.Net.__header.." loaded in memory...")if Toolkit.Debug then Toolkit.Debug:benchmark(Toolkit.Net.__header.." lib","elapsed time: %.3f cpu secs\n","fragment",true)end end;if not Toolkit then error("You must add Toolkit",2)end;if not Toolkit.Xml then Toolkit.Xml={__header="Toolkit.Xml",__version="1.0.1",__node=function(name)local node={}node.___value=nil;node.___name=name;node.___children={}node.___props={}function node:value()return self.___value end;function node:setValue(X)self.___value=X end;function node:name()return self.___name end;function node:setName(name)self.___name=name end;function node:childNodes()return self.___children end;function node:addChild(Y)if self[Y:name()]~=nil then if type(self[Y:name()].name)=="function"then local Z={}table.insert(Z,self[Y:name()])self[Y:name()]=Z end;table.insert(self[Y:name()],Y)else self[Y:name()]=Y end;table.insert(self.___children,Y)end;function node:properties()return self.___props end;function node:addProperty(name,value)local _="@"..name;if self[_]~=nil then if type(self[_])=="string"then local Z={}table.insert(Z,self[_])self[_]=Z end;table.insert(self[_],value)else self[_]=value end;table.insert(self.___props,{name=name,value=value})end;return node end,Node=function(self,name)return self.__node(name)end,ToXmlString=function(value)value=string.gsub(value,"&","&".."amp;")value=string.gsub(value,"<","&".."lt;")value=string.gsub(value,">","&".."gt;")value=string.gsub(value,"\"","&".."quot;")value=string.gsub(value,"([^%w%&%;%p%\t% ])",function(c)return string.format("&#x%X;",string.byte(c))end)return value end,FromXmlString=function(value)value=string.gsub(value,"&#x([%x]+)%;",function(M)return string.char(tonumber(M,16))end)value=string.gsub(value,"&#([0-9]+)%;",function(M)return string.char(tonumber(M,10))end)value=string.gsub(value,"&".."quot;","\"")value=string.gsub(value,"&".."apos;","'")value=string.gsub(value,"&".."gt;",">")value=string.gsub(value,"&".."lt;","<")value=string.gsub(value,"&".."amp;","&")return value end,ParseArgs=function(node,b)string.gsub(b,"(%w+)=([\"'])(.-)%2",function(a0,a1,z)node:addProperty(a0,Toolkit.Xml.FromXmlString(z))end)end,ParseXmlText=function(self,a2)local a3={}local a4=Toolkit.Xml:Node()table.insert(a3,a4)local a5,c,a6,a7,a8;local i,a9=1,1;while true do a5,a9,c,a6,a7,a8=string.find(a2,"<(%/?)([%w_:]+)(.-)(%/?)>",i)if not a5 then break end;local aa=string.sub(a2,i,a5-1)if not string.find(aa,"^%s*$")then local ab=(a4:value()or"")..Toolkit.Xml.FromXmlString(aa)a3[#a3]:setValue(ab)end;if a8=="/"then local ac=Toolkit.Xml:Node(a6)Toolkit.Xml.ParseArgs(ac,a7)a4:addChild(ac)elseif c==""then local ac=Toolkit.Xml:Node(a6)Toolkit.Xml.ParseArgs(ac,a7)table.insert(a3,ac)a4=ac else local ad=table.remove(a3)a4=a3[#a3]if#a3<1 then error("XmlParser: nothing to close with "..a6)end;if ad:name()~=a6 then error("XmlParser: trying to close "..ad:name().." with "..a6)end;a4:addChild(ad)end;i=a9+1 end;local aa=string.sub(a2,i)if#a3>1 then error("XmlParser: unclosed "..a3[#a3]:name())end;return a4 end,version=function()return Toolkit.Xml.__version end}Toolkit:traceEx("red",Toolkit.Xml.__header.." loaded in memory...")if Toolkit.Debug then Toolkit.Debug:benchmark(Toolkit.Xml.__header.." lib","elapsed time: %.3f cpu secs\n","fragment",true)end end;if not Toolkit.HttpUtility then Toolkit.HttpUtility={__header="Toolkit.HttpUtility",__version="1.0.0",urlEncode=function(ae)if ae then ae=string.gsub(ae,"\n","\r\n")ae=string.gsub(ae,"([^%w ])",function(c)return string.format("%%%02X",string.byte(c))end)ae=string.gsub(ae," ","+")end;return ae end,urlDecode=function(ae)if ae then ae=string.gsub(ae,"+"," ")ae=string.gsub(ae,"%%(%x%x)",function(M)return string.char(tonumber(M,16))end)ae=string.gsub(ae,"\r\n","\n")end;return ae end}Toolkit:traceEx("red",Toolkit.HttpUtility.__header.." loaded in memory...")if Toolkit.Debug then Toolkit.Debug:benchmark(Toolkit.HttpUtility.__header.." lib","elapsed time: %.3f cpu secs\n","fragment",true)end end

        
_selfId=_f:getSelfId()_ip=_f:get(_selfId,"IPAddress")_port=_f:get(_selfId,"TCPPort")_icons={main=_f:get(_selfId,"deviceIcon")}if _ip==nil or _port==nil then Tk:traceEx("red","You must configure IPAddress and TCPPort first")os.exit()end;Sonos={__header="SONOS Advanced Remote",__version="1.0.1",_commands=Tk.Collections.Queue.new(),_isPlaying=false,_isPlayingStream=false,_isMuted=false,_debugProcess=true,transportState="",transportStatus="",volume=0,lastVolume=0,lastMuteState=0,lastTransportState="",lastTrack=nil,streamVolumeIsDifferent=false,mediaInfo={title=""},eq={loudness=false},zpStatus={zoneName="",localUID="",macAddress=""},zonePlayers={},radioStations={},currentTrack={isRadio=false,isFile=false,absCount=nil,artist=nil,album=nil,creator=nil,duration=nil,originalTrackNumber=nil,relTime=nil,relCount=nil,title=nil,track=nil,uri=nil},refreshTime=6,defaultRefreshTime=12,props={controlURL={ServerContentDirectory="/MediaServer/ContentDirectory/Control",RendererAVTransport="/MediaRenderer/AVTransport/Control",RendererRenderingControl="/MediaRenderer/RenderingControl/Control",RendererConnectionManager="/MediaRenderer/ConnectionManager/Control",RendererQueue="/MediaRenderer/Queue/Control",DeviceProperties="/DeviceProperties/Control"},serviceType={MediaRenderer="urn:schemas-upnp-org:device:MediaRenderer:1",AVTransport="urn:schemas-upnp-org:service:AVTransport:1",RenderingControl="urn:schemas-upnp-org:service:RenderingControl:1",Queue="urn:schemas-sonos-com:service:Queue:1",GroupRenderingControl="urn:schemas-upnp-org:service:GroupRenderingControl:1",ContentDirectory="urn:schemas-upnp-org:service:ContentDirectory:1",DeviceProperties="urn:schemas-upnp-org:service:DeviceProperties:1"},actions={GetTransportInfo="GetTransportInfo",GetPositionInfo="GetPositionInfo",GetMediaInfo="GetMediaInfo",GetVolume="GetVolume",SetVolume="SetVolume",GetMute="GetMute",SetMute="SetMute",SetLoudness="SetLoudness",GetLoudness="GetLoudness",SetAVTransportURI="SetAVTransportURI",Play="Play",Pause="Pause",Stop="Stop",Previous="Previous",Next="Next",Seek="Seek",Browse="Browse",SetLEDState="SetLEDState",GetLEDState="GetLEDState"},transportState={playing="PLAYING",stopped="STOPPED",pausedPlayback="PAUSED_PLAYBACK",transitioning="TRANSITIONING"},transportStatus={ok="OK"}}}Sonos.play=function(self,callback)Tk:trace("Play request")callback=callback or function(a)Tk:trace("Play sent")end;self.refreshTime=2;return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.Play,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Speed>1</Speed>",callback)end;Sonos.pause=function(self)Tk:trace("Pause request")return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.Pause,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Speed>1</Speed>",function(a)Tk:trace("Pause sent")end)end;Sonos.stop=function(self)Tk:trace("Stop request")return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.Stop,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Speed>1</Speed>",function(a)Tk:trace("Stop was sent")end)end;Sonos.previous=function(self)Tk:trace("Previous request")return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.Previous,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Speed>1</Speed>",function(a)Tk:trace("Previous sent")end)end;Sonos.next=function(self)Tk:trace("Next request")return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.Next,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Speed>1</Speed>",function(a)Tk:trace("Next sent")end)end;Sonos.seek=function(self,type,b)if self.currentTrack.isRadio then Tk:traceEx("Yellow","Cannot seek in radio mode!")return end;Tk:trace("seek to %s",tostring(b))return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.Seek,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Unit>"..type.."</Unit><Target>"..b.."</Target>",function(a)Tk:trace("seek was sent")end)end;Sonos.seekL=function(self)if self.currentTrack~=nil then local c=self.currentTrack.relTime;local d=clockToSeconds(c)if d>=30 then d=d-30 end;self:seek("REL_TIME",secondsToClock(d))else Tk:traceEx("yellow","There is no current track loaded.")end end;Sonos.seekR=function(self)if self.currentTrack~=nil then local c=self.currentTrack.relTime;local e=clockToSeconds(c)or 0;local f=clockToSeconds(self.currentTrack.duration or"00:00")or 0;e=e+30;if e>=f then Tk:traceEx("yellow","try to seek out of range!")return end;self:seek("REL_TIME",secondsToClock(e))Tk:trace("seek right request")else Tk:traceEx("yellow","There is no current track loaded.")end end;Sonos.loudness=function(self,g)Tk:trace("Set Loudness: "..tostring(g))local h=0;if g==true then h=1 end;return sendSoapMessage(self.props.controlURL.RendererRenderingControl,self.props.serviceType.RenderingControl,{name=self.props.actions.SetLoudness,service=self.props.serviceType.RenderingControl},"<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredLoudness>"..h.."</DesiredLoudness>",function(a)if h==0 then Tk:trace("Loudness OFF was sent")self.eq.loudness=false else Tk:trace("Loudness ON was sent")self.eq.loudness=true end end)end;Sonos.getLoudness=function(self)Tk:trace("Get loudness request")return sendSoapMessage(self.props.controlURL.RendererRenderingControl,self.props.serviceType.AVTransport,{name=self.props.actions.GetLoudness,service=self.props.serviceType.RenderingControl},"<InstanceID>0</InstanceID><Channel>Master</Channel>",function(a)local i=tonumber(a:match("<CurrentLoudness>(.+)</CurrentLoudness>")or 0)if i==0 then Tk:trace("Loudness is OFF")self.eq.loudness=false elseif i==1 then Tk:trace("Loudness is ON")self.eq.loudness=true else Tk:trace("Loudness N.C")self.eq.loudness=false end end)end;Sonos.mute=function(self,g)Tk:trace("Mute request")local h=0;if g==true then h=1 end;return sendSoapMessage(self.props.controlURL.RendererRenderingControl,self.props.serviceType.RenderingControl,{name=self.props.actions.SetMute,service=self.props.serviceType.RenderingControl},"<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredMute>"..h.."</DesiredMute>",function(a)if h==0 then Tk:trace("UnMute was sent")self._isMuted=false else Tk:trace("Mute was sent")self._isMuted=true end end)end;Sonos.muteInvert=function(self)self:getMute()if self._isMuted==true then self:mute(false)elseif self._isMuted==false then self:mute(true)end end;Sonos.getMute=function(self)Tk:trace("Get mute state request")return sendSoapMessage(self.props.controlURL.RendererRenderingControl,self.props.serviceType.RenderingControl,{name=self.props.actions.GetMute,service=self.props.serviceType.RenderingControl},"<InstanceID>0</InstanceID><Channel>Master</Channel>",function(a)local h=tonumber(a:match("<CurrentMute>(.+)</CurrentMute>")or 0)if h==0 then self._isMuted=false elseif h==1 then self._isMuted=true end;Tk:trace("mute: %s",tostring(self._isMuted))end)end;Sonos.getVolume=function(self)Tk:trace("Get volume request")return sendSoapMessage(self.props.controlURL.RendererRenderingControl,self.props.serviceType.AVTransport,{name=self.props.actions.GetVolume,service=self.props.serviceType.RenderingControl},"<InstanceID>0</InstanceID><Channel>Master</Channel>",function(a)self.volume=tonumber(a:match("<CurrentVolume>(.+)</CurrentVolume>")or 0)Tk:trace("volume: %d",self.volume)end)end;Sonos.setVolume=function(self,j)Tk:trace("Set volume to %s",tostring(j))return sendSoapMessage(self.props.controlURL.RendererRenderingControl,self.props.serviceType.RenderingControl,{name=self.props.actions.SetVolume,service=self.props.serviceType.RenderingControl},"<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredVolume>"..tostring(j).."</DesiredVolume>",function(a)self.volume=tonumber(j)_f:call(_selfId,"setProperty","ui.slVolume.value",self.volume)Tk:trace("Volume set to %d",j)end)end;Sonos.ledState=function(self,g)Tk:trace("Set LED state request")return sendSoapMessage(self.props.controlURL.DeviceProperties,self.props.serviceType.DeviceProperties,{name=self.props.actions.SetLEDState,service=self.props.serviceType.DeviceProperties},"<DesiredLEDState>"..(g or"ON").."</DesiredLEDState>",function(a)Tk:trace("Set LED state sent")end)end;Sonos.getTransportState=function(self)Tk:trace("Get transport state request")return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.GetTransportInfo,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID>",function(a)self.transportState=a:match("<CurrentTransportState>(.+)</CurrentTransportState>")or""self.transportStatus=a:match("<CurrentTransportStatus>(.+)</CurrentTransportStatus>")or""end)end;Sonos.setCurrentTrack=function(self,k)Tk:trace("Set current track to %s",Tk.HttpUtility.urlDecode(k))return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.SetAVTransportURI,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID>,<CurrentURI>"..k.."</CurrentURI>,<CurrentURIMetaData></CurrentURIMetaData>")end;Sonos.getCurrentTrack=function(self)Tk:trace("get current track request")return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.GetPositionInfo,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Channel>Master</Channel>",function(a)self.currentTrack.track=tonumber(a:match("<Track>(.+)</Track>")or 0)self.currentTrack.duration=tostring(a:match("<TrackDuration>(.+)</TrackDuration>")or"")self.currentTrack.relTime=tostring(a:match("<RelTime>(.+)</RelTime>")or"00:00:00")self.currentTrack.relCount=tonumber(a:match("<RelCount>(.+)</RelCount>")or 0)self.currentTrack.absCount=tonumber(a:match("<AbsCount>(.+)</AbsCount>")or 0)self.currentTrack.uri=tostring(a:match("<TrackURI>(.+)</TrackURI>")or"")local l=decode(tostring(a:match("<TrackMetaData>(.+)</TrackMetaData>")))self.currentTrack.title=string.gsub(decode(tostring(l:match("<dc:title>(.+)</dc:title>")or"")),1,22)self.currentTrack.creator=decode(tostring(l:match("<dc:creator>(.+)</dc:creator>")or""))self.currentTrack.artist=decode(tostring(l:match("<r:albumArtist>(.+)</r:albumArtist>")or""))self.currentTrack.album=decode(tostring(l:match("<upnp:album>(.+)</upnp:album>")or""))self.currentTrack.originalTrackNumber=tostring(l:match("<upnp:originalTrackNumber>(.+)</upnp:originalTrackNumber>")or"")if string.find(decode(self.currentTrack.uri or""),"x-rincon-mp3radio:",1,true)~=nil then self.currentTrack.isRadio=true;self.currentTrack.isFile=false;if self:getMediaInfo()then self.currentTrack.title=string.gsub(self.mediaInfo.title,1,25)end elseif string.find(decode(self.currentTrack.uri or""),"x-file-cifs:",1,true)~=nil then self.currentTrack.isRadio=false;self.currentTrack.isFile=true else self.currentTrack.isRadio=false;self.currentTrack.isFile=false end end)end;Sonos.getMediaInfo=function(self)Tk:trace("Get current media info request")return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.GetMediaInfo,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><Channel>Master</Channel>",function(a)local l=decode(tostring(a:match('<CurrentURIMetaData>(.+)</CurrentURIMetaData>')))self.mediaInfo.title=decode(tostring(l:match('<dc:title>(.+)</dc:title>')or''))end)end;Sonos.createMetaData=function(m)local n={}table.insert(n,'<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0">')table.insert(n,'<item id="HC2" parentID="-1" restricted="1">')table.insert(n,'<dc:title>'..encode(m.title or"Unknown title")..'</dc:title>')table.insert(n,'<res protocolInfo="'..encode(m.protocol or"x-file-cifs:*:audio/mpeg:*")..'">'..encode(m.uri)..'</res>')table.insert(n,'<upnp:class>"'..encode(m.class or"object.item.audioItem.musicTrack")..'"</upnp:class>')table.insert(n,'<dc:creator>"'..encode(m.creator or"Unknown creator")..'"</dc:creator>')table.insert(n,'<r:albumArtist>"'..encode(m.artist or"Unknown artist")..'"</r:albumArtist>')table.insert(n,'</item>')table.insert(n,'</DIDL-Lite>')return encode(table.concat(n))end;Sonos.playRadio=function(self,k,o)Tk:trace("Set Radio request")self:stop()callback=callback or function(a)Tk:trace("Play sent")end;local p=encode('<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item><dc:title>'..o..'</dc:title><upnp:class>object.item.audioItem.audioBroadcast</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON65031_</desc></item></DIDL-Lite>')return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.SetAVTransportURI,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID><CurrentURI>"..k.."</CurrentURI><CurrentURIMetaData>"..p.."</CurrentURIMetaData>",function()Tk:traceEx("Yellow","Radio: "..o.." pushed")_f:sleep(1000)self:play()_f:sleep(1000)self:play()_f:sleep(1000)self:play()end,true)end;Sonos.playStream=function(self,k,m)Tk:traceEx("green","Play Stream request uri: [%s]",Tk.HttpUtility.urlDecode(k))self:getCurrentTrack()self.lastTrack=self.currentTrack;local q=self.volume;if m.volume~=nil then q=m.volume;self:getVolume()self.lastVolume=self.volume end;self:getMute()self.lastMuteState=self._isMuted;self:getTransportState()self.lastTransportState=self.transportState;self._isPlayingStream=true;local r="auto"if m.duration~=nil then r=m.duration end;local s="x-file-cifs"if m.source~=nil then if m.source=="local"then s="x-file-cifs"elseif m.source=="http"then s="x-rincon-mp3radio"else s="x-sonosapi-stream"end end;local t=Sonos.createMetaData({title=k,protocol=s..":*:audio/mpeg:*",uri=s..":"..k,creator="_f-hc2"})return sendSoapMessage(self.props.controlURL.RendererAVTransport,self.props.serviceType.AVTransport,{name=self.props.actions.SetAVTransportURI,service=self.props.serviceType.AVTransport},"<InstanceID>0</InstanceID>,<CurrentURI>"..s..":"..k.."</CurrentURI>,<CurrentURIMetaData>"..t.."</CurrentURIMetaData>",function(a)if self._isMuted==true then self:mute(false)end;_f:sleep(250)if q~=nil and q~=self.volume then self:setVolume(q)self.streamVolumeIsDifferent=true end;_f:sleep(250)self:play(function(a)if r~=nil then if type(r)=="string"and r=="auto"then Tk:trace("Play TSS with auto stop mode")local u=0;self.transportState="TRANSITIONING"while self.transportState=="TRANSITIONING"do if u>20 then break end;self:getTransportState()Tk:trace(Sonos.transportState)_f:sleep(1000)u=u+1 end;self.transportState="PLAYING"while self.transportState=="PLAYING"do if u>120 then break end;self:getTransportState()Tk:trace(Sonos.transportState)_f:sleep(1000)u=u+1 end;_f:sleep(500)self:stop()elseif type(r)=="number"then r=tonumber(r)Tk:trace("Play Stream for "..r.." seconds")_f:sleep(r)local u=0;self.transportState="TRANSITIONING"while self.transportState=="TRANSITIONING"do if u>20 then break end;self:getTransportState()Tk:trace(Sonos.transportState)_f:sleep(1000)u=u+1 end;if r>1 then r=r-1 end;_f:sleep(r*1000)self:stop()end else Tk:trace("play sent")local u=0;self.transportState="TRANSITIONING"while self.transportState=="TRANSITIONING"do if u>30 then break end;self:getTransportState()Tk:trace(Sonos.transportState)_f:sleep(500)u=u+1 end;local v=0;self.transportState="PLAYING"while self.transportState=="PLAYING"do if v>60 then break end;self:getTransportState()Tk:trace(Sonos.transportState)_f:sleep(2000)v=v+1 end;_f:sleep(500)self:stop()end;_f:sleep(100)if self.streamVolumeIsDifferent==true then self:setVolume(Sonos.lastVolume)self.streamVolumeIsDifferent=false end;_f:sleep(100)if self:setCurrentTrack(self.lastTrack.uri)then if not self.lastTrack.isRadio then self:seek("REL_TIME",self.lastTrack.relTime)_f:sleep(250)end;if self.lastTransportState==self.props.transportState.playing then self:play()elseif self.lastTransportState==self.props.transportState.stopped then self:stop()elseif self.lastTransportState==self.props.transportState.pausedPlayback then self:pause()end end;_f:sleep(250)if self.lastMuteState==false then self:mute(false)else self:mute(true)end;self._isPlayingStream=false end)end)end;Sonos.externalTTSService=function(self,w,x)Tk:trace("Request external TTS service, try to create tts response and return meta object.")x=x or 0;local y=Tk.Net.HttpRequest(w.host,w.port)y:setReadTimeout(2000)local a,z,A=y:request("GET",w.root..'?'..w.args,{"User-Agent: FibaroHC2/1.0","Accept: application/json","Accept-Charset: utf-8"})y:disconnect()y:dispose()y=nil;if A==0 then if tonumber(z)==200 then return json.decode(a)else Tk:trace("status: %s",z)end else Tk:traceEx("red","Communication error code: "..A)if x<10 then Tk:trace("retry #%d",x)_f:sleep(500)return self:externalTTSService(w,x+1)else Tk:trace("Error: Code returned %s",tostring(errorcode or"n.c"))end end;Tk:traceEx("green","uri:%s",string.format("%s:%s/%s?%s",w.host,w.port,w.root,w.args))return{success=false}end;Sonos.parseOnDemandTTS=function(self,B)if string.len(B)>0 then local C={}local D,E;for D,E in string.gmatch(B,"(%w+)=([%w%s?!.,;:-]+)|")do Tk:trace("%s -> %s",tostring(D),tostring(E))C[D]=tostring(E)end;return C end;return nil end;Sonos.getZonePlayer=function(self,j,F)local G={}if F~=nil and F=="ip"then for v,H in ipairs(self.zonePlayers)do if H.ip==tostring(j)then G=H;break end end end;return G end;Sonos.process=function(self)local I=false;if self._debugProcess and Tk.isTraceEnabled~=true then Tk.isTraceEnabled=true;I=true end;local J=nil;local K=DataPersistence:get(_selfId)if K and type(K)=="table"then if K.action~=nil and string.len(K.action)>0 then J=K.action;Tk:traceEx("green","pending action: "..J)end end;if J~=nil and string.len(J)>0 then local L=""for M in string.gmatch(J,"[^%s]+")do if M~=L then self._commands:enqueue(M)else Tk:traceEx("red","duplicate '%s' detected and removed from actions queue:",M)end;L=M end;DataPersistence:set(_selfId,{action=""})while self._commands:count()>0 do Tk:traceEx("green","dequeue command %s",self._commands:peek())local N=nil;N=exec(self._commands:dequeue())while N==nil do _f:sleep(250)end;Tk:traceEx("green","command result is %s",tostring(N))end;_f:call(_selfId,"setProperty","currentIcon",_icons.main)end;local O=nil;if K and type(K)=="table"then if K.stream~=nil and type(K.stream)=="table"then O=K.stream;Tk:traceEx("green","Stream pending")end end;if O~=nil then Tk:traceEx("green","STREAM data found, please wait...")DataPersistence:set(_selfId,{stream=""})local m=O;if m~=nil then if m.stream~=nil and m.stream==""then Tk:traceEx("red","stream location is empty !")os.exit()end;Tk:traceEx("yellow","stream location: %s",m.stream or"n.c")if m.duration~=nil and type(m.duration)=="number"then Tk:traceEx("yellow","stream duration set to: %s seconds",m.duration or"n.c")elseif m.duration~=nil and type(m.duration)=="string"then Tk:traceEx("yellow","stream duration mode: %s seconds",m.duration or"n.c")end;if m.volume~=nil then Tk:traceEx("yellow","stream volume: %s",m.volume or"n.c")end;self:playStream(m.stream,m)end end;local P=nil;if K and type(K)=="table"then if K.tts~=nil and type(K.tts)=="table"then P=K.tts;Tk:traceEx("green","TTS pending")end end;if P~=nil then Tk:traceEx("green","TTS data found, please wait...")DataPersistence:set(_selfId,{tts=""})local m=P;if m~=nil then if m.message~=nil and m.message==""then Tk:traceEx("red","TTS message is empty !")os.exit()end;Tk:traceEx("yellow","TTS message: %s",m.message or"n.c")if m.duration~=nil and type(m.duration)=="number"then Tk:traceEx("yellow","TTS duration set to: %s seconds",m.duration or"n.c")elseif m.duration~=nil and type(m.duration)=="string"then Tk:traceEx("yellow","TTS duration mode: %s seconds",m.duration or"n.c")end;if m.volume~=nil then Tk:traceEx("yellow","TTS volume: %s",m.volume or"n.c")end;if m.language~=nil then Tk:traceEx("yellow","TTS language: %s",m.language or"n.c")end;local k;if UserParams.voiceRssApiKey~=nil and string.len(UserParams.voiceRssApiKey)>0 then local Q="11khz_8bit_mono"if UserParams.voiceRssSoundQuality~=nil and string.len(UserParams.voiceRssApiKey)>0 then if UserParams.voiceRssSoundQuality=="low"then Q="8khz_8bit_mono"elseif UserParams.voiceRssSoundQuality=="medium"then Q="22khz_8bit_mono"elseif UserParams.voiceRssSoundQuality=="high"then Q="44khz_16bit_mono"end end;k="//api.voicerss.org/?key="..UserParams.voiceRssApiKey.."&hl="..tostring(m.language or"fr-fr").."&c=mp3&f="..Q.."&src="..tostring(Tk.HttpUtility.urlEncode(m.message or""))else k="//responsivevoice.org/responsivevoice/getvoice.php?t="..tostring(Tk.HttpUtility.urlEncode(m.message or"")).."&l="..tostring(m.language or"fr-fr")end;m.source="http"self:playStream(k,m)end end end;if DataPersistence==nil then DataPersistence={root="x_sonos_object"}DataPersistence.load=function(self)local j=_f:getGlobalValue(self.root)if j==nil then return end;if string.len(j)>0 then local K=json.decode(j)if K and type(K)=="table"then return K else Tk:traceEx("red","Unable to process data, check variable")end else Tk:traceEx("red","No data found!")end end;DataPersistence.set=function(self,R,K)local i=self:load()if i[tostring(R)]then for v,H in pairs(K)do i[tostring(R)][v]=H end else i[tostring(R)]=K end;_f:setGlobal(self.root,json.encode(i))end;DataPersistence.get=function(self,R)local i=self:load()if i and type(i)=="table"then for v,H in pairs(i)do if tostring(v)==tostring(R or"")then return H end end end;return nil end;DataPersistence.reset=function(self)_f:setGlobal(self.root,json.encode({}))end end;processResponse=function(S,T)if S==nil then return nil end;return S(T)end;sendSoapMessage=function(U,w,V,T,callback,W,x)x=x or 0;local y=Tk.Net.HttpRequest(_ip,_port)y:setReadTimeout(2000)local X=[[
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  	<s:Body>]]..string.format('<u:%s xmlns:u="%s">%s</u:%s>',V.name,V.service,tostring(T or""),V.name)..[[</s:Body>
  </s:Envelope>
  ]]local a,z,A=y:request("POST",U,{'Content-Type: text/xml; charset="utf-8"','SOAPAction: "'..w..'#'..V.name..'"'},X)y:disconnect()y:dispose()y=nil;if A==0 then if tonumber(z)==200 then if callback~=nil then processResponse(callback,a)end;return true else Tk:trace("status: %s",z)end else Tk:traceEx("red","Communication error code: "..A)if W~=nil and W==true then return true end;if x<10 then Tk:trace("retry #%d action: %s",x,V.name)_f:sleep(1000)return sendSoapMessage(U,w,V,T,callback,W,x+1)else Tk:trace("Error: Code returned %s",tostring(errorcode or"n.c"))end end;return false end;encode=function(Y)if Y~=nil then return Y:gsub("&","&".."amp;"):gsub("<","&".."lt;"):gsub(">","&".."gt;"):gsub('"',"&".."quot;"):gsub("'","&".."apos;")end;return""end;decode=function(Y)if Y~=nil then return Y:gsub("&".."#38;",'&'):gsub("&".."#60;",'<'):gsub("&".."#62;",'>'):gsub("&".."#34;",'"'):gsub("&".."#39;","'"):gsub("&".."lt;","<"):gsub("&".."gt;",">"):gsub("&".."quot;",'"'):gsub("&".."apos;","'"):gsub("&".."amp;","&")end;return""end;exec=function(Z)if Z=="PLAY"then Tk:traceEx("yellow","Execute command: Play")if Sonos:play()then _f:log("Play")_f:call(_selfId,"setProperty","ui.lblDebug.value","Play")return true end elseif Z=="PAUSE"then Tk:traceEx("yellow","Execute command: Pause")if Sonos:pause()then _f:log("Pause")_f:call(_selfId,"setProperty","ui.lblDebug.value","Pause")return true end elseif Z=="STOP"then Tk:traceEx("yellow","Execute command: Stop")if Sonos:stop()then _f:log("Stop")_f:call(_selfId,"setProperty","ui.lblDebug.value","Stop")return true end elseif Z=="PREV"then Tk:traceEx("yellow","Execute command: Previous")if Sonos:previous()then _f:log("Previous")_f:call(_selfId,"setProperty","ui.lblDebug.value","Previous")return true end elseif Z=="NEXT"then Tk:traceEx("yellow","Execute command: Next")if Sonos:next()then _f:log("Next")_f:call(_selfId,"setProperty","ui.lblDebug.value","Next")return true end elseif Z=="SEEKL"then Tk:traceEx("yellow","Execute command: Seek left")if Sonos:seekL()then _f:log("Seek left")_f:call(_selfId,"setProperty","ui.lblDebug.value","Seek left")return true end elseif Z=="SEEKR"then Tk:traceEx("yellow","Execute command: Seek Right")if Sonos:seekR()then _f:log("Seek Right")_f:call(_selfId,"setProperty","ui.lblDebug.value","Seek Right")return true end elseif Z=="LNON"then Tk:traceEx("yellow","Execute command: Loudness On")if Sonos:loudness(true)then _f:log("Loudness On")_f:call(_selfId,"setProperty","ui.lblDebug.value","Loudness On")return true end elseif Z=="LNOFF"then Tk:traceEx("yellow","Execute command: Loudness Off")if Sonos:loudness(false)then _f:log("Loudness Off")_f:call(_selfId,"setProperty","ui.lblDebug.value","Loudness Off")return true end elseif Z=="MUTE"then Tk:traceEx("yellow","Execute command: Mute")if Sonos:mute(true)then _f:log("Mute")_f:call(_selfId,"setProperty","ui.lblDebug.value","Mute")return true end elseif Z=="UNMUTE"then Tk:traceEx("yellow","Execute command: UnMute")if Sonos:mute(false)then _f:log("UnMute")_f:call(_selfId,"setProperty","ui.lblDebug.value","UnMute")return true end elseif Z=="TMUTE"then Tk:traceEx("yellow","Execute command: Toggle mute")if Sonos:muteInvert()then _f:log("Toggle mute")_f:call(_selfId,"setProperty","ui.lblDebug.value","Toggle mute")return true end else local _=Z:match("VOL(%d+)")if _~=nil then Tk:traceEx("yellow","Execute command: Set volume")_f:call(_selfId,"setProperty","ui.slVolume.value",tonumber(_))if Sonos:setVolume(tonumber(_))then _f:log("Set volume to "..Sonos.volume or"n.c")_f:call(_selfId,"setProperty","ui.lblDebug.value","Set volume to "..Sonos.volume or"n.c")return true end end;local _=Z:match("RST(%d+)")if _~=nil then Tk:traceEx("yellow","Execute command: Set radio station")local K=DataPersistence:get(_selfId)if K~=nil and K.radioStations~=nil then local a0=#K.radioStations;if a0==0 or tonumber(_)>a0 then Tk:traceEx("yellow","Cannot execute command: no radio station to play!")os.exit()end;Tk:traceEx("yellow","Number of radio:  "..a0)local a1=K.radioStations[tonumber(_)]Tk:traceEx("yellow","Try to play radio: "..a1.title)local a2=encode(a1.res)if Sonos:playRadio(a2,encode(a1.title))then local a3="Set radio station to "..a1.title or"n.c"_f:log(a3)_f:call(_selfId,"setProperty","ui.lblDebug.value",a3)return true end else Tk:traceEx("red","No data found!")end end end;return false end;Tk.isTraceEnabled=false;Sonos:process()

Wenn ich unter ID die 28 eintrage dann “übersetzt” er es mir beim speichern in “button_15_0”

Hast du dazu noch eine Idee oder suche ich an der falschen Stelle?

Bitte am VD nichts ändern.
Das VD benötigt die Szene und umgekehrt.
Und Du steuerst TTS nur über diese Szene (oder eine andere, die dann auch wieder auf das VD zurückgreift).
Mit dem Process-Button kannst Du so nichts anfangen, der ist nur für die Szene wichtig.

Hi Hoggle,

so, ich bin nun auch wieder Online, nachdem ein Bagger unser Kabel zerstört hatte…

Alles klar, das wird dann nun ein wneig deutlicher für mich.

Gut, dann ändere ich da nichts dran. Kannst Du mir noch einen Tipp geben, wie ich die Radio Stationen belegen kann?
Ich hatte es schon mit variablen versuchst, dass klappt aber nicht, oder ich habe was falsch gemacht.

Hi,

mit dem Commander kannst Du den Weg über die globale Var nehmen (das nutze ich).

Mit dem Remote-VD werden wohl die Favoritennamen aus der Sonos-APP über die vorbelegten Buttons abgefragt:

.....
response = sonos:GET("/"..zonename.."/Favorite/4FM"
...

In dem Beispiel gibt es sicherlich einen Radio-Favoriten, der “4FM” heißt.

VG Hoggle

Hi Hoogle,

ich muss leider nochmal nachfragen, da ich noch nicht so Firm in dem Them bin.

Also: Du nutzt das Remote VD nur um Voice Rss zu nutzen, richtig?
Um dann die entsprechenden Sender anzuspielen nutzt Du den Remote VD.

Darf ich Dich fragen wie Du das verknüft hast?
Ich würde ja gerne, dass morgens die SONOS starte wenn ich zwischen 6-730 das Bad besuche, aber auch nur wenn kein Wochenende oder (um es noch komplexer zu machen) Feiertag ist.

Das folgende habe ich schon gefunden:

–[[ 
%% properties 
46 value
5 value
%% globals 
–]]
— MOTION-Sensor muss unter %% properties aufgeführt sein
— Definitionen
local scene = 6; — ID dieser Szene
local sonosCommander = 46; — ID des SONOS Commanders
local motion = 5; — ID des Bewegungssensors
local volume = 20; — Lautstärke festlegen
local time = 0;
local currentTime = os.date("%H:%M");
local timereset = 60; — Verzögerung (in Sekunden) nach der letzten Bewegung, bevor der Radio ausschaltet.
fibaro:setGlobal(„SONOS_RADIO“, „player.ffn.de/ffn.mp3„); — Radio Sender der globalen Variabel zuweisen
— Lösche vorhandene Szenen
if (fibaro:countScenes()>1) then 
fibaro:abort(); 
end
— Bewegung erkannt & Radio einschalten
if ( currentTime >= "06:00" and currentTime < "07:30") and tonumber(fibaro:getValue(motion, „value“)) > 0 then
if tonumber(fibaro:getValue(motion, „value“)) > 0 then 
fibaro:call(sonosCommander, „setSlider“, „7“, volume); — Lautstärke festlegen
fibaro:call(sonosCommander, „pressButton“, „1“); — Radio starten
fibaro:debug(„1: Starte Radio“);
— Schleife die prüft ob in der Zwischenzeit wieder eine Bewegung erkannt wurde. (Falls ja, wird der Timer zurückgesetzt) 
repeat
if tonumber(fibaro:getValue(motion, „value“)) > 0 then
time = 0;
else
time = time + 1;
end
fibaro:sleep(999);
fibaro:debug(„2: Zeit ohne Bewegung“ .. time ..“ von “ .. timereset);
until time > timereset
— Schalte Radio aus, da Timer abgelaufen
fibaro:call(sonosCommander, „pressButton“, „4“); — Radio anhalten
fibaro:debug(„3: Timer abgelaufen, Radio ausgeschalten.“);
fibaro:killScenes(scene); 
end

Wenn ich ehrlich bin, weiß ich garnicht wo ich was ergönzen, oder verknüfen muss…

Hi,

für TTS nutze ich das Remote VD nicht mehr sondern geh über die Sonos-API und einen RPI.

Für bewegungsmeldergesteuerte Sonos-Start nutze ich den Commander.
dazu müssen die Radiosender in der globalen Var (“SONOS_RADIO”) vordefiniert werden, z.B:


--[[
%% properties
392 value
%% globals
--]]

--Wenn der Bewegungsmelder im SchalZi Mo-Fre zw 5:50-6:20 UHr Bewegung registriert,
--wird das Radio im Bad gestartet 
--Die GV "Aufstehen_Bad" wird in der Szene "Reset BewegVorKueche" um 0:10 Uhr wieder auf Null gesetzt

local currentTime = os.date("%H:%M");
local startSource = fibaro:getSourceTrigger();
local currentDate = os.date("*t");

 if ( currentTime >= "05:45"  and currentTime < "06:20" and
    ( (currentDate.wday == 2 or currentDate.wday == 3 or currentDate.wday == 4 or currentDate.wday == 5 or currentDate.wday == 6) ) and
      -- nur Mo bis Freitag
    ( tonumber(fibaro:getValue(392, "value")) > 0 ) and
    ( tonumber(fibaro:getGlobalValue("Aufstehen_Bad")) == tonumber("0") ) and
  	  tonumber(fibaro:getGlobalValue("Feiertag_heute")) == tonumber("0") )
  
then
  fibaro:setGlobal("SONOS_RADIO", "radiohamburg.hoerradar.de/radiohamburg-live-mp3-192?");
  fibaro:call(254, "setSlider", "6", "6");
  --letzter Wert setzt Lautstärke
  fibaro:call(254, "pressButton", "1");
        
  fibaro:setGlobal("Aufstehen_Bad", "1");
else
  --fibaro:debug("es ist NICHT zwischen der relevantzen Zeit - no action");
end
 

die zusätzliche globale Var “Aufstehen_Bad” benötige ich, damit Sonos im Bad nur einmal gestartet wird.

VG Hoggle

392 ist mein MS
254 der Sonos Commander (im Bad)

Die globale Var “Feiertag_heute” kommt aus dem Feiertags-Script

Moin Hoogle,

ok, verstanden, da muss ich ggf. nochmal an die RPI Lösung dran, ich denke es wird auch nicht wirklich eine Lösung geben, dass man das koppelt, oder?

Du schreibst:

die zusätzliche globale Var „Aufstehen_Bad“ benötige ich, damit Sonos im Bad nur einmal gestartet wird

Wie meinst Du das? Das habe ich nicht verstanden…

Das Feiertagsscript habe ich gefunden:

%% autostart
%% properties
%% globals
--]]
 
-- ************************************************************************
-- FEIERTAGE-SCRIPT
-- ************************************************************************
 
-- Das Skript berechnet die eidg./kant. Feiertage im aktuellen Jahr und prüft,
-- ob heute oder morgen ein Feiertag ist und hinterlegt das Ergebnis in den
-- Systemvariablen "Feiertag_heute" und "Feiertag_morgen".
 
-- (c) Simon Betschmann (01-2015)
-- Version: 1.0
-- Basierend auf einer Idee von Peter Beck (01-2011)
 
-- Es werden folgende Systemvariablen benötigt:
-- Feiertag_heute
-- Feiertag_morgen
 
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- EINSTELLUNGEN
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
-- Bitte nur in dieser Sektion Einstellungen vornehmen
local debuging = false -- TOGGLE DEBUGING (true|false)
local Kanton = "ZH" -- Kanton angeben für kantonale Feiertage (ZH, AG,...)
-- leer lassen, um Funktion auszuschalten
 
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- DEBUG
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
-- Grundfunktion: Debug("red", "Nachrichtentext")
Debug = function(color, message)
if (debuging) then
fibaro:debug(string.format('<%s style="color:%s;">%s', "span", color, message, "span"))
end
end
 
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- FEIERTAGE BERECHNEN
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
function FeiertageFunc()
 
local Datum = os.date("%d.%m.") -- heutiges Datum (ohne Jahr)
local i = 1
 
-- OSTERDATUM BERECHNEN
local Jahr = os.date("%Y") -- Das aktuelle Jahr ermitteln
local oTag -- Das Tagesdatum vom Ostersonntag
local oMonat -- Das Monatsdatum von Ostersonntag
local oDatum -- Das komplette Datum vom Ostersonntag
 
local mTage -- Variable für die interne Berechnung
local LVar1 -- Variable für die interne Berechnung
local LVar2 -- Variable für die interne Berechnung
local LVar3 -- Variable für die interne Berechnung
local zahl -- Variable für die interne Berechnung
 
-- Interne Funktion zum Runden auf Ganzzahlen
function round(x)
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end
 
-- Die modifizierte Gauss-Formel nach Lichtenberg
local LVar1 = ((19 * (Jahr % 19)) + (15 + (((3 * (Jahr / 100)) + 3) / 4) - (((8 * (Jahr / 100)) + 13) / 25))) % 30;
local LVar2 = (LVar1 / 29) + (((LVar1 / 28) - (LVar1 / 29)) * ((Jahr % 19) / 11));
local LVar3 = 7 - (((21 + LVar1 - LVar2) - (7 - ((Jahr + (Jahr / 4) + (2 - (((3 * (Jahr / 100)) + 3) / 4))) % 7))) % 7);
local oTag = round((21 + LVar1 - LVar2) + LVar3);
 
-- Monat und Tag von Ostern bestimmen
if (oTag > 31) then -- Der Monat März hat nur 31 Tage -> Ostern im April
oMonat = "04";
oTag = oTag - 31;
else
oMonat = "03"; -- Ostern ist im März
end
 
-- Das Ergebnis in Variable oDatum hinterlegen
if (oTag < 10) then
oDatum = "0" .. oTag .. "." .. oMonat .. "." .. Jahr;
else
oDatum = oTag .. "." .. oMonat .. "." .. Jahr;
end
Debug("gray", "Osterdatum: ".. oDatum)
 
-- Ermitteln, wieviele Tage der Februar im aktuellen Jahr hat
zahl = Jahr % 4;
 
if (zahl == 0) then -- Ein Schaltjahr -> Feb. = 29 Tage
mTage = {31,29,31,30,31,30,31,31,30,31,30,31}; -- Anzahl der Tage im Monat Jan.-Dez.
Debug("gray", Jahr.. " ist ein Schaltjahr.")
else
mTage = {31,28,31,30,31,30,31,31,30,31,30,31}; -- Anzahl der Tage im Monat Jan.-Dez.
Debug("gray", Jahr.. " ist kein Schaltjahr.")
end
 
-- Feiertage mit dem heutigen & morgigen Datum vergleichen und Ergebnis
-- in den Variablen "Feiertag_heute" bzw. "Feiertag_morgen" speichern
while i < 3 do
 
local Feiertag = 0
 
-- FESTE FEIERTAGE IN DER GANZEN SCHWEIZ
if (Datum == "01.01.") then Feiertag = 1 end -- Neujahr
if (Datum == "01.08.") then Feiertag = 1 end -- Nationalfeiertag
if (Datum == "25.12.") then Feiertag = 1 end -- Weihnachten
if (Datum == "26.12.") then Feiertag = 1 end -- Stephanstag
 
-- FEIERTAGE IM KANTON ZÜRICH
if (Kanton == "ZH") then
if (Datum == "01.05.") then Feiertag = 1 end -- Tag der Arbeit
end
-- FEIERTAGE IM KANTON AARGAU
if (Kanton == "AG") then
if (Datum == "02.01.") then Feiertag = 1 end -- Berchtoldstag
end
 
-- VARIABLE FEIERTAG IN DER GANZEN SCHWEIZ
-- Aray mit Differenztagen zum Ostersonntag:
-- Ostermontag, Karfreitag, Auffahrt, Pfingstmontag
local feiertage = {1,-2,39,50}
 
-- VARIABLE FEIERTAGE: DATEN BERECHNEN
oTage = mTage[tonumber(oMonat)] -- Anzahl Tage im Ostermonat
Debug("grey", "Anzahl Tage im Ostermonat: "..oTage)
 
local fDatum; -- Feiertagsdatum
local fDiff; -- Anzahl der Tage vom Ostersonntag entfernt
local Schleife; -- Für die Berechnungen in der while-Schleife notwendig
 
for x in next, feiertage do
 
fDiff = feiertage[x]
Schleife = true;
 
zahl = oTag + fDiff; -- Differenztage zum Referenztag hinzurechnen
Debug("grey", "Zahl: "..zahl..", oTag: "..oTag..", fDiff: "..fDiff..", index: "..x)
 
if (fDiff > 0) then
if (zahl > oTage) then
zahl = zahl - oTage; -- Sind wir ausserhalb des Referenzmonats?
Debug("grey", "Ausserhalb des Referenzmonats > Schleife durchlaufen.")
else -- Nein => Berechnungschleife nicht durchlaufen.
Schleife = false;
LVar2 = oMonat;
Debug("grey", "Innerhalb des Referenzmonats > Schleife nicht durchlaufen.")
end
else
if (zahl > 0) then -- Wenn innerhalb des Referenzmonats => Berechnungsschleife nicht durchlaufen.
Schleife = false;
LVar2 = oMonat;
Debug("grey", "Innerhalb des Referenzmonats > Schleife nicht durchlaufen.")
else
Debug("grey", "Ausserhalb des Referenzmonats > Schleife durchlaufen.")
end
end
 
LVar1 = 0;
 
-- Schleife 'Monatsberechnung', falls nicht im gleichen Monat wie Ostern
if (fDiff > 0) then
while (Schleife) do
LVar1 = LVar1 + 1.00;
LVar2 = oMonat + LVar1;
zahl = zahl - mTage[tonumber(oMonat)];
if (zahl <= 0) then -- Aktueller Monat gefunden
Schleife = false;
zahl = zahl + mTage[tonumber(oMonat)] -- Wieder addieren, um den Tag zu berechnen
end
end
else
if (fDiff < 0) then
while (Schleife) do
LVar1 = LVar1 + 1.00;
LVar2 = oMonat - LVar1;
zahl = zahl + mTage[tonumber(oMonat)]
if (zahl > 0) then -- Aktueller Monat gefunden
Schleife = false;
end
end
end
end
 
-- Datum des Feiertags berechnen
Debug("grey", "Datum des Feiertags berechnen...")
if (zahl < 10 and tonumber(LVar2) < 10) then
fDatum = "0" .. zahl .. ".0"..tonumber(LVar2).. ".";
elseif (zahl < 10) then
fDatum = "0" .. zahl .. "."..tonumber(LVar2).. ".";
elseif (tonumber(LVar2) < 10) then
fDatum = zahl .. ".0"..tonumber(LVar2).. ".";
else
fDatum = zahl .. "."..tonumber(LVar2).. ".";
end
 
Debug("white", "Feiertag: "..fDatum)
 
-- Prüfen, ob Datum heute/morgen mit Feiertag übereinstimmt
if (Datum == fDatum) then
Feiertag = 1
fibaro:debug("Feiertag ("..fDatum..") gefunden.")
end
 
end
 
-- VARIABLEN SETZTEN
if i == 1 then
-- 1. Durchgang: Datum heute mit den Feiertagen prüfen
Debug("grey", "1. Durchgang: Datum heute="..Datum..", Feiertag="..Feiertag)
fibaro:setGlobal("Feiertag_heute", Feiertag)
i = 2
Datum = os.date("%d.%m.", os.time()+24*60*60) -- Datum auf morgen setzen
else
-- 1. Durchgang: Datum morgen mit den Feiertagen prüfen
Debug("grey", "2. Durchgang: Datum morgen="..Datum..", Feiertag="..Feiertag)
fibaro:setGlobal("Feiertag_morgen", Feiertag)
i = 3
end
 
end
 
-- ERGEBNIS AUSGEBEN
if fibaro:getGlobal("Feiertag_heute") == 0 then
fibaro:debug("Heute ist kein Feiertag.")
else
fibaro:debug("Heute ist ein Feiertag. Variable gesetzt!")
end
if fibaro:getGlobal("Feiertag_morgen") == 0 then
fibaro:debug("Morgen ist kein Feiertag.")
else
fibaro:debug("Morgen ist ein Feiertag. Variable gesetzt!")
end
 
end
 
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- SZENE JEDEN TAG UM 00:01 AUSFÜHREN
-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
local sourceTrigger = fibaro:getSourceTrigger();
 
function tempFunc()
local currentDate = os.date("*t");
local startSource = fibaro:getSourceTrigger();
if ((((currentDate.wday == 1 or currentDate.wday == 2 or currentDate.wday == 3 or currentDate.wday == 4 or currentDate.wday == 5 or currentDate.wday == 6 or currentDate.wday == 7) and string.format("%02d", currentDate.hour) .. ":" .. string.format("%02d", currentDate.min) == "00:01") )) then
FeiertageFunc()
end
setTimeout(tempFunc, 60*1000)
end
 
if (sourceTrigger["type"] == "autostart") then
tempFunc()
else
local currentDate = os.date("*t");
local startSource = fibaro:getSourceTrigger();
if (startSource["type"] == "other") then
FeiertageFunc()
end
end

Ich denke dass ich das nur als Szene anlegen muss? Oder muss ich noch was tun dass es geht…

Hi,

zu dem Feitertags-Script gibt es auch eine deutsche Variante - außer Du lebst in der Schweiz.

Sonos soll nur beim ersten mal angehen, wenn ich aufstehe, dafür benötige ich die globale Var „Aufstehen_Bad“.
Wenn noch jemand den MS passiert (später) soll Sonos nicht noch einmal angehen.

VG Hoggle

Hi Hoggle,

danke erstmal für Deinen ganzen Support, sowas ist nicht selbstverständlich.

Ich habe nun das Script im Forum auf Deutsch gefunden, danke nochmal für den Hinweiß…

Also wenn ich gerne möchte, dass es in der Zeit immer angeht, müsste das Script so aussehen, oder?

--[[
%% properties
392 value
%% globals
--]]
 
--Wenn der Bewegungsmelder im SchalZi Mo-Fre zw 5:50-6:20 UHr Bewegung registriert,
--wird das Radio im Bad gestartet 
--Die GV "Aufstehen_Bad" wird in der Szene "Reset BewegVorKueche" um 0:10 Uhr wieder auf Null gesetzt
 
local currentTime = os.date("%H:%M");
local startSource = fibaro:getSourceTrigger();
local currentDate = os.date("*t");
 
 if ( currentTime >= "05:45"  and currentTime < "06:20" and
    ( (currentDate.wday == 2 or currentDate.wday == 3 or currentDate.wday == 4 or currentDate.wday == 5 or currentDate.wday == 6) ) and
      -- nur Mo bis Freitag
    ( tonumber(fibaro:getValue(392, "value")) > 0 ) and
    ( tonumber(fibaro:getGlobalValue("Aufstehen_Bad")) == tonumber("0") ) and
  	  tonumber(fibaro:getGlobalValue("Feiertag_heute")) == tonumber("0") )
  
then
  fibaro:setGlobal("SONOS_RADIO", "radiohamburg.hoerradar.de/radiohamburg-live-mp3-192?");
  fibaro:call(254, "setSlider", "6", "6");
  --letzter Wert setzt Lautstärke
  fibaro:call(254, "pressButton", "1");
        
  --fibaro:setGlobal("Aufstehen_Bad", "1");
else
  --fibaro:debug("es ist NICHT zwischen der relevantzen Zeit - no action");
end