Files
Smart-Dashboard/restricted/wallboxen.php
T
adminandClaude Opus 5 77651a860a Verwaistes wattpilot.py heisst jetzt wattpilot_unused.py
Seit der Umstellung auf MQTT ruft es niemand mehr auf - kein PHP, kein
Startskript, kein Aufgabenplaner. Der neue Name macht das sichtbar, bevor
die Datei irgendwann geloescht wird; das Wallbox-Passwort steht darin
weiterhin im Klartext.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 09:06:37 +02:00

145 lines
5.4 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. Das Skript liegt seit dem
* Umbau als `/volume1/homes/wagner/wattpilot_unused.py` daneben und wird von
* niemandem mehr aufgerufen - es enthaelt das Wallbox-Passwort im Klartext
* und gehoert geloescht, sobald sicher ist, dass es niemand vermisst.
*
* 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;
}