Der Schieberegler "Zusaetzlich benoetigte Ladung" rechnet Prozent in Wattstunden um. Dafuer standen bisher drei verschiedene Zahlen im Code: 140 Wh/% beim Schreiben der EG-Wallbox, 90 Wh/% beim go-e und feste 14000 Wh beim Zurueckrechnen im Browser - fuer beide Autos dieselbe. Eine Einstellung von 50 % am go-e schrieb 4500 Wh und las sich beim naechsten Oeffnen als 32 % zurueck. Jetzt kommt die Zahl aus einer Quelle: restricted/config.php nennt Auto und nutzbare Akkukapazitaet je Wallbox, carForm.php gibt die Wattstunden je Prozent als data-Attribut an den Schieberegler weiter, und das JavaScript rechnet damit zurueck statt mit einer festen Konstanten. EG: Skoda Enyaq RS, Modelljahr 2026, 84 kWh brutto / 79 kWh nutzbar OG: Jeep Compass 4xe, 11,4 kWh Das Lademodal zeigt Auto und Kapazitaet jetzt unter dem Regler an - damit sichtbar ist, worauf sich die Prozente beziehen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
143 lines
5.3 KiB
PHP
143 lines
5.3 KiB
PHP
<?php
|
|
/**
|
|
* Beide Wallboxen ueber denselben Weg ansteuern: MQTT.
|
|
*
|
|
* Bis September 2026 liefen die beiden voellig verschieden. Der go-e bekam
|
|
* seine Kommandos per HTTP (`api/set?frc=2`), die Wattpilot ueber einen
|
|
* eigenen Python-Prozess je Klick, der sich einen Websocket aufbaute,
|
|
* anmeldete, ein Feld setzte und wieder starb - inklusive Passwort im
|
|
* Klartext in `/volume1/homes/wagner/wattpilot.py`.
|
|
*
|
|
* Warum MQTT und nicht HTTP fuer beide: die Wattpilot hat keine lokale
|
|
* REST-API. Sie beantwortet auf Port 80 jeden Pfad mit 404 und spricht nur
|
|
* ueber ihren Websocket. Fronius liefert die go-e-Firmware ohne die
|
|
* HTTP-Schnittstelle aus.
|
|
*
|
|
* Warum es umgekehrt geht: der go-e hat einen eigenen MQTT-Client (`mce`),
|
|
* haengt an unserem Broker (`mcu`) und laesst Schreibzugriffe zu
|
|
* (`mcr = false`). Die Wattpilot erreicht denselben Broker ueber
|
|
* `wattpilot_bruecke.py` im SolarManager. Beide nehmen Kommandos auf
|
|
* `<praefix><schluessel>/set` entgegen und antworten auf
|
|
* `<praefix><schluessel>/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");
|
|
require_once(__DIR__ . "/config.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" => [],
|
|
// 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"],
|
|
// 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,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Wieviel Wattstunden ein Prozent des Schiebereglers bedeutet.
|
|
*
|
|
* Der Wert steht nicht in der Tabelle oben, weil er nichts mit der Wallbox
|
|
* zu tun hat, sondern mit dem Auto davor: er kommt aus der nutzbaren
|
|
* Akkukapazitaet in restricted/config.php. Wechselt das Auto, wird dort eine
|
|
* Zeile geaendert.
|
|
*/
|
|
function wallboxWhProProzent(string $box): int
|
|
{
|
|
$autos = fahrzeuge();
|
|
if (!isset($autos[$box])) {
|
|
throw new InvalidArgumentException("Kein Auto fuer Wallbox: " . $box);
|
|
}
|
|
return (int) round($autos[$box]["akkuKWh"] * 1000 / 100);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|