Die Ventilsteuerung veroeffentlicht neuerdings je Steuerung zwei weitere Topics: WateringToday mit der heute schon gelaufenen Zeit je Ventil und LastWateringDay mit derselben Summe fuer den letzten Tag, an dem ueberhaupt bewaessert wurde. Beide tauchen als zweite Zeile in der Zonenkarte auf: "Heute 35m - Vortag 30m". WateringToday wird gegen das heutige Datum geprueft. Die Nachricht ist retained; nach einem Tageswechsel ohne Bewaesserung stuende sonst die Summe von gestern als heutige da. Passt der Tag nicht, zeigt die Karte "--" und nennt den Stand im Titel. Die Spalte heisst "Vortag" und nicht "Gestern", weil LastWateringDay einen eigenen wateringDay mitfuehrt - es ist der letzte Tag mit Bewaesserung, nicht zwangslaeufig der gestrige; das Datum steht im Titel. Geprueft: leeres Topic, Nutzlast mit 0 Sekunden, gefuellte Werte, veraltete Nutzlast, fehlendes Feld, kaputtes JSON, Datumsformat - zehn Faelle, keine Abweichung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
141 lines
5.4 KiB
PHP
141 lines
5.4 KiB
PHP
<?php
|
|
|
|
require_once("./phpMQTT.php");
|
|
require_once("../helper.php");
|
|
|
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|
header("Content-Type: application/json; charset=utf-8");
|
|
|
|
if (!checkLogin()) {
|
|
http_response_code(401);
|
|
echo json_encode(["success" => false, "message" => "Nicht angemeldet."]);
|
|
exit;
|
|
}
|
|
|
|
$topic = $_POST["topic"] ?? "";
|
|
$payload = $_POST["payload"] ?? "";
|
|
$allowedTopics = [
|
|
"Gartenwasser/vorn/set",
|
|
"Gartenwasser/vorn/auto",
|
|
"Gartenwasser/hinten/set",
|
|
"Gartenwasser/hinten/auto"
|
|
];
|
|
|
|
if (!in_array($topic, $allowedTopics, true)) {
|
|
http_response_code(400);
|
|
echo json_encode(["success" => false, "message" => "Nicht erlaubtes MQTT-Topic."]);
|
|
exit;
|
|
}
|
|
|
|
if (str_ends_with($topic, "/set")) {
|
|
$command = json_decode($payload, true);
|
|
$validValve = is_array($command)
|
|
&& isset($command["valve"], $command["set"])
|
|
&& in_array((int) $command["valve"], [1, 6], true)
|
|
&& in_array($command["set"], ["open", "close"], true);
|
|
if ($validValve && $command["set"] === "open") {
|
|
$validValve = isset($command["timer"])
|
|
&& is_numeric($command["timer"])
|
|
&& (int) $command["timer"] >= 1
|
|
&& (int) $command["timer"] <= 120;
|
|
}
|
|
if (!$validValve) {
|
|
http_response_code(400);
|
|
echo json_encode(["success" => false, "message" => "Ungültiger Ventilbefehl."]);
|
|
exit;
|
|
}
|
|
$payload = json_encode($command, JSON_THROW_ON_ERROR);
|
|
} elseif (!in_array($payload, ["Hoch", "Vorn", "Trog", "Stop"], true)) {
|
|
http_response_code(400);
|
|
echo json_encode(["success" => false, "message" => "Ungültiger Automatikbefehl."]);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
$mqtt = new Bluerhinos\phpMQTT("localhost", 1883, "WateringWeb-" . bin2hex(random_bytes(4)));
|
|
if (!$mqtt->connect(true, null, "", "")) {
|
|
http_response_code(503);
|
|
echo json_encode(["success" => false, "message" => "MQTT-Broker nicht erreichbar."]);
|
|
exit;
|
|
}
|
|
$mqtt->publish($topic, $payload, 0, false);
|
|
$mqtt->close();
|
|
} catch (Throwable $error) {
|
|
http_response_code(503);
|
|
echo json_encode(["success" => false, "message" => "MQTT-Fehler: " . $error->getMessage()]);
|
|
exit;
|
|
}
|
|
echo json_encode(["success" => true, "topic" => $topic, "payload" => $payload]);
|
|
exit;
|
|
}
|
|
|
|
if (!checkLogin()) {
|
|
exit;
|
|
}
|
|
|
|
// Zonen des Modals. Die Reihenfolge bestimmt die Reihenfolge der Karten, die
|
|
// Keys muessen zu wateringZones in js/solar/solarMQTT.js passen.
|
|
$zonen = [
|
|
["key" => "troughs", "label" => "Tröge", "minuten" => 5],
|
|
["key" => "raisedBeds", "label" => "Beete", "minuten" => 30],
|
|
["key" => "frontGarden", "label" => "Vorn", "minuten" => 30],
|
|
];
|
|
|
|
// Was die Automatik je Zone vorhat, fuellt solarMQTT.js aus dem RainStatus.
|
|
$planZeilen = "";
|
|
foreach ($zonen as $zone) {
|
|
$planZeilen .= " <div>{$zone['label']}: <span data-watering-plan=\"{$zone['key']}\">--</span></div>\n";
|
|
}
|
|
|
|
echo <<<HTML
|
|
<div class="p-3">
|
|
<div id="watering-feedback" class="alert d-none mb-3" role="status"></div>
|
|
|
|
<div class="card mb-3">
|
|
<div class="card-body">
|
|
<div class="fw-semibold mb-3">Automatik</div>
|
|
<div class="d-grid gap-2 mb-3">
|
|
<button type="button" class="btn btn-outline-primary" data-watering-auto-toggle data-watering-auto="Trog,Hoch" data-watering-topics="Gartenwasser/vorn,Gartenwasser/hinten" data-watering-label="Tröge und Hochbeete">Tröge und Hochbeete</button>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<button type="button" class="btn btn-outline-primary btn-sm flex-fill" data-watering-auto-toggle data-watering-auto="Trog" data-watering-topic="Gartenwasser/vorn" data-watering-label="Tröge">Tröge</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm flex-fill" data-watering-auto-toggle data-watering-auto="Hoch" data-watering-topic="Gartenwasser/hinten" data-watering-label="Hochbeete">Hochbeete</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm flex-fill" data-watering-auto-toggle data-watering-auto="Vorn" data-watering-topic="Gartenwasser/vorn" data-watering-label="Garten vorn">Garten vorn</button>
|
|
</div>
|
|
</div>
|
|
<div class="small text-muted">
|
|
$planZeilen <div class="fst-italic" data-watering-plan-updated></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
HTML;
|
|
|
|
foreach ($zonen as $zone) {
|
|
$key = $zone["key"];
|
|
$label = $zone["label"];
|
|
$minuten = $zone["minuten"];
|
|
echo <<<HTML
|
|
|
|
<div class="card mb-2">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="fw-semibold">$label</span>
|
|
<span class="badge bg-secondary" data-watering-state="$key">unbekannt</span>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-2 mb-2">
|
|
<div class="input-group input-group-sm" style="max-width: 7.5rem;">
|
|
<input class="form-control" type="number" min="1" max="120" value="$minuten" data-watering-timer="$key" aria-label="Dauer in Minuten für $label">
|
|
<span class="input-group-text">Min.</span>
|
|
</div>
|
|
<button type="button" class="btn btn-success px-4 ms-auto" data-watering-toggle data-watering-zone="$key">Ein</button>
|
|
</div>
|
|
<div class="small text-muted">
|
|
Zuletzt <span data-watering-last="$key">--</span> · <span data-watering-duration="$key">--</span><br>
|
|
Heute <span data-watering-today="$key">--</span> · Vortag <span data-watering-prevday="$key">--</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
HTML;
|
|
}
|
|
|
|
echo "\n</div>\n";
|