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!

Fragen / Kommentare