Moin,
ich wurde gefragt, ob ich nicht mein Automowerskript veröffentlich könnte. Da ich das nicht irgendwo in einem Thread machen wollte, mache ich hier mal was neues „eindeutiges“ auf.
Da das Skript im Endeffekt recht einfach zu bedienen ist, habe ich jetzt nicht auf den RS Exporter zurückgegriffen.
Vorgehensweise:
- Client Socket auf Automower IP erstellen.
- Cutter auf den Socket mit folgenden Einstellungen:
- Benutze feste Schritte
- Eingabelänge 5
- Sync Zeichen (HEX) 0F
- Register Variable auf den Cutter mit Target das nachfolgende Skript
Zusätzlich benötigt man noch ein paar Variablen die nicht automatisch erstellt werden. Und deren IDs (die man ins Skript einträgt)
Automower Status (Bool) -> Enthält einen Wochenplan wie gewünscht
Errorcode Counter (Int)
Modus (Int) inkl Profil. Siehe später
Mäh Saison (Bool)
Regenpause (Bool)
Siehe Bild.
Der Wochenplaner wird wie gewohnt erstellt…
–> SetValue($_IPS[„TARGET“],true/false)
Er zeigt, wann der Mower fahren soll, und wann nicht.
WICHTIG: Im Mower habe ich die interne Zeitsteuerung komplett deaktiviert, weil es mit dem Wochenplaner einfach flexibler ist. Ich nehme in Kauf, dass bei einem IPS Absturz während der Mähphase das Gerät einfach weiter arbeitet. Aber das soll ja generell nicht passieren.
Der ErrorCode Counter wurde von mir eingeführt, weil ich manchmal einen Fehler bekomme (Mower festgefahren) aber er schafft es dann doch noch sich zu befreien. Und da will ich nicht genervt werden, sondern erst wenn das Gerät x mal hintereinander diesen Fehler geschickt hat.
Modus
Mit diesem Profil versehen
Mäh Saison schaltet das komplette Skript inkl. Client Socket usw. aus. Schalte ich immer im Winter dazu, bzw. im Sommer wieder an. Hiermit kann auch im Webfront einiges deaktiviert werden.
Regenpause wird durch externe Sensoren gesetzt, um den Mower nach Hause zu schicken. Dies geschieht bei mir entweder durch einen Fühler (aktueller Wert) oder durch das RainForecast Skript von Raketenschnecke. (Von hier nochmal Danke dafür!)
Nun mal das Skript. Da es eigentlich nicht zur Veröffentlichung gedacht war, sind noch einige Dinge, die verbessert werden können. Aber so ist das wohl immer.
Manuelle Anpassungen im oberen Bereich sind notwendig!
<?php
// V1.00 11.12.2014 - Erstes Release
// Dieses Skript fragt Datenpunkte eines Husqvarna Automowers vom Typ 220AC ab, und erstellt
// entsprechende Variablen dazu. Weiterhin werden Push Nachrichten im Fehlerfall abgesetzt,
// und der Mower kann über einen Wochenplaner (IPS 3.2) gesteuert werden.
//
// © by Kevin Heidrich 2014
// kevinheidrich@me.com
// Web: www.kh-solutions.de
//Vorgehensweise:
// - Client Socket auf Automower IP erstellen.
// - Cutter auf den Socket mit folgenden Einstellungen:
// * Benutze feste Schritte
// * Eingabelänge 5
// * Sync Zeichen (HEX) 0F
// - Register Variable auf den Cutter mit Target das nachfolgende Skript
// Zusätzlich benötigt man noch ein paar Variablen die nicht automatisch erstellt werden. Und deren IDs (die man ins Skript einträgt)
// Automower Status (Bool) -> Enthält einen Wochenplan wie gewünscht
// Errorcode Counter (Int)
// Modus (Int) inkl Profil. Siehe später
// Mäh Saison (Bool)
// Regenpause (Bool)
//
// So meldet sich das Skript im Debug Log
$skriptName = "MOWER";
// Client Socket (zum Senden der Daten)
$clientSocketID = 45983 /*[Automower]*/;
// Root Kategorie (Konnte ich automatisch ermitteln, aber halt nicht immer korrekt!)
$rootCategoryID = 54655 /*[Automower]*/;
// Variable - Wochentimer
$weekStateID = 12891 /*[Automower\Automower Status]*/ ;
// Variable - Regenpause
$rainStateID = 36120 /*[Automower\Regenpause]*/;
// Taste - Modus (0 Auto / 1 Dauerhaft / 2 Rasensperre)
$mowerModeID = 37016 /*[Automower\Modus]*/;
// Taste - Mäh Saison
$mowingSeasonID = 26645 /*[Automower\Mäh Saison]*/;
// Timer - Fragt die Betriebsdaten ab
$pollTimerID = 46678 /*[Automower\Decode_Mower\]*/;
// String Variable der letzten Aktualisierung
$lastReadingID = 19368 /*[Automower\Letzte Aktualisierung]*/;
// Fehlerzähler. Wird benötigt, um im Fehlerfall eine Nachricht per Push Notification abzusetzen
$errorCodeCounterID = 21016 /*[Automower\Errorcode Counter]*/;
// Wie oft soll ein gleicher Fehler auftreten, bevor die Meldung per Push Notification abgesetzt wird
$maxErrorCodeCounter = 10;
// Die ID des Webfront Konfigurators
$webfrontID = 40164;
// Meldungen ins Debug Log schreiben
$debug = true;
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// Webfrontelements um das Webfront hidden schalten zu können
// ------------------------------------------------------------------------------------------------------------------------------------------------------
$webfrontElements = array( 31205 /*[Visualisierungen\WebFront Kevin\Informationen\Automower\Links\Steuerung]*/,
14964,
17536 /*[Visualisierungen\WebFront Kevin\Informationen\Automower\Links\Daten]*/
);
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// Datenarrays (Ab hier nichts mehr ändern!)
// ------------------------------------------------------------------------------------------------------------------------------------------------------
$statusArray = array(
array("Value" => 12, "Type" => "ALERT", "Text" => "Kein Schleifensignal"),
array("Value" => 18, "Type" => "ALERT", "Text" => "Niedrige Batteriespannung"),
array("Value" => 28, "Type" => "ALERT", "Text" => "Manuelles Laden erforderlich"),
array("Value" => 30, "Type" => "ALERT", "Text" => "Messer blockiert"),
array("Value" => 34, "Type" => "ALERT", "Text" => "Mäher hochgehoben"),
array("Value" => 52, "Type" => "ALERT", "Text" => "Kein Kontakt in Ladestation"),
array("Value" => 54, "Type" => "ALERT", "Text" => "PIN abgelaufen" , "RenewPIN" => true),
array("Value" => 1000, "Type" => "WORKING", "Text" => "Ladestation verlassen"),
array("Value" => 1002, "Type" => "WORKING", "Text" => "Mähen"),
array("Value" => 1006, "Type" => "WORKING", "Text" => "Mähwerk starten"),
array("Value" => 1008, "Type" => "WORKING", "Text" => "Mähwerk gestartet"),
array("Value" => 1012, "Type" => "WORKING", "Text" => "Signal starte Mähwerk"),
array("Value" => 1014, "Type" => "WAITING", "Text" => "In Ladestation ladend"),
array("Value" => 1016, "Type" => "WAITING", "Text" => "In Ladestation wartend"),
array("Value" => 1024, "Type" => "WORKING", "Text" => "Aus Ladestation ausfahren"),
array("Value" => 1036, "Type" => "WORKING", "Text" => "Viereckmodus"),
array("Value" => 1038, "Type" => "ALERT", "Text" => "Festgefahren"),
array("Value" => 1040, "Type" => "WORKING", "Text" => "Kollission / Schleife erreicht"),
array("Value" => 1042, "Type" => "WORKING", "Text" => "Suche Ladestation"),
array("Value" => 1044, "Type" => "WAITING", "Text" => "Stop"),
array("Value" => 1048, "Type" => "WORKING", "Text" => "Andocken"),
array("Value" => 1050, "Type" => "WORKING", "Text" => "Aus Ladestation fahren"),
array("Value" => 1052, "Type" => "WAITING", "Text" => "Bitte Yes drücken"),
array("Value" => 1056, "Type" => "WAITING", "Text" => "Wartend (Manuell/Home)"),
array("Value" => 1058, "Type" => "WORKING", "Text" => "Bregrenzung folgend"),
array("Value" => 1060, "Type" => "WORKING", "Text" => "N-Signal gefunden"),
array("Value" => 1064, "Type" => "WORKING", "Text" => "Suche Ladestation"),
array("Value" => 1070, "Type" => "WORKING", "Text" => "Suchschleife folgen"),
array("Value" => 1072, "Type" => "WORKING", "Text" => "Schleife folgen")
);
// ----------------------------------------------------------------------------
// Betriebsdaten
// ----------------------------------------------------------------------------
$dataArray = array(
array("ReadAddress" => array("\x01","\xf1") , "Unit" => "", "Type" => "STATUS", "Title" => "Statuscode"),
array("ReadAddress" => array("\x01","\xeb") , "Unit" => "A", "Type" => "INT", "Min" => -3, "Max" => 3, "Math" => "/1000", "Title" => "Stromfluss"),
array("ReadAddress" => array("\x02","\x33") , "Unit" => "°C", "Type" => "INT", "Min" => 0, "Max" => 50, "Title" => "Batterietemperatur"),
array("ReadAddress" => array("\x2e","\xe0") , "Unit" => "Ah", "Type" => "INT", "Min" => 0, "Max" => 3, "Math" => "/1000", "Title" => "Batterieleistung"),
array("ReadAddress" => array("\x2e","\xea") , "Unit" => "rpm", "Type" => "INT", "Min" => 0, "Max" => 3000, "Title" => "Drehzahl Mähwerk"),
array("ReadAddress" => array("\x2e","\xf4") , "Unit" => "V", "Type" => "INT", "Min" => 15, "Max" => 25, "Math" => "/1000", "Title" => "Batteriespannung")
);
// ----------------------------------------------------------------------------
// Mowerinterne Uhrzeit
// ----------------------------------------------------------------------------
$clockArray = array(
array("CheckType" => "CLOCK_WRITE", "Address" => array("\xb6","\xb1") , "Unit" => "", "Type" => "INT","Title" => "Uhr Sekunde"),
array("CheckType" => "CLOCK_WRITE", "Address" => array("\xb6","\xb3") , "Unit" => "", "Type" => "INT","Title" => "Uhr Minute"),
array("CheckType" => "CLOCK_WRITE", "Address" => array("\xb6","\xb5") , "Unit" => "", "Type" => "INT","Title" => "Uhr Stunde"),
array("CheckType" => "CLOCK_WRITE", "Address" => array("\xb6","\xb7") , "Unit" => "", "Type" => "INT","Title" => "Uhr Tag"),
array("CheckType" => "CLOCK_WRITE", "Address" => array("\xb6","\xb9") , "Unit" => "", "Type" => "INT","Title" => "Uhr Monat"),
array("CheckType" => "CLOCK_WRITE", "Address" => array("\xb6","\xbd") , "Unit" => "", "Type" => "INT","Title" => "Uhr Jahr")
);
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// Timer
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
if ($_IPS['SENDER'] == "TimerEvent")
{
if ($debug)
IPS_LogMessage($skriptName,"Timer event");
if ($_IPS['EVENT'] == $pollTimerID)
{
if ($debug)
IPS_LogMessage($skriptName,"Lese Betriebsdaten");
// Normale Daten
foreach($dataArray as $dataEntry)
{
$data = "\x0f".$dataEntry["ReadAddress"][0].$dataEntry["ReadAddress"][1]."\x00\x00";
if ($debug)
IPS_LogMessage($skriptName,"-> ".$dataEntry["Title"]." ".bin2hex($data));
csck_sendtext($clientSocketID, $data);
}
}
else
IPS_LogMessage($skriptName,"Timer ID nicht gefunden");
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// Rückantwort über Register Variable auswerten
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
else if ($_IPS['SENDER'] == "RegisterVariable")
{
$data = $_IPS['VALUE'];
$preFix = (substr($data,0,1));
$addressHigh = substr($data,1,1);
$addressLow = substr($data,2,1);
/* if ($debug)
IPS_LogMessage($skriptName,"Empfange ".bin2hex($data));
*/
// Ungültige Datenpakete raus
if ($preFix != chr(0x0F))
{
IPS_LogMessage($skriptName,"Prefix falsch!");
exit();
}
SetValue($lastReadingID,date("H:i:s d.m.Y"));
// ------------------------------------------------------------------------------------------
// Normales Datenarray
// ------------------------------------------------------------------------------------------
foreach($dataArray as $dataEntry)
{
if ($dataEntry["ReadAddress"][0] != $addressHigh || $dataEntry["ReadAddress"][1] != $addressLow)
continue;
// Wie müssen die Daten interpretiert werden?
// Normale Value
if ($dataEntry["Type"] == "INT")
{
$value = (ord(substr($data,3,1))+256*ord(substr($data,4,1)));
if ($value & 0x8000)
$value = $value - 0x10000;
if (isset($dataEntry["Math"]))
{
eval("\$value = \$value".$dataEntry["Math"].";");
$value = floatval($value);
}
if ($value < $dataEntry["Min"] || $value > $dataEntry["Max"])
IPS_LogMessage($skriptName,"Value invalid!!! -> ".$dataEntry["Title"]."=".$value);
else
{
if ($debug)
IPS_LogMessage($skriptName,$dataEntry["Title"]."=".$value);
setVariable($rootCategoryID,$dataEntry["Title"],$value,$dataEntry["Unit"]);
}
}
// Status Anzeige
else if ($dataEntry["Type"] == "STATUS")
{
$value = (ord(substr($data,3,1))+256*ord(substr($data,4,1)));
if ($debug)
IPS_LogMessage($skriptName,$dataEntry["Title"]."=".$value);
foreach($statusArray as $status)
{
if ($status["Value"] == $value)
{
// Bei Fehler eine Fehlermeldung absetzen!
if ($status["Type"] == "ALERT")
{
$errorCodeCounter = GetValueInteger($errorCodeCounterID);
// Aber nur, wenn der Fehler die letzten 10 Abfragen (5 Minuten) besteht
if ($errorCodeCounter == $maxErrorCodeCounter)
WFC_PushNotification($webfrontID,"Fehler","Automower FEHLER - ".$status["Text"],"Fehler",0);
$errorCodeCounter ++;
SetValueInteger($errorCodeCounterID,$errorCodeCounter);
}
else
SetValueInteger($errorCodeCounterID,0);
setVariable($rootCategoryID,$dataEntry["Title"],$status["Text"],$dataEntry["Unit"]);
setVariable($rootCategoryID,$dataEntry["Title"]." - Type",$status["Type"],$dataEntry["Unit"]);
// PIN muss erneuert werden
if (isset($status["RenewPIN"]))
{
/*
$data=chr(0x0F).chr(0x80).chr(0x5F).chr(0x00).chr(0x01);
csck_sendtext($clientSocketID , $data );
IPS_Sleep(100);
$data=chr(0x0F).chr(0x80).chr(0x5F).chr(0x00).chr(0x09);
csck_sendtext($clientSocketID , $data );
IPS_Sleep(100);
$data=chr(0x0F).chr(0x80).chr(0x5F).chr(0x00).chr(0x07);
csck_sendtext($clientSocketID , $data );
IPS_Sleep(100);
$data=chr(0x0F).chr(0x80).chr(0x5F).chr(0x00).chr(0x06);
csck_sendtext($clientSocketID , $data );
IPS_Sleep(100);
$data=chr(0x0F).chr(0x80).chr(0x5F).chr(0x00).chr(0x12);
csck_sendtext($clientSocketID, $data);
IPS_Sleep(100);
*/
IPS_LogMessage($skriptName,"HINWEIS - PIN war abgelaufen, und wurde erneuert!");
WFC_PushNotification($webfrontID,"Hinweis","Automower HINWEIS - PIN war abgelaufen, und wurde erneuert!","Hinweis",0);
}
continue;
}
}
}
}
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// Buttons im WebFront
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
else if ($_IPS['SENDER'] == "WebFront")
{
SetValue($_IPS["VARIABLE"],$_IPS["VALUE"]);
// ----------------------------------
// Button - Mowermodus
// ----------------------------------
if ($_IPS["VARIABLE"] == $mowerModeID)
{
// Automatisch
if ($_IPS["VALUE"] == 0)
{
// Automatisch, nur wieder aktivieren, wenn Timer aktiv
if (GetValue($weekStateID))
setMowerActivity(true);
else
setMowerActivity(false);
}
// Dauerhaft
else if ($_IPS["VALUE"] == 1)
{
setMowerActivity(true);
}
// Rasensperre
else if ($_IPS["VALUE"] == 2)
{
setMowerActivity(false);
}
}
// ----------------------------------
// Button - Mäh Saison
// ----------------------------------
else if ($_IPS["VARIABLE"] == $mowingSeasonID)
{
// Mäh Saison schliest den Client Socket und deaktiviert die Abfragetimer
// Saison aktiv
if ($_IPS["VALUE"])
{
IPS_LogMessage($skriptName,"Aktiviere Automower");
// Connector aktivieren
IPS_SetProperty($clientSocketID, "Open", true);
IPS_ApplyChanges($clientSocketID);
// Timer Poll aktivieren
IPS_SetEventActive($pollTimerID, true);
// Widget Elemente visible schalten
foreach($webfrontElements as $element)
IPS_SetHidden($element, false);
}
else
{
IPS_LogMessage($skriptName,"Deaktiviere Automower");
// Connector deaktivieren
IPS_SetProperty($clientSocketID, "Open", false);
IPS_ApplyChanges($clientSocketID);
// Timer Poll deaktivieren
IPS_SetEventActive($pollTimerID, false);
// Widget Elemente hidden schalten
foreach($webfrontElements as $element)
IPS_SetHidden($element, true);
}
}
else
IPS_LogMessage($skriptName,"Button (".$_IPS['VARIABLE'].") nicht unterstützt!");
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// Variablen die sich ändern
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------
else if ($_IPS['SENDER'] == "Variable")
{
IPS_LogMessage($skriptName,"SENDER = VARIABLE");
// Wenn die Mäh Saison deaktiv ist, kann das hier ignoriert werden!
if (!GetValueBoolean($mowingSeasonID))
{
IPS_LogMessage($skriptName,"Variablenänderung ignoriert - Keine Mähsaison!");
return;
}
// Event Wochentimer
// --------------------------
/*
if (!drivenow)
setMowerActivity(active)
*/
// --------------------------
// Es wird Regen gemeldet (Live oder Forecast!)
// --------------------------
if ($_IPS['VARIABLE'] == $rainStateID)
{
if ($_IPS["VALUE"])
setMowerActivity(false);
else if (GetValue($weekStateID) || GetValue($mowerModeID) == 1)
setMowerActivity(true);
else
setMowerActivity(false);
}
else if ($_IPS['VARIABLE'] == $weekStateID)
{
setMowerActivity($_IPS['VALUE']);
}
else
IPS_LogMessage($skriptName,"Variable (".$_IPS['VARIABLE'].") nicht unterstützt!");
}
else
IPS_LogMessage($skriptName,"Sender (".$_IPS['SENDER'].") nicht unterstützt!");
// -----------------------------------------------------------------------------
// Mower ansteuern
// -----------------------------------------------------------------------------
function setMowerActivity($value)
{
global $skriptName;
global $clientSocketID;
global $rainStateID;
global $mowerModeID;
// Wenn Regen -> kein Start (Stop erlaubt!)
// Bzw. wenn Manuell.. dann scheiss auf den Regen
if ($value && GetValue($rainStateID) && GetValue($mowerModeID) != 1)
{
IPS_LogMessage($skriptName,"Automower wird nicht gestartet - Es regnet!");
return;
}
// Wenn Rasensperre -> kein Start (Stop erlaubt!)
if ($value && GetValue($mowerModeID) == 2)
{
IPS_LogMessage($skriptName,"Automower wird nicht gestartet - Rasen gesperrt!");
return;
}
// Auto Modus
csck_sendtext($GLOBALS["clientSocketID"], "\x0f\x81\x2c\x00\x01");
// Alle Timer aus
if ($value)
{
IPS_LogMessage($skriptName,"Automower ON");
// aktiv -> fahren
csck_sendtext($clientSocketID, "\x0f\xca\x4e\x00\x01");
}
else
{
IPS_LogMessage($skriptName,"Automower OFF");
// inaktiv -> nach Hause
csck_sendtext($clientSocketID, "\x0f\xca\x4e\x00\x00");
}
}
// -----------------------------------------------------------------------------
// Zusatzfunktionen
// -----------------------------------------------------------------------------
function setVariable($parentID,$varName,$value,$unit)
{
global $skriptName;
$varProfilName = "";
// Evtl. Profil anlegen
if ($unit != "")
{
if ($unit == "°C")
$varProfilName = "Mower_Profil_Temperatur";
else if ($unit == "%")
$varProfilName = "Mower_Profil_Prozent";
else if ($unit == "µA")
$varProfilName = "Mower_Profil_Strom";
else
$varProfilName = "Mower_Profil_".$unit;
if (is_bool($value))
$varProfilName .= "_bool";
else if (is_int($value))
$varProfilName .= "_int";
else if (is_float($value))
$varProfilName .= "_float";
else if (is_string($value))
$varProfilName .= "_string";
// Evtl. ein Variablenprofil anlegen
if (!IPS_VariableProfileExists($varProfilName))
{
if (is_bool($value))
IPS_CreateVariableProfile($varProfilName,0);
else if (is_int($value))
IPS_CreateVariableProfile($varProfilName,1);
else if (is_float($value))
IPS_CreateVariableProfile($varProfilName,2);
else if (is_string($value))
IPS_CreateVariableProfile($varProfilName,3);
IPS_SetVariableProfileText($varProfilName,""," ".$unit);
}
}
// Variable nicht vorhanden.. erstellen!
if(!$varID=@IPS_GetVariableIDByName($varName,$parentID))
{
// Variable vom Typ erzeugen
if (is_bool($value))
$varID=IPS_CreateVariable(0);
else if (is_int($value))
$varID=IPS_CreateVariable(1);
else if (is_float($value))
$varID=IPS_CreateVariable(2);
else if (is_string($value))
$varID=IPS_CreateVariable(3);
else
return;
IPS_SetName($varID, $varName);
IPS_SetParent($varID,$parentID);
if ($varProfilName != "")
IPS_SetVariableCustomProfile($varID,$varProfilName);
}
if (!SetValue($varID,$value))
IPS_LogMessage($skriptName,"Konnte Variable ".$varID." nicht mit ".$value." schreiben!");
}
function getVariable($parentID,$varName)
{
global $skriptName;
// Variable vorhanden
if($varID=IPS_GetVariableIDByName($varName,$parentID))
{
return GetValue($varID);
}
IPS_LogMessage($skriptName,"Konnte Variable $varName aus Parent $parentID nicht lesen");
return null;
}
?>
Zum Schluss noch das Webfront, damit man einen Eindruck bekommt.
Viel Spass beim zusammen fummeln.
Gruß
Kevin