VD – Virtuel Device eller virtuell enhet – kan det vara något?

Detta är inte så mycket LUA i sig, men något som i vissa lägen kräver LUA, således kan jag inte låta bli att skriva några rader om det. VDs är mycket viktiga för att få till det där sista och den där flexibiliteten. Jag kommer dels beskriva hur man skapar, exporterar och importera VDs. Men även hur man kan styra sin stereo (eller liknande) enhet via LUA eller den inbyggda versionen av VD…

Låt oss börja med hur man skapar en VD, och det gör man t.ex. från:

Dvs välj först Enheter, klicka sedan på “Lägg till eller ta bort enhet”, vilket för dig till:

Klicka således på “Lägg till” där jag markerat med rött – då skapas en ny virtuell enhet.

Observera att här finns också möjligheten att importera en VD från fil, t.ex. om man vill återanvända någon annans VD i sin egna installation – detta är rätt vanligt för de som vill styra just stereo eller andra media enheter. Jag menar, varför uppfinna hjulet flera gånger?

När du sedan skapat din nya virtuella enhet, så börja med att döpa den till något, och placera den i något rum. I mitt fall så får den heta “VD Test” och jag placerar den i det rum jag döpt till “Diverse” där allt möjlig omöjligt hamnar…

Och tryck SAVE ute till höger (den blåmarkerade iconen):

Nu har du skapat din första VD. Som du också ser finns några fält som kan innehålla IP-adress eller TCP-Port. Vi lämnar dessa blanka för nu, jag återkommer till när och varför man kan använda dessa fält lite senare.

Så det första vi skall göra är att orientera oss lite, och testa på något enkelt. Således, vi börjar stenhårt med att välja “Avancerat”-fliken:

Jahapp – nu dök det upp en hel del. Jag tänker börja med det som finns längst ned, där det står “Main loop” och med en enkel LUA för att se vad som händer. “Main loop” är en rutin som snurrar hela tiden, om den fylls med LUA kod – för här kan man bara koda i LUA:

Observera att jag alltså bara har en fibaro:debug() och inget mera, och att jag sedan sparat VDn – för man måste alltid göra SAVE ute till höger innan man kör VDn. Om det inte är sparat så kommer koden du skrivit att försvinna / inte köras. Så det är bara att vänja sig vid, om inte annat den hårda vägen.

I vilket fall, du har skrivit in koden, och gjort save, och nu trycker du på knappen som säger “Debug” längst ned, och ut kommer ett nytt fönster “Enhetslista” (jag skulle vilja ta en sväng med den som gjort översättningen…), och tryck då på knappen “Kör”, så ser du troligen:

Att för varje gång koden körs så skrivs raden ut – observera dock att det inte är varje sekund som det står om man läser innantill, utan ja i mitt fall var 3e sekund som koden körs. Dvs HC2 kör main loop, och sedan vilar den 3 sekunder, och sedan körs koden igen. Om man då lägger till en fibaro:sleep(3*1000) – dvs sov i 3 sekunder (fibaro:sleep sover i antalet millisekunder som anges, därav 3 * 1000 ms = 3 sekunder) – så sker föga oväntat:

Observera alltså att vi låter snurran sova i 3 sekunder, och HC2 låter snurran i sig vila i 3 sekunder innan nästa varv körs = total blir det var 6e sekund som LUA koden körs. Inte superviktigt, men som sagt, varje sekund är det inte ;-)

[u]Kom nu ihåg att ta bort denna kod[/u], om du testat samma sak som jag just beskrev – denna kod körs bevisligen kontinuerligt, så bäst är att lämna “Main loop” tom tills man verkligen behöver använda denna rutin.

Om vi nu går upp lite på denna VD vi just skapat, under fliken “Avancerad” så syns en bild med en massa knappar:

Och det är här man alltså börjar normalt sett för att skapa olika händelser/aktiviteter med hjälp av VDs. Jag skulle tro att de flesta som tittar på VDs gör det för att styra t.ex. sin stereo eller liknande – dvs skapa ett gränssnitt mot andra produkter som går att kontrollera direkt eller indirekt via IP trafik (dvs http av något slag). Jag kommer visa på några enkla varianter, men avhålla mig från mera avancerade lösningar, eftersom ja med avancerade lösningar tillkommer otroligt snåriga felsökningar när saker och ting inte håller ihop längre, och man inte förstår (?) varför inget händer, eller allt står och blinkar typ…

