Mähroboter-Steuerung Smartplug

Steuerung eines M√§hroboters mittels Steckdose und einem PHP-Webserver. F√ľr die Steuerung werden aktuelle¬†Wetterdaten verwendet. Bei zuviel Niederschlag bleibt der M√§her zu Hause. Getestet wurde das Skript mit einem Husqvarna 330x, siehe:¬†LiBe.net: universelle M√§hroboter Steuerung

Funktion / Bedienung

Mittels Webseite k√∂nnen der Stromstatus und die Wetterdaten, sowie die Schaltvorg√§nge, kontrolliert werden. Bei Bedarf kann der M√§her komplett ausgeschaltet, komplett eingeschaltet oder komplett deaktiviert werden. Zudem ist es m√∂glich den M√§her nur f√ľr den aktuellen Tag zu aktivieren oder f√ľr den aktuellen Tag und den darauffolgenden Vormittag zu deaktivieren. Damit der Webserver den Strom nicht w√§hrend einer M√§hphase schaltet, wurden die Zeiten mit den M√§hzeiten des M√§hers abgeglichen. Der M√§her hat dazu einen Zeitbereich vormittags und einen nachmittags bzw.¬†abends. Der Zeitbereich ist jeden Tag gleich. Die Zeiten sind auch am Webserver hinterlegt. In meinem Fall sieht das so aus:

morgens von 6:30-9:00 und abends von 19:30-23:30: Die Zeiten können in den Variablen des Webservers angepasst werden.

Auswertung der Wetterdaten

Die Niederschlagswerte der Wetterdaten werden einfach zusammengez√§hlt. Ab einem bestimmten Schwellwert wird der M√§her deaktiviert, z.B. 2 l/m2. Wetterdaten au√üerhalb der abendlichen M√§hphase, werden einfach halbiert. (etwas Fl√ľssigkeit verdunstet ja wieder) F√ľr die morgendliche M√§hphase wird der vorhergehende Tag und die bis zum Ende der M√§hphase angefallenen Niederschl√§ge zusammengez√§hlt. (morgens sollte der M√§her etwas empfindlicher sein). Sollte der berechnete Niederschlagswert an zwei hintereinander folgenden Tagen √ľber dem Schwellwert liegen, wird der Schwellwert auf 0 gesetzt. Im Klartext hei√üt das, wenn es zwei Tage regnet, darf am dritten¬†Tag kein Tropfen vom Himmel fallen, ansonsten bleibt der M√§her auch an diesem Tag zu Hause. (Damit sich die Wiese nach Tagen des Dauerregnens erholen kann)

Voraussetzungen und Installation:

Hardware - Edimax SmartPlug

Link auf Amazon.de:

Webserver

  • PHP CURL library
  • PHP allow_url_fopen=on
  • Crontab oder ein¬†Task Scheduler um den Wetterbericht herunterzuladen und um die Schaltvorg√§nge durchzuf√ľhren
  • Verbindung vom Webserver zum Stromstecker. (Sollte sich der Webserver im Internet befinden: Portforwarding am Router zum SmartPlug)
  • Schreibzugriff auf die Datei¬†values.json im Root-Ordner des Skripts

Variablen-Konfiguration

  • Konfiguration der Variablen in der Datei update.php: $timespan1, $timespan2, $smartplug (User, Password and IP)√Ąndern der URL¬†f√ľr den Abruf des Wetters¬†API Location¬†und¬†API-Key f√ľr¬†http://openweathermap.org/api. Um weitere Wetterquellen hinzuzuf√ľgen k√∂nnen¬†die Werte des¬†Array $arr['weaterdata'][date('Ymd')] upgedated werden. Aus diesem Grund werden alle Wetterdaten zuerst gesammelt und dann ausgewertet.

Dateien:

