Projekt Morgenmuffel Teil 3 - Cronjobs und Adminscript

Heute dann der 3. Teil, die Versierten unter euch werden vermutlich reichlich Anlass finden an meinen Quelltexten und meinem Umgang mit Linux herum zu mäkeln
Ich bin kein sonderlich guter Programmierer und meine Linux-Kenntnisse bewegen sich irgendwo im Bereich des gefährlichen Halbwissens also wer etwas verbessern möchte: Bitte gerne ;-)
Wie auch immer, die Scripte tun so was sie sollen, sind vielleicht nicht sonderlich sicher aber das spielt hier bei mir auch keine Rolle da davon nichts von außen erreichbar ist.

Nun geht es im wesentlichen darum, die Elemente Licht und Ton durch einen Cronjob (das ist ein zeitlich gesteuerter Programmaufruf) ausführen zu lassen.

Dafür lege ich erst einmal eine Cron-Tabelle an. Ich gehe davon aus, dass die Tabelle bisher leer ist. Sofern ihr schon andere Cronjobs laufen habt müssen diese im weiter unten folgenden Steuerscript zusätzlich herausgefiltert werden!

Wir rufen in der Konsole folgendes auf:

 crontab -e

Möglicherweise kommt noch eine Nachfrage welchen Editor man verwenden möchte, ich benutze Nano/Pico.

Sofern vorhanden wird jetzt die Cron-Tabelle für den aktuellen Nutzer (bei mir pi) geöffnet oder eben eine neue, leere angelegt die ich wie folgt fülle:

30 8 * * 1 /usr/bin/sudo home/pi/milight_binaries/sunrise.sh # an9s8o
30 8 * * 2 /usr/bin/sudo home/pi/milight_binaries/sunrise.sh # bn9s8o
30 8 * * 3 /usr/bin/sudo home/pi/milight_binaries/sunrise.sh # cn9s8o
30 8 * * 4 /usr/bin/sudo home/pi/milight_binaries/sunrise.sh # dn9s8o
30 8 * * 5 /usr/bin/sudo home/pi/milight_binariest/sunrise.sh # en9s8o
30 8 * * 6 /usr/bin/sudo home/pi/milight_binaries/sunrise.sh # fn9s8o
30 8 * * 7 /usr/bin/sudo home/pi/milight_binaries/sunrise.sh # gn9s8o


Und wieder raus aus dem Editor (wenn pico/nano) mit Ctrl-o und Ctrl-x.



Das bedeutet Folgendes:

| Minute | Stunde | Jeder Tag | Jeder Monat | Wochentag | auszuführendes Script | Kommentar | Id


Die Wochentage fangen bei Montag=1 an und enden bei Sonntag=7
Die Zeilen-Reihenfolge der Tage spielt keine Rolle.
Also wird hier in diesem Beispiel jeden Tag um 8:30 das Shellscript sunrise.sh ausgeführt.
Die Id hinter der Raute dient der Identifikation für den Crontab-Manager, siehe weiter unten.

Jetzt würde der Wecker schon laufen sofern das shellscript existiert.
Dieses lege ich entsprechend unter /home/pi/milight_binaries/sunrise.sh an, es kann aber auch an anderer Stelle liegen da ohnehin die Pfade im Script mit angegeben werden.

sudo pico  /home/pi/milight_binaries/sunrise.sh

#!/bin/bash

sudo /home/pi/milight_binaries/milight 0 ON
sudo /home/pi/milight_binaries/milight 0 W
sudo /home/pi/milight_binaries/milight 0 B 1

curl -H 'Content-Type: text/xml; charset=utf-8' -H 'SOAPAction: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"' -d '<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><CurrentURI><![CDATA[http://192.168.178.40/test.mp3]]></CurrentURI><CurrentURIMetaData></CurrentURIMetaData></u:SetAVTransportURI></s:Body></s:Envelope>' 'http://192.168.178.37:49494/upnp/control/rendertransport1'