Vi börjar med den första “knappen” som kallas Label – detta är inte en knapp i sig, utan ett fält som visar information, t.ex. värdet av en global variabel eller någon annan form av status information som man vill kunna visa. En Label kan man inte gör något med, utan alltså bara visa något – således ingen knapp isig. Men för sakens skull så börjar vi med att skapa en Label som vi senare skall använda. Vi bockar alltså i Label:

Och trycker på knappen längst ned i bilden “Add set”, och efter det SAVE (så att det sparas – om du inte trycker SAVE så riskerar du att förlora alla ändringar och det är inte kul). Då ser det ut lite såhär:

Som ni ser längst ned har jag tömt “Main loop” – så inget kört. Och högst upp ser vi att själva VD’ns presentation har ändrats till:

Vi ser alltså fältet som skapats, och det benämns med “Label 1″ – dvs default värde när man skapar en Label. Som synes i nedre delen av bilden ovan så är det alltså så att man inte kan mata in något för Label fält utan dom är så att säga tomma på logik. Vi återkommer till detta senare.

Nu vill vi dock skapa en riktig knapp, där vi verkligen kan göra något:

Så samma ramsa som med Label men nu skapar vi en riktig knapp – EN knapp på EN rad (vi kommer till flera snart), och denna gång (efter att ha tryckt “Add set” och sedan SAVE) så kommer vi använda logik för att tända 3 lampor samtidigt med EN knapptryckning (utan att skapa en scen). När man skapar en knapp så kommer vi till standardläget där all text är i blått typ:

I detta läget som knappen befinner sig i, så kan man (om man fyllt i IP-adress och TCP-port på första fliken “Allmänt” – mer om det senare) sända enkla IP kommandon till olika enheter som man har som TV eller Stereo produkter som är anslutna via TCP/IP – men jag kommer till det snart.

Vi kommer dock gå ut hårt, och skriva LUA direkt, så längst ned på knappen finns en möjlighet att byta från “String” till “LUA-kod”:

Klicka i “LUA-kod” och knappen byter utseende till:

Och nu kan vi börja koda i LUA på denna knapp. Och som jag skrev tidigare kommer vi att använda denna knapp för att tända lite lampor. Det första vi gör är att döpa knappen till något vettigt, som t.ex. “Tänd Lampor” och sedan in med lite LUA kod:

Och för den som vill kopiera själva LUA koden:

-- Golvlampor Stereo/Liten/Stor, då det är dimmerpuckar
-- så måste vi skicka vilket dimmervärde vi önskar tända med,
-- och i detta fallet vill vi få maximalt värde, alltså 100%
  	fibaro:call(29, "setValue", "100");
	fibaro:call(60, "setValue", "100");
	fibaro:call(64, "setValue", "100");

Som ni ser av kommentaren så skall jag alltså styra så att alla tre lamporna går max när jag trycker på VD knappen “Tänd Lampor”. Vill jag göra motsatsen – dvs släcka lamporna så är det bara att ändra sista värdet från 100 till 0 på samtliga tre rader:

	fibaro:call(29, "setValue", "0");
	fibaro:call(60, "setValue", "0");
	fibaro:call(64, "setValue", "0");

Glöm inte att trycka SAVE innan du vill testa din knapp ;-)

Om du inte har dimmerpuckar som i mitt fall ovan, så tänder och släcker du med LUA på detta sättet:

fibaro:call(142, "turnOn") -- Tänd lampa

 

fibaro:call(142, "turnOff") -- Släck lampa

Således nu har vi skapat vår allra först VD med en enkel knapp som bara tänder lamporna maximalt. Inte speciellt svårt eller hur – bara en massa klickande, och kom ihåg SAVE!!!