Basis des Skripts ist die Datei update.php. In dieser befinden sich die Variablendefinitionen und die Logik f√ľr die Ein- und Ausschaltvorg√§nge. Die Datei Update.php speichert Variablen und aktuelle Wetterdaten in die dynamisch generierte Datei values.json.

Die Anzeige des Status und die Steuerung erfolgt √ľber die Datei index.php. Die Datei index.php verwendet dazu wiederum die Datei values.json.

Update.php

[+]
<?php
/*
please visit: https://www.libe.net/universelle-maehroboter-steuerung
and https://www.script-example.com for more details

Prerequisite:
# PHP CURL library
# PHP allow_url_fopen=on
# Crontab or Task Scheduler to periodically call the Page outsite the Timespans
# Configured Smartplug and Router Access to it
# Configured Variables in this Script:$timespan1, $timespan2, $smartplug (User, Password and IP)
# Modify Weather API Location and API-Key in this Script
# Write Access to values.json File
*/

/*-----------------------------------------------------------------------------
Variabledefinitions and Variablehandling
-----------------------------------------------------------------------------*/
$filevaluesjson="values.json";
//initial Settings:
$smartplug="http://admin:Password@xx.???.???.???:10000/smartplug.cgi";//not saved in values.json for Security Reason change Username, Password and IP/Port
$strjson = '
{
    "too_much_rain": "2",
    "timespan1": 
		{"start":"06:30",
		 "end":"09:00"
		},
	"timespan2": 
		{"start":"19:30",
		 "end":"23:30"
		},
    "crontimes":"01:55,02:55,03:55,04:55,05:55,06:55,07:55,08:55,10:55,11:55,12:55,13:55,14:55,15:55,16:55,17:55,18:55,20:55,21:55,22:55,06:20,19:20",
    "too_much_rain": "2",
	"sendermail":"myemail@myemail.loc",
	"mailto":"myemail@myemail.loc",
	"mower_name":"Mower",
	"manual":"auto",
	"openweathermapurl":"http://api.openweathermap.org/data/2.5/forecast?id=???&appid=????????????",
	"on":"<?xml version=\"1.0\" encoding=\"utf-8\"?><SMARTPLUG id=\"edimax\"><CMD id=\"setup\"><Device.System.Power.State>ON</Device.System.Power.State></CMD></SMARTPLUG>",
	"off":"<?xml version=\"1.0\" encoding=\"utf-8\"?><SMARTPLUG id=\"edimax\"><CMD id=\"setup\"><Device.System.Power.State>OFF</Device.System.Power.State></CMD></SMARTPLUG>"
}';

function smartplug($smartplug,$action)
{$ch = curl_init();curl_setopt($ch,CURLOPT_URL, $smartplug);//url
curl_setopt($ch,CURLOPT_POST, count($action));curl_setopt($ch,CURLOPT_POSTFIELDS, $action);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);//return result
$result = curl_exec($ch);curl_close($ch);return $result;}

$arrtimes=array('0','2','5','8','11','14','17','20','23');
function realhour($hour){if ($hour<0) return "00:00";if ($hour<10)$hour="0".$hour.":00";else $hour=$hour.":00";return $hour;}

//merge initial Settings and Saved Data to Settings Array $arr
$arr= json_decode($strjson,true);//build Array and write initial settings
if (!file_exists($filevaluesjson)){file_put_contents($filevaluesjson,json_encode($arr));$arr["last_update_runtime"]=$timenow;}
$strfilevaluesjson=file_get_contents($filevaluesjson); //get json variables from File and update initial Settings:
if (!empty($strfilevaluesjson)){$arrfilevaluesjson= json_decode($strfilevaluesjson,true);$arr=array_merge($arr, $arrfilevaluesjson);}

$timenow=date('H:i', time());//$timespan1e=explode("-",$arr['timespan1']);$timespan2e=explode("-",$arr['timespan2']);

//test if update.php is called twice 
if ($arr["last_update_runtime"]!=date('H:i d.m.Y', time()))
	{
		$arr["last_update_runtime"]=date('H:i d.m.Y', time());
	}else {echo "Script is running in this minute";exit;die();}


//load Weatherdata from Openweathermap:
$string=file_get_contents($arr['openweathermapurl']);
if (strlen($string)>1000) $downloadopenweather="ok";
$obj =(json_decode($string));
//echo $string;
foreach($obj->list as $mydata)
{
while (list($key, $value) = each($mydata->rain)) 
		{	
		 $arr['weatherdata_openweathermaps'][date('Ymd',$mydata->dt)][date('H:i',$mydata->dt)]=$value;
		 }
	}


//merge individual weather data arrays to one array:
//it is possible to merge more data: $arr["weatherdata"][date('Ymd')]=array_merge($arr['weatherdata_openweathermaps'][date('Ymd')], $arr["weatherdata_additionalweatherdata"][date('Ymd')]);
$arr["weatherdata"][date('Ymd')]=$arr['weatherdata_openweathermaps'][date('Ymd')];

//calc
$arr["rain"][date('Ymd')]="0";
while (list($key, $value) = each($arr["weatherdata"][date('Ymd')]))
{
	if ($timenow<=$arr[timespan1]["end"]){//timespan1 ignore Rain after timespan1:
		if (trim($key)<trim($arr[timespan1]["end"])){ $arr["rain"][date('Ymd')]=$arr["rain"][date('Ymd')]+$value;echo $key. " addvalue".$value."rain".$arr["rain"][date('Ymd')]."<br>";}
		
	}else
	{//timespan2
	if (trim($key)>=$arr[timespan2]["start"]) 
		{$arr["rain"][date('Ymd')]=$arr["rain"][date('Ymd')]+$value;
		 echo $key.":+".$value."<br>";
		}else
		{
		$arr["rain"][date('Ymd')]=$arr["rain"][date('Ymd')]+($value/2);
		echo $key.":+ 1/2: ".($value/2)."<br>";
		}
	}
}

//for morning: additional values from yesterday:
if ($timenow<=$arr["timespan1"]["end"]){
while (list($key, $value) = each($arr["weatherdata"][date('Ymd', strtotime('-1 days'))]))
{$arr["rain"][date('Ymd')]=$arr["rain"][date('Ymd')]+$value;}
$arr["calctimespan"]="timespan1";
}else $arr["calctimespan"]="timespan2";
$raingr0=0;

//when rain>$too_much_rain on last 2 days: too_much_rain=0 
if ($timenow<=$arr["timespan1"]["end"]) $numdays=3; else $numdays=2;
			for ($x = 1; $x <= $numdays; $x++) 	{$vallastday=$arr["rain"][date('Ymd', strtotime('-'.$x.' days'))];if ($vallastday>$arr["too_much_rain"]) $raingr0++;}
if ($raingr0>=2){echo "on the last 2 days: $raingr0 raindays change threshold to 0";$arr["actual_too_much_rain"]=0;}else $arr["actual_too_much_rain"]=$arr["too_much_rain"];
//end change too_much_rain

//cleanup old data:
$historydays=7;
while (list($key, $value) = each($arr["weatherdata"])) 
{	if ($key<date('Ymd', strtotime('-'.$historydays.' days'))) 
	{echo "cleanup:".date('Ymd', strtotime('-'.$historydays.' days'));
	 unset($arr["rain"][$key]);
	 unset($arr["weatherdata"][$key]);
	 unset($arr["weatherdata_openweathermaps"][$key]);
	 unset($arr['weatherdata_forecastio_Probability'][$key]);
	 unset($arr['weatherdata_forecastio_Intensity'][$key]);
	}
}

//reset manual to auto
$arr["manual"]=trim($arr["manual"]);

if ((($timenow<$arr["timespan1"]["start"] or $timenow > $arr["timespan1"]["end"]) and ($timenow<$arr["timespan2"]["start"] or $timenow > $arr["timespan2"]["end"])) or $arr["manual"]=="on" or $arr["manual"]=="on".date('Ymd')) 
	