curl -H 'Content-Type: text/xml; charset=utf-8' -H 'SOAPAction: "urn:schemas-upnp-org:service:AVTransport:1#Play"' -d '<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><Speed>1</Speed></u:Play></s:Body></s:Envelope>' 'http://192.168.178.37:49494/upnp/control/rendertransport1'

for i in {1..19}
 do
  sudo /home/pi/milight_binaries/milight 0 B "$i"
  sleep 47
 done

sleep 900
sudo /home/pi/milight_binaries/milight 0 B 1
sleep 1
sudo /home/pi/milight_binaries/milight 0 OFF
sleep 1
sudo /home/pi/milight_binaries/milight 0 OFF

Wiederrum raus aus dem Editor (wenn pico/nano) mit Ctrl-o und Ctrl-x.


Der Vollständigkeit halber verpasse ich dem gesamten Bereich unter /milight_binaries
den Nutzer und die Gruppe pi:pi. Normal sollte dies mit sudo eigentlich unproblematisch sein
aber mit den Rechten unter Linux habe ich schon jede Menge Zeit verschwendet also lieber gleich mit dem Holzhammer ;-)

sudo chown -R pi:pi /home/pi/milight_binaries

Zur Erläuterung des Shellscripts:


Die ersten 3 sudo Zeilen schalten Licht 0 an und setzen es auf Weiß / Helligkeit 1.

Der erste Curl Block initiiert das MP3 und sagt der airmusic Box wo das MP3 zu finden ist,
diese Passage MUSS natürlich entsprechend angepasst werden: http://192.168.178.40/test.mp3 

Im zweiten Curl Block wird das MP3 abgespielt. Das Einfaden habe ich direkt im MP3 mit einem Audio Editor arrangiert.

Anschließend wird die Helligkeit des LED Streifens, mit je 47 Sekunden Pause zwischen den Stufen, erhöht. Mehr Schritte sind so leider nicht möglich, siehe Teil2.

Zum Schluss bleibt das Licht noch 900 Sekunden an und wird dann auf Helligkeit 1 gesetzt um dann zu guter Letzt abgeschalten zu werden.

Das Abschalten erfolgt zweimal da ich es schon beobachten musste, dass nicht immer alle Signale am Ziel ankommen. So ist es etwas sicherer, dass das Licht dann tatsächlich aus ist.

Nun möchten wir natürlich nicht jedes Mal über SSH in der Konsole oder am Raspberry direkt die Weckzeiten einstellen also habe ich mich des Crontab-Managers bedient:
https://github.com/MediovskiTechnology/php-crontab-manager

Wer jetzt noch keinen Webserver und kein PHP installiert hat muss dies an dieser Stelle tun.
Anleitungen dafür gibt es reichlich im Netz, daher gehe ich jetzt nicht weiter darauf ein.
Beide sind allerdings mit apt-get install recht einfach zu installieren.

Unter /var/www oder /var/www/html 
kann man jetzt mittels git clone eine Kopie des Crontab Managers installieren:

 git clone https://github.com/MediovskiTechnology/php-crontab-manager.git 

Wie beim Crontabmanager beschrieben ergibt es nun Sinn die sudoers Rechte entsprechend anzupassen damit der PHP-Crontabmanager auch im Crontab vom Nutzer Pi herummurksen kann.


 sudo pico /etc/sudoers 


Ich füge die Zeile:



%www-data ALL=(pi)NOPASSWD:/usr/bin/crontab

hinzu, damit der Nutzer www-data meinen Pi-Crontab ändern kann.
Leute mit Paranoia lösen das bitte anders ;-)



Nun kommt noch mein gefrickeltes PHP Script hinzu mit dem man dann die Uhrzeiten einstellen kann. Derzeit wird mit der Auswahl der Minuten und Stunden jeweils direkt aktualisiert. Das ist nicht wirklich elegant aber funktioniert. Achso, für die Buttons habe ich mich bei  http://colorzilla.com/ bedient. Alle "Grafiken" sind also dynamisch via CSS erzeugt. Das Ganze dann im var/www bzw var/www/html (je nach Linux Distribution) als index.php abgelegt kann unter der IP (oder Hostnamen falls eingerichtet) des Raspberry die Weckeradmin aufgerufen werden, sofern die Pfade zum Crontabmanager passen:

