/set` entgegen und antworten auf * `/result` mit `success` oder einer Fehlermeldung. * * Der Lesestand liegt ohnehin schon auf demselben Broker - der Browser * abonniert `go-eCharger/270003/...` und `wattpilot/properties/.../state` * nebeneinander (js/solar/solarMQTT.js). */ require_once(__DIR__ . "/../ajax/phpMQTT.php"); // Der Broker laeuft auf demselben Rechner wie der Webserver. Steht hier und // nicht in commands.php, damit diese Datei nicht die halbe Raumsteuerung // mitzieht, nur um zwei Konstanten zu erben. const WALLBOX_BROKER = "localhost"; const WALLBOX_PORT = 1883; /** * Die beiden Wallboxen und worin sie sich noch unterscheiden. * * Die Schluessel `frc`, `lmo`, `amp` und `fup` heissen auf beiden gleich - * es ist beides go-e-Firmware. Uebrig bleiben drei echte Unterschiede, und * die stehen hier statt in zwei getrennten Codepfaden. */ function wallboxen() { return [ "eg" => [ "titel" => "Wattpilot (EG)", "praefix" => "wattpilot/properties/", // Die Bruecke haengt "/set" an den Eigenschaftsnamen an. "namen" => [], // Wattstunden je Prozent des Schiebereglers - Akku des Autos, // das hier haengt. "whProProzent" => 140, // Zeichenketten gehen roh hinaus; die Bruecke gibt sie // unveraendert an die Wallbox weiter. "textInJson" => false, // Die Ladeplanung rechnet mit der Uhr der Wallbox. Beim alten // Python-Aufruf wurde sie vor jedem Moduswechsel gestellt // (-setTime); das bleibt so, damit die Funktion nicht verloren // geht. Der go-e hat das nie gebraucht. "uhrStellen" => true, ], "og" => [ "titel" => "go-e (OG)", "praefix" => "go-eCharger/270003/", // Zielzeit und Zielenergie heissen hier anders. Die Wattpilot // kennt "ate"/"att" gar nicht, der go-e umgekehrt "fte"/"ftt". "namen" => ["fte" => "ate", "ftt" => "att"], "whProProzent" => 90, // Der go-e liest jeden Wert als JSON. Zeichenketten muessen // deshalb in Anfuehrungszeichen stehen, sonst antwortet er mit // "tried to set api key with invalid json". "textInJson" => true, "uhrStellen" => false, ], ]; } /** * Einen Wert so schreiben, wie die jeweilige Wallbox ihn erwartet. */ function wallboxNutzlast($wert, array $konf) { if (is_bool($wert)) { return $wert ? "true" : "false"; } if (is_string($wert) && $konf["textInJson"]) { return json_encode($wert); } return (string) $wert; } /** * Mehrere Eigenschaften einer Wallbox setzen. * * Die Reihenfolge von $werte bleibt erhalten - sie ist nicht beliebig: der * Modus (`lmo`) kommt zuletzt, damit Zielzeit und Zielenergie schon stehen, * wenn er greift. * * Liefert true, wenn alles beim Broker war. Ob die Wallbox die Werte * genommen hat, steht anschliessend auf den `/result`-Topics; darauf wird * hier nicht gewartet, damit ein Klick nicht an einer stummen Wallbox * haengenbleibt. */ function wallboxSetzen(string $box, array $werte): bool { $alle = wallboxen(); if (!isset($alle[$box])) { throw new InvalidArgumentException("Unbekannte Wallbox: " . $box); } $konf = $alle[$box]; $mqtt = new Bluerhinos\phpMQTT(WALLBOX_BROKER, WALLBOX_PORT, "WallboxWeb-" . bin2hex(random_bytes(4))); if (!$mqtt->connect(true, null, "", "")) { error_log("wallboxSetzen: kein Kontakt zum MQTT-Broker"); return false; } foreach ($werte as $schluessel => $wert) { $name = $konf["namen"][$schluessel] ?? $schluessel; $mqtt->publish($konf["praefix"] . $name . "/set", wallboxNutzlast($wert, $konf), 0, false); } $mqtt->close(); return true; }