{//not in timespan1 or 2
/*-------------------------------------------------------------------------------------
Turn the Smartplug on or off:
-------------------------------------------------------------------------------------*/
//Ein und Aus-Schalten ...
if (!empty($_GET["test"]))$arr["rain"][date('Ymd')]=$_GET["test"];
if (($arr["rain"][date('Ymd')]>$arr["actual_too_much_rain"] and $arr["manual"]!="on" and $arr["manual"]!="on".date('Ymd')) or $arr["manual"]=="off" or $arr["manual"]=="off".date('Ymd') or ($arr["manual"]=="off".date('Ymd', strtotime('-1 days')) and $timenow < $arr["timespan1"]["end"])) 
{//power off ...			   
	$arr["smartplug_answer"]=smartplug($smartplug,$arr["off"]); 
	//2nd try:
	if (strlen($arr["smartplug_answer"])<2) $arr["smartplug_answer"]=smartplug($smartplug,$arr["off"]);
	if (strlen($arr["smartplug_answer"])>2){if ($arr["power"]!="off") $arr["powerchanged"]=date('Ymd H:i'); $arr["power"]="off";$arr["errorcounter"]="0";}
} else 
{//power on ...
	$arr["smartplug_answer"]=smartplug($smartplug,$arr["on"]);
	if (strlen($arr["smartplug_answer"])<2) $arr["smartplug_answer"]=smartplug($smartplug,$arr["on"]);//..2nd try
	if (strlen($arr["smartplug_answer"])>2){if ($arr["power"]!="on") $arr["powerchanged"]=date('Ymd H:i');$arr["power"]="on";$arr["errorcounter"]="0";} 
}
if (strlen($arr["smartplug_answer"])<2){$arr["power"]="no";}
echo "Antwort:".$arr["smartplug_answer"];
}//end. not in timespan1 or 2

/*-------------------------------------------------------------------------------------
Mail on errors:
-------------------------------------------------------------------------------------*/
if (strlen($arr["smartplug_answer"])<=2) {$arr["errorcounter"]++;$arr["smartplug_answer"]=$arr["errorcounter"];}
if ($arr["errorcounter"]==3 or $arr["errorcounter"]==6) {
$headers = "From:" . $arr[sendermail];
mail($mailto, 'Warnung Smartplug not accessible', "The Smartplug was not accessible",$headers, "-f ".$arr[sendermail]);
}

echo "<h1>Array:</h1>";
print_r($arr);

//save all Values to json-File:
file_put_contents($filevaluesjson,json_encode($arr));

?>

 Index.php

[+]
<?php
$filevaluesjson="values.json";
$strfilevaluesjson=file_get_contents($filevaluesjson);
$arr= json_decode($strfilevaluesjson,true);
error_reporting(0);
?>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>das Oswald - Experiment</title>
    <link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
	<link rel="stylesheet" href="style.css" type="text/css" />
  </head>
  <body>

<?php
$timenow=date('H:i', time());
$time_last_update=str_replace(" ".date('d.m.Y'),"",$arr["last_update_runtime"]);
$arrcrontimes=explode(",", $arr[crontimes]); //find out next Cronjob
    natsort($arrcrontimes);
    foreach ($arrcrontimes as $next) {if (empty($nextcron))$nextcron=$next;
        if ($timenow < $next) {$nextcron=$next;break;}
    }

if ($arr["manual"]=="auto")
{
    if (trim($arr["power"])!="on") echo "<h1 class=\"no\">".$arr[mower_name]." is not activated</h1>";
    else { //power=on
            if (($timenow<$arr["timespan1"]["start"] or $timenow > $arr["timespan1"]["end"]) and ($timenow<$arr["timespan2"]["start"] or $timenow > $arr["timespan2"]["end"]))	
            {//power on and not in the timespan:																						   
            echo "<h1 class=\"yes\">".$arr[mower_name]." hat Dienst, mäht aber derzeit nicht"; if ($timenow<$arr["timespan2"]["start"]) echo " next start at ".$arr["timespan2"]["start"]." Uhr ";
                                if ($timenow<$arr["timespan1"]["start"]) echo " next start at ".$arr["timespan1"]["start"]." Uhr ";
                                echo "</h1>";
            }else echo "<h1 class=\"yes\">".$arr[mower_name]." is activated</ h1>";																									   
        }
}else 	if ($arr["power"]!="on") echo "<h1 class=\"no\">Manual: ".$arr[mower_name]." is not activated</h1>";else echo "<h1 class=\"yes\">Manual: ".$arr[mower_name]." is activated</h1>";
?>
<div class="zeit">State Power now: <b><?php echo $arr["power"]." </b>(seit ".$arr["powerchanged"].") last answer <b>".$arr[smartplug_answer];?></b></div>
	  Time and date last update: <b><?php echo $arr["last_update_runtime"];
	  ?></b> <?php echo "next update<b> $nextcron Uhr</b><br>";?>

<?php  if (!empty($_GET["manual"])) {
		  echo "<script language=\"javascript\">window.location.href = \"index.php\"</script>";
		  if ($_GET["manual"]=="offtd") $arr["manual"]="off".date('Ymd');elseif ($_GET["manual"]=="ontd") {$arr["manual"]="on".date('Ymd');}else $arr["manual"]=$_GET["manual"];	  
        //save all Values to json-File:
        file_put_contents($filevaluesjson,json_encode($arr));  
	  }?>
<div class="panel panel-default">
	<div class="container"><br>
		<?php 
        echo "<button type=\"button\" "; if ($arr["manual"]!="auto") echo "onclick=\"window.location.href='index.php?manual=auto'\""; echo "class=\"btn btn-primary"; if ($arr["manual"]=="auto") echo " disabled"; echo" \">auto</button> ";
        echo "<button type=\"button\" "; if ($arr["manual"]!="on") echo "onclick=\"window.location.href='index.php?manual=on'\""; echo "class=\"btn btn-success"; if ($arr["manual"]=="on") echo " disabled"; echo "\">on</button> ";
        echo "<button type=\"button\" "; if ($arr["manual"]!="on".date('Ymd')) echo "onclick=\"window.location.href='index.php?manual=ontd'\""; echo "class=\"btn btn-success"; if ($arr["manual"]=="on".date('Ymd')) echo " disabled";  echo "\">on today</button> ";
        echo "<button type=\"button\" "; if ($arr["manual"]!="off") echo "onclick=\"window.location.href='index.php?manual=off'\""; echo "class=\"btn btn-danger"; if ($arr["manual"]=="off") echo " disabled"; echo "\">off</button> ";
        echo "<button type=\"button\" "; if ($arr["manual"]!="off".date('Ymd')) echo "onclick=\"window.location.href='index.php?manual=offtd'\""; echo "class=\"btn btn-danger"; if ($arr["manual"]=="off".date('Ymd')) echo " disabled";  echo "\">off today</button> ";
        echo "<br><div style=\"color:#888;font-size:0.6em;\">(Übernahme der Einstellungen erfolgt erst beim nächsten Update: <b>$nextcron</b> Uhr)</div>";
        ?>

	  <br>actual mode: <b><?php echo $arr["manual"];?></b> (<?php $arr["manual"]=trim($arr["manual"]);
		if ($arr["manual"]=="auto") echo "Weather mode: the mower will start automatically";
		if ($arr["manual"]=="on") echo "Weather mode deactivated: mower is on";
		if ($arr["manual"]=="off") echo "Weather mode deactivated: mower is off";
		if ($arr["manual"]=="off".date('Ymd') or ($arr["manual"]=="off".date('Ymd', strtotime('-1 days')) and $timenow < $arr["timespan1"]["end"]))  {echo "mower deactivated: automatic mode at "; if ($arr["manual"]!="off".date('Ymd', strtotime('-1 days'))) echo "tomorrow ";  echo $arr["timespan2"]["start"];}
		if ($arr["manual"]=="on".date('Ymd'))  {echo "mower activated: automatich mode for tomorrow ".$arr["timespan1"]["start"];}
		?>)
		<?php

if ($time_last_update>=$arr["timespan1"]["end"]) {$maxdays=1; echo "<br>Precipitation today: ";}else {$maxdays=2; echo "<br>Precipitation yesterday and today: ";}
echo "<b>".$arr["rain"][date('Ymd')]."</b><br>";echo "actual calculated timespan <b>";  echo $arr[$arr[calctimespan]]["start"]. " - " .$arr[$arr[calctimespan]]["end"]; echo "</b><br>";
//Threshold: 
if ($arr["actual_too_much_rain"]!=$arr["too_much_rain"])
    {echo " on the last $numdays days precipitation, the treshold value is set from ".$arr["too_much_rain"]." to 0<br>";
    }else echo "treshold value not changed (". $arr["actual_too_much_rain"].")";
    
echo "<br><br>Weatherdata:<table class=\"table\"><tr><td>Time</td><td>Openweather</td><td>calc</td>";
    ksort($arr["weatherdata"][date('Ymd')]);
while (list($key, $value) = each($arr["weatherdata"][date('Ymd')])) 
{echo "<tr><td>".$key."</td><td>".$arr["weatherdata_openweathermaps"][date('Ymd')][$key]."</td><td>".$value."</td></tr>";
}
echo "</td></tr></table>";
?><div style="font-size:0.6em;">
		(<?php echo "Calculation add all precipitation values, if ".$arr["actual_too_much_rain"]; echo": power off.";
if ($time_last_update>$arr["timespan1"]["end"]) echo " outside timespan $timespan2 use precipitation/2)"; 
		?>
		Sources: <a href="http://openweathermap.org">openweathermap.org</a>) 
<?php
echo " <a href=\"values.json\">Values.json</a>";
?>
</div></div> 
    <script src="bootstrap/jquery-1.11.3.js"></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
  </body>
</html>

Download des Scripts:

smartplug.zip

positive Bewertung({{pro_count}})
Beitrag bewerten:
{{percentage}} % positiv
negative Bewertung({{con_count}})

DANKE f√ľr deine Bewertung!

Aktualisiert: 21.12.2018 von Bernhard |ūüĒĒ

‚ě® Regex, √úberblick, Begriffe, Parameter, Grundlagen und Beispiele | ‚ě¶ PHP | PHP UTF-8 Umlaute ‚ě®

Top-Artikel in diesem Bereich


PHP UTF-8 Umlaute

Bei Problemen mit den Umlauten in PHP, liegt das meist an einer falschen Zeichenkodierung.


PHP Befehle: Funktionen auf einen Blick

Die¬†verf√ľgbaren PHP-Funktionen (Befehle) k√∂nnen mit der PHP-Funktion¬†get_defined_functions();¬†aufgelistet werden.


Regex, √úberblick, Begriffe, Parameter, Grundlagen und Beispiele

Regex ist eine universelle Beschreibungssyntax um bestimmte Teile aus Zeichenketten zu pr√ľfen oder zu filtern. Als Beispiel k√∂nnten mit Regex sehr einfach alle <h1>-√úberschriften aus einem HTML-Quellcode herausgefiltert werden. Angefangen mit PHP, habe ich Regex sp√§ter auch in PowerShell und JavaScript eingesetzt. Zugegeben, anfangs habe ich Beispiele aus dem Internet f√ľr meine Einsatzzwecke angepasst und diese nur teilweise verstanden, zumal die Regex-Syntax doch...

Fragen / Kommentare