Jag gillar att använda VDs istf scener när det är något specifikt jag vill göra som att tända en rad lampor eller något liknande, och sedan anropa en VD’s knapp istf. att koda i LUA på flera ställen som gör samma sak. Dvs jag samlar ihop flera lampor under en VD som en form av “paraply” knapp. På det sättet kan jag byta EnhetsID för lampor som jag lägger till eller tar bort, utan att påverka mina scener som tänder och släcker. Jag får helt enkelt ett mellanlager som håller reda på själva grundfunktionerna som just lampors EnhetsIDn så om jag tvingas byta just en fibaro puck i väggen eller något liknande, så slipper jag koda om alla scener, jag byter bara EnhetsID inne i VDn – och därmed förenklar jag radikalt just vanligt underhåll (som verkar behövas, dvs byte av EnhetsID på puckar med mera). Att jag sedan får de snyggt grupperade är lite en bonus. Detta är för övrigt lite av det jag ibland kallar “NG” – Next Generation av hemautomatisering och samtidigt en förberedelse inför bytet till version 4.x (och evnt byte av puckar, om det nu blir Qubino eller nya Fibaro puckar när dom väl kommer). Bra att hålla i minnet är alltså att kapsla in olika lampor i VDs för att på det sättet hantera framtida förändringar på ett något lättare sätt.

Nu skall vi ge oss på något lite mera avancerat och det jag tror flertalet tänker på i första hand: Styra stereon…

Vi börjar med att skapa en Två delad knapp, alltså möjligheten att på en rad i VDn få två knappar:

Ni känner igen er eller hur – kom ihåg att trycka “Add set” och därefter SAVE. Såhär ser det ut om ni scrollar ned lite (beror iofs på hur stor skärm ni har, och på vilken ledd):

Två knappar alltså. Och denna gång skall vi använda JUST det “blåa” läget där vi utnyttja de inbyggda funktionerna för att sända TCP/IP kommandon, och denna gång till en Marantz receiver (detta passar alla kända Marantz som jag vet finns, både SR och NR serien, och troligen råkar det även fungera till Denon som delar mycket av plattformen med Marantz då de tillverkas av samma fabrik/ägare, D&M Holding). Således får vi gå tillbaka till första fliken “Allmänt” och mata in IP-adress och TCP-port. Och i mitt fall så skall det se ut såhär:

Efter detta så byter vi tillbaka till fliken som heter “Avancerat” och scrollar ned till de två knapparna vi just lagt till, och i detta fallet så skall vi göra det som på Marantz språk kan kallas “Zon Main On” resp “Zon Main Off”, och döpa knapparna till lite vettigt då.

När man skapar knappar på detta sättet, och här är det viktigt att hålla reda på hur varje enhet kommunicerar. I Marantz fallet så skall man alltid avsluta med ett CHR(13) – en vagnretur eller ävne ibland kallar . Denna vagnretur har värdet CHR(13) i ASCII språket som är det man nyttjar i detta fallet. För den som inte hänger med, så föreslår jag att ni tar det lugnt och inte hänger upp er på detaljerna kring ASCII och CR – utan bara accepterar att det fungerar typ. Eftersom värdet CHR(13) inte går att mata in sådär rakt av så måste vi ta till ett knep för att beskriva värdet, och detta genom hexadecimal konvertering, eller 0x0D – där 0x talar om att nu kommer ett hexadecimalt värde, och det värdet är 0D – dvs 13 enligt det hexadecimala talsystemet. Oki? Well let’s hope so anyway – det blir mycket tekniskt här inser jag, saker som jag är van vid dagligen men som inte alltid är uppenbart…

I vilket fall, kommandot till en Marantz receiver för att slå på huvudzonen är:

ZMON

= Zon Main ON och sedan måste vi lägga på CarrigeReturn (dvs. CHR(13)) således blir det:

ZMON0x0D

Det är allt vi behöver, och för att slå av blir det:

ZMOFF0x0D

Snyggt enkelt och ja hyggligt rakt fram:

Vi har nu skapat den första externa kommunikationen UT från en Fibaro HC2 – och det med standardfunktionerna.

Men det är inte alltid detta fungerar, utan ibland måste man ta till lite special knep, som i fallet med min SqueezBox. Den kräver en del formatering av meddelande som inte direkt stöds av Fibaro’s variant av TCP/IP kommunikation, och således får man ta till LUA. Jag tänker nu inte gå in alltför djupt i detta, men jag vill ändå visa vad man kan göra:

