Jak se hlídá elektrárna
Zveřejněno: 28. 02. 2014 | Autor: Petr Šafařík
Kategorie: Linux
Majitelé malých fotovoltaických elektráren (FVE, do 5kWp výkonu) si tuto elektrárnu pořizují z jednoho důvodu — ušetřit na nákladech za elektriku. Jsa jedním z nich (mám doma na střeše FVE 4.4kWp) jsem se rozhodl, že budu v tomto trochu aktivnější a dle aktuální výroby budu zapínat či vypínat naše domácí elektrospotřebiče (boilery). Za tímto účelem jsem vytvořil sadu skriptů — FVEread. V tomto krátkém seriálu popíšu, co všechno skripty umí a jak to dělají.
Jak funguje FVE
Nebojte, nemíním zde popisovat fyzikální pozadí foto-voltaického jevu, jen krátce shrnu zapojení a terminologii, abychom se v následující části neztráceli.
První v řetězci jsou fotovoltaické panely. Tyto jsou nejviditelnější částí celé FVE – jsou vidět na střechách domů a bohužel i na polích. Tyto panely se zapojují do série, přičemž po osvícení generují stejnosměrné napětí (a následně i proud). Dráty z panelů se poté svedou do jediného místa – střídače, což je zařízení, které ze stejnosměrného proudu dělá proud střídavý, 220V, tedy to, co je v běžné síti. Také hlídá, aby „tzv. seděla fáze“ a mnohé další. Ze střídače se, nyní již střídavý, proud svede do pojistkové skříně tak, že vyrobená energie je k dispozici pro spotřebu v domácnosti a nebo se „posílá“ do rozvodné sítě.
Vzhledem k tomu, že jsem si FVE pořizoval s úmyslem maximálně ušetřit, chci maximum z vyrobené energie rovnou spotřebovat doma — v místě výroby. Většina domácích FVE se „smíří“ s tím, že mají např. mrazák či boiler naprogramovaný na hodiny, kdy obvykle svítí sluníčko, jinak se nespíná (ala časově spínané zásuvku). Ovšem v takovém případě nahříváte vodu v poledne i v případě, že venku prší, či je mlha a vaše FVE neprodukuje nic (případně jen desítky wattů, což je tak akorát na provoz počítače, který bude spotřebu a vše okolo hlídat), což není nejekonomičtější.
A nebo budeme aktivnější: budeme aktivně hlídat výrobu a na základě rozvahy a energetického plánu si budeme vybírat, které zařízení zapneme. A o tom bude pojednávat tento krátký seriál.
Co všechno musí skript hlídat
Tedy cíl je dán: hlídat mnozství vyrobené energie a na jeho základě aktivně řídit spotřebu. Co k tomu budeme potřebovat:
- Střídač, který je možné připojit do interní sítě (většina střídačů již toto umí)
- Linuxový počítač pro tento úkol – já sám mám jeden vyhrazený virtuální stroj, který vše hlídá, komunikuje se střídačem a ovládá elektrospotřebiče. Vše je možné stejně tak dobře provozovat na mikropočítači typu Rapsberry Pi nebo i na vašem stolním počítači. Jediné, co je nutné, je aby se přes den tento počítač nevypínal – nebo spíše aby byl neustále zaplý
- V případě, že chcete i veřejně dostupnou HTML stránku, poté i vlastní server, na který budete data nahrávat
Nyní již k vlastnímu skriptu a tomu, co musí umět (uvažujeme, že chceme vygenerovat i HTML stránku).
- V prvním neoddiskutovatelném případě je to hlídání výroby. To znamená:
- Připojit se ke střídači
- Stáhnout si potřebná data
- Zpracovat data do proměnných tak, aby tomu systém rozuměl
- Dle výroby se rozhodnout, které spotřebiče mají být zaplé a které nikoli
- Porovnat posledních x minut výroby a určit, jak moc kolísavý je výkon, aby se co minutu neměnil stav v případě, že výroba je na spínané hraně
- Přihlédnout k současnému stavu zařízení
- Zapnout či naopak vypnout spotřebič
- Případně aktualizovat stránku se stavem zařízení
- Aktualizovat stránku na internetu
- Vygenerovat stránku s aktuální výrobou
- Aktualizovat stránku na internetu
Jak vidíte, je toho poměrně dost, co musí náš skript umět. Dá se rozdělit na celkem tři logické celky a několik podfunkcí.
První, hlavní, se jmenuje FVEread
— vyčítá informace ze střídače a pouští další skripty. FVEoptRun
se stará o optimalizaci spotřeby – rozhoduje se, které zařízení má být zaplé, které vyplé a podobně. FVEgenGraphs
nedělá nic jiného, než že generuje grafy. A konečně FVEgenPage
generuje a aktualizuje HTML stránky (o jejichž nahrávání na server se stará FVEread
z čistě praktických duvodu).
V tomto krátkém seriálu projdu všechny skripty a části tohoto systému, přičemž vysvětlím, proč je to psáno tak, jak to je napsáno. Podotýkám, že skript je zatím ve vývoji, především co se týče logiky části FVEoptRun
, která je prozatím velmi hloupá, ale než se k tomu v seriálu dostaneme, zajisté již bude přepsaná a chytřejší.
Nyní se podíváme na to, jak hlídat malou FVE a optimalizovat spotřebu a v této části se již budeme věnovat skriptovaní v BASHi. Jak jsem slíbil v předchozí části, zde se budeme věnovat hlavnímu skriptu jménem FVEread, který je hlavní činnou jednotkou celého systému.
Před tím, než začnete číst tento seriál, doporučuji si pročíst můj nedávný krátký článek o GITu. Zvláště, pokud uvažujete nad tím, že byste tento skript (sadu) použili, byste měli používat výhradně kód z GitHubu, nikoli to, co je uváděno zde.
FVEread
je hlavní výkonnou jednotkou celého systému. Jeho funkce jsou:
- řídit celý systém optimalizace spotřeby
- stahovat (vyčítat) data ze střídače
- spouštět ostatní skripty (pro generování HTML kodu a optimalizaci spotřeby)
- nahrávat soubory na server
FVEread přijímá krom parametru -h|--help
už jen dva parametry.
--stdout
Pouze vypíše informace na stdout — stáhne data ze střídače a vypíše. Neprovádí optimalizaci ani aktualizaci HTML stránek--daemon|-d
Spustí skript s automatickým opakováním každých 60 vteřin.
Aby byl skript efektivní a fungoval tak, jak má, je vnodné jej pouštět v pravidelných intervalech (minuta či dvě — tak aby vše bylo dynamické a mohlo se reagovat na skutečnou situaci. Já sám to mám naprogramované pomocí služby cron:
* * * * * /cesta/k/FVEread 2> /dev/null
kdy každou minutu se spustí celý systém. Druhá možnost je právě parametr --daemon
, kdy se děje totéž, ale skript se spustí jen jednou a co minutu se provádí hlavní interní routina. Tedy je jen na vás, čemu dáte přednost — cronu nebo spouštění na pozadí, např. přes init
.
Dobrá ale, je nutné začít popořadě tak, jak se jednotlivé funkce načítají.
[codesyntax lang=“bash“ lines=“no“]
#!/bin/bash source ~/.FVErc function printHelp() { echo -e "usage: `basename $0` [--stdout] Opts: --help|-h Print this help message --stdout Will not update webpage, but print just output to stdout --daemon|-d Enable auto-refresh, otherwise the script will issue only one iteration." exit 0 } if [ "$#" = 1 ]; then if [ "$1" = '-h' -o "$1" = "--help" ]; then printHelp exit elif [ "$1" = "--stdout" ]; then export STDOUT="YES" elif [ "$1" = "--daemon" -o "$1" = "-d" ]; then export DAEMON=1 else echo "Non supported parametr $1" exit 1 fi elif [ "$#" -gt 1 ]; then echo "Too much options" printHelp exit fi if [ $DAEMON ]; then while true; do mainLoop sleep 60 done else mainLoop fi
[/codesyntax]
Jak vidno, pokud je použit přepínač --daemon
(a nebo -d
), script skončí ve smyčce while-do-done
, která neskončí. Pokud tento parametr předán není, skočí se rovnou to funkce mainLoop.
Nepovažuji za nutné popisovat funkci print_help()
a část s processingem parametrů – s tím si užijeme v některém z dalších dílů, kde parametry zpracováváme spolu s funkcí getopts
.
Jediná zajímavost zde je druhý řádek, kde se načítají některé proměnné, jako uživatelské jméno a heslo do střídače, k FTP serveru, cesty a názvy souborů.
[codesyntax lang=“bash“ lines=“no“]
function mainLoop() { getData if [ "$STDOUT" ]; then printDataStdout else genFiles FVEoptRun "$CURRENT" uploadDataFTP fi cleanFile }
[/codesyntax]
Funkce mainLoop
spouští již jednotlivé úkoly. getData
stáhne data ze střídače a naplní proměnné, se kterými poté operují další funkce. Pokud je předán parametr --stdout
, tak v tuto chvíli veškerá práce končí, načtené proměnné se jen vypíší na terminál.
Parametr
--stdout
používám na ostatních počítačích, kde mám udělanýalias FVE='ssh fvechecker "FVEread --stdout"'kde
fvechecker
je počítač určený pro hlídání FVE. Takto mohu jednoduše zjistit aktuální hodnoty, aniž bych se musel připojovat na web.
Pokud ovšem požadujete všechny další funkcionality skriptu, je potřeba vygenerovat HTML soubory a grafy (funkce genFiles
), upravit a optimalizovat spotřebu (FVEoptRun
) a konečně nahrát soubory na server (uploadDataFTP
). FVEoptRun
je již jiný skript s vlastní logikou, ale nebojte se, popíšu jej v jednom z dalších dílů. Ale ještě před tím, než začneme vypisovat data na terminál či nahrávat soubory, je nutné vyčíst data ze střídače — čas na funkci getData()
.
Vyčítáme data ze střídače
Funkce getData()
je jednohuchá routina, která prvně stáhne stránku ze střídače (vizte poznámku níže) — getFileFromFVE
a poté spustí 2 velmi podobné funkce pro získání proměnné $CURRENT
(aktuální výroba) a $TODAY
(množství energie vyrobené za dnešek).
Střídač, co mám, je KOSTAL Piko 4.2. Je možné k němu přistupovat přes http stránku s autentifikací uživatel/heslo. Proto funkce getData je customizována a stahuje onu HTML stránku, rozparsuje ji a vrátí hodnoty. Pokud byste měli jiný střídač, úpravám této funkce se nevyhnete.
V případě, že není možné se připojit ke střídači, plní tyto proměnné nulami.
[codesyntax lang=“bash“ lines=“no“]
function getData() { getFileFromFVE if [ $? = 1 ]; then CURRENT="-0" TODAY="-0" FVESTATUS="Down/stopped" else CURRENT="`getCurrent`" TODAY="`getToday`" FVESTATUS="Running" fi getAvrg }
[/codesyntax]
Stahování dat ze střídače dělám běžným cURL
příkazem, kterému předávám i username a heslo a ukládám do $TMPDIR/FVE.$$.html
. Proměnná $TMPDIR
je načítána hned na začátku celého skriptu. Pokud neznáte trik s $$
, poté vězte, že konstrukce $$
je PID daného procesu. Výborně se to hodí na generování dočasných souborů s unikátním názvem. Je ovšem nutné pamatovat na to, abyste tyto soubory odstraňovali — tento skript vytvoří jeden takovýto soubor každou minutu, což je 1440 souborů za den! V případě, že stažení stránky ze střídače nefunguje, navrací funkce návratovou hodnotu 1 a předpokládá se chyba v komunikaci, či že je střídač vypnutý.
[codesyntax lang=“bash“ lines=“no“]
function getFileFromFVE() { curl -u $FVEUSERNAME:$FVEPWD http://stridac > "$TMPDIR"/FVE.$$.html 2>/dev/null if [ $? != 0 ]; then echo "Stridac neni dostupny" return 1 else return 0 fi }
[/codesyntax]
Existují dvě funkce, které jsou skoro stejné, jen se liší drobnostmi, které vyhledávají. Obě funkce následně sdílí společný konec, tj. že na získanou hodnotu volají krátkou routinu cleanLine, která vrátí pouze čísla. Jedná se o implementační kroky poměrně pevně vázané na můj střídač. Funkce getAvrg zprůměruje výrobu za posledních 10 minut.
[codesyntax lang=“bash“ lines=“no“]
function getCurrent() { iCURRENT=`cat "$TMPDIR"/FVE.$$.html | grep -A2 'aktu[.]*'|grep ln -A2 | tail -1 ` echo `cleanLine "$iCURRENT"` } function getToday() { iTODAY=`cat "$TMPDIR"/FVE.$$.html | grep -A2 'denn[.]*'|grep energie -A2 | tail -1` echo `cleanLine "$iTODAY"` } function cleanLine() { LINE=$1 echo $LINE | sed 's/<\/td>//g' | sed 's/ //g' | tr -d ' ' | \ sed 's/xxx/0/g' | tr -dc '[:print:]' } function getAvrg() { AVRG=$(expr $(expr $(tail -10 $HISTORYFILE | awk '{print $2}' | sed '/^$/d' | sed ':a;N;$!ba;s/\n/ + /g')) / $(tail -10 $HISTORYFILE | awk '{print $2}' | sed '/^$/d' | wc -l) ) }
[/codesyntax]
Nyní již je v proměnných $CURRENT
a $TODAY
uložen aktuální výkon a množství vyrobené energie pro daný den. Ve funkci getData
tedy pokračujeme k tomu, má-li se vše vytisknout na terminál (standardni output), a nebo vytvořit HTML soubory a nahrát na server a optimalizovat spotřebuju. Jakmile se zavolá funkce printDataStdout
, vypíší se hodnoty a funkce končí:
[codesyntax lang=“bash“ lines=“no“]
function printDataStdout() { #usage: #printDataStdout echo "My FV: `date`:" echo "Current production [kW] = $CURRENT" echo "Today was produced [kWh] = $TODAY" return 0 }
[/codesyntax]
Generujeme soubory
Podtitul k této části by mohlo být „ale jen náznakem, více někdy příště“. O generování HTML souborů se totiž stará skript FVEgenPage
a o generování grafů FVEgenGraphs
.
Poté, co se z funkce mainLoop()
zavolá genFiles()
, do souboru history se zapíší aktuální hodnoty ze střídače spolu s datem a časem. Poté se vygeneruje HTML stránka s aktuální produkcí, a nakonec vygenerují grafy.
[codesyntax lang=“bash“ lines=“no“]
function genFiles() { echo `date +%Y%m%d%H%M` $CURRENT $TODAY $AVRG >> "$HISTORYFILE" FVEgenPage --powerpage --current $CURRENT --today $TODAY --fvestatus $FVESTATUS updateProductionFile FVEgenGraphs --all }
[/codesyntax]
Když už mluvíme o proměnných a souborech, trochu je představím. Začnu s proměnnými, uloženými v souboru ~/.FVErc:
FVEUSERNAME=
— Uzivatelské jméno do střídačeFVEPWD=
—heslo ke střídačiFTPUSERNAME=
— uživatelské jméno pro komunikaci s FTP serveremFTPPASSWD=
— heslo pro komunikaci s FTP serveremTMPDIR=/tmp
— umístění dočasných souborůFVEHTMLNAME=fve.html
— jméno pro hlavní stránkuFVEHTMLOPTNAME=fveopts.html
— jméno stránky se stavem řízení spotřebyFVEHTMLPOWERNAME=fvepower.html
— jméno stránky se stavem výrobyPATH=$PATH:~/bin
— je nutné dopsat cestu k adresáři, ve kterém jsou ostatní skriptyFVEDATADIR=~/fve
— adresář, ve kterém bude ukládána historie výrobyPRODUCTIONFILE="$FVEDATADIR"/production.`date +%Y%m`
— soubor s historií celkové výrobyHISTORYFILE="$FVEDATADIR"/history.`date +%Y%m`
— soubor s historií výrobyHTMLSERVER=SERVER.COM
— jméno FTP serveru- Poslední tři proměnné pouze určují cesty, netřeba je měnit.
HTMLFILE="$TMPDIR"/"$FVEHTMLNAME"
HTMLOPTFILE="$TMPDIR"/"$FVEHTMLOPTNAME"
HTMLPOWERFILE="$TMPDIR"/"$FVEHTMLPOWERNAME"
Není jich málo, pravda. Na druhou stranu, některé není třeba měnit (libovolné dočasné cesty a soubory), jiné jsou vázány na mou konkrétní implementaci. O tom, jak vypadajií HTML soubory, se rozepíšu v některém z příštích dílů, kdy se budu věnovat FVEgenPage
.
Ovšem nejpodstatnější soubory jsou soubory history
a production
, které jsou umístěny v $FVEDATADIR
.
Soubor history obsahuje 4 sloupce. První sloupec je čas, kdy byla data získána. Ve druhém sloupci je zaznamenán aktuální výkon v daném čase. V předposledním sloupci je hodnota, kolik FVE vyrobila energie do té chvíle. Poslední sloupec uvádí průměrnou výrobu za posledních 10 minut. Kompenzují se tím drobné výkyvy a např. minutové zatmění mraky a podobně.
Soubor production
má sloupce jen dva. Časová známka obsahuje pouze datum — jeden den tak je uveden pouze na jednom řádku.
Ze souboru history
je tak možné vyčíst průběh výroby v průběhu doby. Naopak production
obsahuje vlastně jednotnou historii produkce v průběhu dnů.
Obsah souboru history
vzniká na jediném řádku ve funkci genFiles
[codesyntax lang=“bash“ lines=“no“]
echo `date +%Y%m%d%H%M` $CURRENT $TODAY $AVRG >> "$HISTORYFILE"
[/codesyntax]
Obsah souboru production
vzniká na více řádcích. V prvním se smaže předešlý záznam pro daný den a poté se na konec souboru vloží data. Nakonec jsem k tomu napsal zvláštní funkci:
[codesyntax lang=“bash“ lines=“no“]
function updateProductionFile() { DATE=`date +%Y%m%d` sed -i /"$DATE"/d $PRODUCTIONFILE echo "$DATE $TODAY" >> $PRODUCTIONFILE return 0 }
[/codesyntax]
Na prvním řádku se vytvoří proměnná data, poté se pomocí sed-u odstraní řádek, který obsahuje záznam toho dne. A nakonec se do souboru production vloží nový záznam ve formátu datum-hodnota.
Toto je možné samozřejmě řešit více přístupy, případně vše generovat jen ze souboru history. Ovšem prvně je tato implementace jednodušší co do kódu, tak i do pochopení.
Nahráváme a uklízíme
V předešlé části jsme vygenerovali data a soubory. Nyní je před námi poslední výkonová část skriptu. Funkce uploadDataFTP
. Tato funkce na FTP server nahraje potřebná data — grafy a html soubory.
[codesyntax lang=“bash“ lines=“no“]
function checkRunningStatus() { if [ -e "$TMPDIR"/.fveread ]; then echo "Already running, quiting." echo $$ >> "$TMPDIR"/.fveDebug exit 0 fi echo $$ > "$TMPDIR"/.fveread } function uploadDataFTP() { checkRunningStatus lftp -u "$FTPUSERNAME",$FTPPASSWD -e "put -O "$HTMLDESTDIR" $TMPDIR/fvem.png $TMPDIR/fved.png /$TMPDIR/fvep.png $HTMLPOWERFILE $HTMLOPTFILE $HTMLFILE && bye" $HTMLSERVER mv "$TMPDIR"/fved.png "$TMPDIR"/d`date +%Y%m%d`.png mv "$TMPDIR"/fvem.png "$TMPDIR"/m`date +%Y%m`.png mv "$TMPDIR"/fvep.png "$TMPDIR"/p`date +%Y%m`.png lftp -u "$FTPUSERNAME",$FTPPASSWD -e "mput -O "$HTMLDESTDIR"/fve.hist/ $TMPDIR/m*.png $TMPDIR/p*.png $TMPDIR/d*.png && bye" $HTMLSERVER rm -f "$TMPDIR"/.fveread return 0 }
[/codesyntax]
V první řadě se překontroluje, jestli se již nenahrávají data na FTP server. Může se stát, že se nahrávání z nejakého důvodu zasekne (např. spadne spojení) a pak by se množilo množství FTP příkazů. Funkce checkRunningStatus
tak překontroluje, jestli již existuje tzv. „zámek“, tj. soubor "$TMPDIR"/.fveread
a pokud neexistuje, vytvoří jej. Pokud tento soubor ovšem najde, přestane se provádět a skript se ukončí. Poté se na server uloží všechna data. Jakmile jsou FTP přenosy hotové, smaže se zámek.
V průběhu doby vzniká poměrně velké množství souborů (grafy, html stránky…), které nejsou po přenesení na server potřeba. Proto existuje funkce cleanFile
, která nakonec uklidí.
[codesyntax lang=“bash“ lines=“no“]
function cleanFile() { rm -f "$TMPDIR"/FVE.$$.html rm -f "$TMPDIR"/fved.png rm -f "$TMPDIR"/fvem.png rm -f "$TMPDIR"/fvep.png rm -f "$TMPDIR"/d`date +%Y%m%d`.png rm -f "$TMPDIR"/m`date +%Y%m`.png rm -f "$TMPDIR"/p`date +%Y%m`.png }
[/codesyntax]
A tímto hlavní skript končí. I když práce vlastně teprve začíná — nezapomeňte, že zatím se ze střídače pouze stáhla data, uložila do pár souborů, poté se zavolala magie (FVEoptRun
, FVEgenPage
, FVEgenGraphs
) a pak se vše nahrálo na FTP server a smazalo… Tedy, zatím žádná užitečná práce, řeklo by se. Nebojte, všechno bude.
Vzhledem k úzce specifickému tématu další pokračování tohoto tématu naleznete na blogu autora tohoto seriálu.