(0)
Artikel
bewerten
(100% positiv)
(2)

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.xxx.xxx.xxx: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=xxx&appid=xxxxxxxxxxxx",
	"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

letzte Änderung dieses Artikels: 30.09.2016 09:16




Kommentare