include("../php-crontab-manager/src/CliTool.php");
include("../php-crontab-manager/src/CronEntry.php");
include("../php-crontab-manager/src/CrontabManager.php");


Die sieht dann in etwa so aus und passt auf den Bildschirm eines Android Smartphones.

Ein Manko hat das Ding allerdings momentan noch, bei einem Reload der Seite werden doppelte Cronjobs erzeugt.  Im Zweifelsfall müssen die dann manuell über crontab -e gelöscht werden. Wenn ich Muse habe kommt dazu nochmal ein Update.












<html>
    <head>
       <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
        <style type="text/css" >
            .body {
              font-family:arial,helvetica;     
              overflow-x: hidden;
              overflow-y: auto;
              background-color:#505050;
              font-size:18pt;
              color:#909090;
            }
       
            .arrow-down {
              float:left;
              border-radius: 16px 16px 16px 16px;
              padding: 0px;
              text-align:center;
              width: 80px;
              height: 30px;
              padding-left:4px;
              border-width:1px;
              border-style:outset;
           
              background: #c9c9c9; /* Old browsers */
              background: -moz-linear-gradient(-45deg, #c9c9c9 0%, #848484 46%, #7d7d7d 50%, #828282 53%, #a5a5a5 76%, #9a9a9a 87%, #8b8b8b 100%); /* FF3.6+ */
              background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#c9c9c9), color-stop(46%,#848484), color-stop(50%,#7d7d7d), color-stop(53%,#828282), color-stop(76%,#a5a5a5), color-stop(87%,#9a9a9a), color-stop(100%,#8b8b8b)); /* Chrome,Safari4+ */
              background: -webkit-linear-gradient(-45deg, #c9c9c9 0%,#848484 46%,#7d7d7d 50%,#828282 53%,#a5a5a5 76%,#9a9a9a 87%,#8b8b8b 100%); /* Chrome10+,Safari5.1+ */
              background: -o-linear-gradient(-45deg, #c9c9c9 0%,#848484 46%,#7d7d7d 50%,#828282 53%,#a5a5a5 76%,#9a9a9a 87%,#8b8b8b 100%); /* Opera 11.10+ */
              background: -ms-linear-gradient(-45deg, #c9c9c9 0%,#848484 46%,#7d7d7d 50%,#828282 53%,#a5a5a5 76%,#9a9a9a 87%,#8b8b8b 100%); /* IE10+ */
              background: linear-gradient(135deg, #c9c9c9 0%,#848484 46%,#7d7d7d 50%,#828282 53%,#a5a5a5 76%,#9a9a9a 87%,#8b8b8b 100%); /* W3C */
              filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#c9c9c9', endColorstr='#8b8b8b',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
            }     
       
            .redbutton {
              margin-left:4px;     
              color:#400000;
              float:left;
              border-radius: 16px 16px 16px 16px;
              padding: 0px;
              text-align:center;
              width: 80px;
              height: 30px;
              padding-left:4px;
              border-width:1px;
              border-style:outset; 
       
             /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#f69292+0,db1313+46,d01212+50,d91313+53,ef4949+76,ed3333+87,ea1414+100 */
              background: #f69292; /* Old browsers */
              background: -moz-linear-gradient(-45deg, #f69292 0%, #db1313 46%, #d01212 50%, #d91313 53%, #ef4949 76%, #ed3333 87%, #ea1414 100%); /* FF3.6+ */
              background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#f69292), color-stop(46%,#db1313), color-stop(50%,#d01212), color-stop(53%,#d91313), color-stop(76%,#ef4949), color-stop(87%,#ed3333), color-stop(100%,#ea1414)); /* Chrome,Safari4+ */
              background: -webkit-linear-gradient(-45deg, #f69292 0%,#db1313 46%,#d01212 50%,#d91313 53%,#ef4949 76%,#ed3333 87%,#ea1414 100%); /* Chrome10+,Safari5.1+ */
              background: -o-linear-gradient(-45deg, #f69292 0%,#db1313 46%,#d01212 50%,#d91313 53%,#ef4949 76%,#ed3333 87%,#ea1414 100%); /* Opera 11.10+ */
              background: -ms-linear-gradient(-45deg, #f69292 0%,#db1313 46%,#d01212 50%,#d91313 53%,#ef4949 76%,#ed3333 87%,#ea1414 100%); /* IE10+ */
              background: linear-gradient(135deg, #f69292 0%,#db1313 46%,#d01212 50%,#d91313 53%,#ef4949 76%,#ed3333 87%,#ea1414 100%); /* W3C */
              filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f69292', endColorstr='#ea1414',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
            }

            .greenbutton {
              margin-left:4px;
              color:#004000;
              float:left;
              border-radius: 16px 16px 16px 16px;
              padding: 0px;
              text-align:center;
              width: 80px;
              height: 30px;
              padding-left:4px;
              border-width:1px;
              border-style:outset; 
              /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#afdaae+0,4da44a+46,499c46+50,4ca349+53,79c276+76,69ba66+87,52af4f+100 */
              background: #afdaae; /* Old browsers */
              background: -moz-linear-gradient(-45deg, #afdaae 0%, #4da44a 46%, #499c46 50%, #4ca349 53%, #79c276 76%, #69ba66 87%, #52af4f 100%); /* FF3.6+ */
              background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#afdaae), color-stop(46%,#4da44a), color-stop(50%,#499c46), color-stop(53%,#4ca349), color-stop(76%,#79c276), color-stop(87%,#69ba66), color-stop(100%,#52af4f)); /* Chrome,Safari4+ */
              background: -webkit-linear-gradient(-45deg, #afdaae 0%,#4da44a 46%,#499c46 50%,#4ca349 53%,#79c276 76%,#69ba66 87%,#52af4f 100%); /* Chrome10+,Safari5.1+ */
              background: -o-linear-gradient(-45deg, #afdaae 0%,#4da44a 46%,#499c46 50%,#4ca349 53%,#79c276 76%,#69ba66 87%,#52af4f 100%); /* Opera 11.10+ */
              background: -ms-linear-gradient(-45deg, #afdaae 0%,#4da44a 46%,#499c46 50%,#4ca349 53%,#79c276 76%,#69ba66 87%,#52af4f 100%); /* IE10+ */
              background: linear-gradient(135deg, #afdaae 0%,#4da44a 46%,#499c46 50%,#4ca349 53%,#79c276 76%,#69ba66 87%,#52af4f 100%); /* W3C */
              filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#afdaae', endColorstr='#52af4f',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
            }
       
            .input {
              font-size:20px;
              background-color:#808080;
              border:0px;
              padding-right:4px;
              height:31px;
              width:32px;
              text-align:right;
              color:#FFF;        
            }
       
            .poser {        
              padding:2px;  
              position: fixed;
              top: 0;
              left: 0;
            }
            .weekday_1,weekday_2,weekday_3,weekday_4,weekday_5 {
              color:#909090;
             }
       
            .weekday_7 {
              color:#FF0000;
            }
       
            .weekday_6 {
              color:#EE0000;
            }                   
        </style>
       
        <script>
           function setonoff(id,onoff) {
             minute=document.getElementById('Minute_'+id).value;
             hour=document.getElementById('Hour_'+id).value;
             weekday=document.getElementById('weekday_'+id).value;
             document.location.href = 'index.php?minute='+minute+'&hour='+hour+'&id='+id+'&action='+onoff+'&weekday='+weekday;                   
           }     
        </script>   
    </head>   
   
   
    <body class="body">
<div class="poser">

<?php

include("../php-crontab-manager/src/CliTool.php");
include("../php-crontab-manager/src/CronEntry.php");
include("../php-crontab-manager/src/CrontabManager.php");
use php\manager\crontab\CrontabManager;






if (!isset($_REQUEST["action"])) $_REQUEST["action"]="list";

function listjobs() {
   
    $weekdays= array(
                 "1"=>"Monday",
                 "2"=>"Tuesday",
                 "3"=>"Wednesday",
                 "4"=>"Thursday",
                 "5"=>"Friday",
                 "6"=>"Saturday",
                 "7"=>"Sunday",
               );
   
    $crontab = new CrontabManager();
                $crontab->user = "pi";
                $joblist=explode("\n",$crontab->listJobs());
                $i=0;           
               
                foreach ($joblist as $key=>$val) {
                    $onejob=preg_split('/[\s]+/',$val,6);
                   
                    if(is_numeric($onejob[0])) {
                     $jobsbyday[$onejob[4]] [$i]['Hour']=$onejob[1];
                     $jobsbyday[$onejob[4]] [$i]['Minute']=$onejob[0];
                     $appandid=explode('#',$onejob[5]);
                     $jobsbyday[$onejob[4]] [$i]['App']=trim($appandid[0]);
                     $jobsbyday[$onejob[4]] [$i]['Id']=trim($appandid[1]);
                    }
                 $i++;      
                       
                }

                // sort by weekday
                ksort($jobsbyday);                 
                
                foreach ($jobsbyday as $key=>$val) {
                    echo '<span class="weekday_'.$key.'">'.$weekdays[$key].'</span><br>';
                    $i=0;
                   
                    foreach ($val as $k=>$v) {
                   
                        echo '<div><div style="float:left"><select onChange="setonoff(\''.$v["Id"].'\',\'on\')" id="Hour_'.$v["Id"].'" class="arrow-down">';
                              for ($i=0;$i<24;$i++)   {
                                  if (($v["Hour"]*1)==$i) $sel=' selected'; else $sel="";
                                  echo '<option value="'.$i.'"'.$sel.'>'.$i.'</option>';                                
                              }
                        echo '</select>:</div>  ';
                       
                        echo '<div style="float:left"><select onChange="setonoff(\''.$v["Id"].'\',\'on\')" id="Minute_'.$v["Id"].'" class="arrow-down">';
                              for ($i=0;$i<60;$i++)   {
                                 
                                  if (($v["Minute"]*1)==$i) $sel='selected'; else $sel="";
                                  echo '<option value="'.$i.'"'.$sel.' >'.$i.'</option>';                                
                              }
                        echo '</select><input type="hidden" id="weekday_'.$v["Id"].'" value="'.$key.'"></div>';
                       
                        if (stristr($v["App"],"milight")) {                      
                            echo '<div class="greenbutton" onClick="setonoff(\''.$v["Id"].'\',\'off\')">active</div>';
                        } else {
                            echo '<div class="redbutton" onClick="setonoff(\''.$v["Id"].'\',\'on\')">off</div>';
                        }
                        echo '<div style="clear:both"></div>';
                       
                       
                    }
                 
                   
                   
                }   
   
}


switch ($_REQUEST["action"]) {

   case "off" :
   case "on" :      
               // remove the old cronjob
               $crontabdel = new CrontabManager();
               $crontabdel->user = "pi";           
               $crontabdel->deleteJob(''.trim($_REQUEST["id"]).'');
               $crontabdel->save(false);              
              
               // create a new cronjob
               $crontabadd = new CrontabManager();
               $crontabadd->user = "pi";
               $job = $crontabadd->newJob();
               $job->on($_REQUEST["minute"].' '.$_REQUEST["hour"].' * * '.$_REQUEST["weekday"]);
              
               // if active run shellscript else echo nothing
               if ($_REQUEST["action"]=="on") {
                $job->doJob("sudo /home/pi/milight/sunrise.sh");                       
               } else {
                $job->doJob("sudo echo nothing");                          
               }
              
               $crontabadd->add($job);
               $crontabadd->save();
               listjobs();
               break;                            
   
   case "list":
   default:    listjobs();break;       
 
}
?>
   

</div>
    </body>
</html>














Comments

Popular posts from this blog

OSDYLS - Open Source DIY Lasertag System #7 IR communication

OSDYLS - Open Source DIY Lasertag System #6 testing all components with the S2 mini