Alltså skapa en knapp, “Add set” och SAVE. Byt till LUA-kod, och sedan kan man knappa in koden:

-- SqueezServer IP
local ipadd = '192.168.1.44'
local portno = 9090
local player = "00:04:20:16:e9:28 "
-- the cmnd string
local cmnd = "power 1 "
-- construct the sting to send
local stringtosend = player .. cmnd .. string.char(10)
tcpSocket = Net.FTcpSocket ( ipadd , portno)
tcpSocket:setReadTimeout(2000)
--send the command
bytes, errorCode = tcpSocket:write (stringtosend)

Den som behöver hjälp med detta bör nog konsultera forumet först. Det blir en del special lätt som inte alltid är så lätt att hantera såhär på en introduktion till VDs. Men var trygg i att man kan göra mer än man kanske först anar. T.ex. kan man anropa HC2 och dra ut JSON koden som därmed talar om exakt hur något är skapat och därifrån processa fram någon funktion. Finns en hel del exempel på riktigt kluriga små snurror på det, på Fibaro’s egna forum.

Kommer ni ihåg den där Label som vi skapade i början? Hur vore det om den talade om status på sin Stereo? Ja förutsatt att stereon kan tala om det då? I Marantz fall så kan man fånga detta med lite LUA kod som gör ett anrop mot receivern och returnerar ett svar. Detta placeras för övrigt gärna i “Main loop” för att man hela tiden skall ha detta värde uppdaterat. I fallet med Marantz så kan man skriva koden såhär:

local MarantzIP= "192.168.1.107"
local thisId = fibaro:getSelfId();

function trim(s)
-- trim leading and trailing whitespaces --
return (s:gsub("^%s*(.-)%s*$", "%1"))
end

function Marantz(modechange)
    tcpSocket = Net.FTcpSocket(MarantzIP, 23);
    tcpSocket:setReadTimeout(10000);
    tcpSocket:write(modechange.."\r");
    result, err = tcpSocket:read();
    tcpSocket:disconnect();
    fibaro:sleep(1000);
    if (err ~= 0) then
        fibaro:log("transfer Not OK");
    end
end

function MarantzMode(ModeNow)
    fibaro:call(thisId, "setProperty", "ui.Label1.value", ModeNow);
end

--ask power status main zone
Marantz("ZM?")
--if main zone is on
if string.match(result, "ZMON") then
    --ask current INPUT selection
    Marantz("SI?")
    --strip the zone information off and set the current mode
    stringlength = string.len(result)
    ModeNow = (string.sub(result,3,stringlength));
    --Now get the volume status
    Marantz("MV?")
    fibaro:debug(result)
	ModeNow = ModeNow.." "..result
    MarantzMode(ModeNow)
--If power is not on
else
    stringlength = string.len(result)
    ModeNow = (string.sub(result,3,stringlength));
    MarantzMode(ModeNow)
end

--sleep 30 seconds then repeat
fibaro:sleep(30*1000)

Som ni säkert noterar är det en hel del kod för att åstadkomma detta – och återigen kommer det an till hur din stereo fungerar. Jag uppdaterar i alla fall detta fält var 30+3 sekunder (kom ihåg 3 sekunders fördröjningen – i alla fall hemma hos mig).

Det viktiga är följande rad som uppdaterar Label fältet:

fibaro:call(thisId, "setProperty", "ui.Label1.value", ModeNow);

Alltså, thisId är samma id som VDn har, “setProperty” är kommandot som sätter ett Label fälts, och fältet hetre “ui.Label1.value” – dvs det vi döpt Label fältet till och vi ger det alltså värdet “ModeNow” – i detta exempel är det vilken källa och volymens nivå som visas. Om man vill kan man t.ex. skriva:

fibaro:call(thisId, "setProperty", "ui.Label1.value", fibaro:getGlobal("<globalt variabelnamn>"));

Dvs nu får man ut den globala variabelns värde i Label fältet.

Om det finns frågor så är det bara att fråga – jag kommer nog skriva en fortsättning någon dag, men just nu känner jag för att detta kan nog räcka för nu. Slidern kommer jag till alltså i nästa kapitel eller vad man nu skall kalla detta :mrgreen: