Skoda: Schluessel in den Einstellungen, Ladungen am Stueck, Wallbox in kW
- neuer Reiter "Fahrzeug": API-Schluessel eintragen und pruefen, ohne dass ein Wert je an den Browser zurueckgeht (restricted/skodaKeys.php) - ajax/skoda.php trennt Ladungen am Zustand statt an 30 Minuten Pause und rahmt sie mit der Zeile davor und danach ein - solarLog_skoda_ladepunkte.sql: Wallbox-Verlauf waehrend einer Ladung - Kachelformat "leistung_kw" fuer Quellen, die schon Kilowatt melden - Steckersymbol versteht evPlug als 1/0 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+39
-73
@@ -14,28 +14,25 @@
|
||||
* ein vorausgeladenes Bild oder einen Klick auf eine fremde Seite fernsteuerbar.
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
* Zwei Schlüssel, zwei Kontingente
|
||||
* Ein Kontingent für alles
|
||||
*
|
||||
* Die API begrenzt auf 20 Anfragen je Stunde **und Schlüssel**, und Befehle
|
||||
* zählen genauso mit wie Abrufe. Steht in skoda.conf ein eigener
|
||||
* CMD_API_KEY, hat die Steuerung damit ihr eigenes Kontingent und nimmt dem
|
||||
* Abruf des Fahrzeugzustands nichts weg. Beide Schlüssel werden in der
|
||||
* MyŠkoda-App angelegt.
|
||||
* Die API begrenzt auf 20 Anfragen je Stunde, und Befehle zählen genauso mit
|
||||
* wie Abrufe. Die Doku schreibt "je Schlüssel" - gemessen stimmt das nicht,
|
||||
* der Zähler gilt für alle Schlüssel des Kontos zusammen (die Rechnung steht
|
||||
* bei _LIMIT in gatherSkodaData.py). Mehrere Schlüssel bringen also kein
|
||||
* zusätzliches Kontingent.
|
||||
*
|
||||
* Ohne eigenen Schlüssel greift dieses Skript auf API_KEY zurück - dann
|
||||
* teilen sich beide ein Kontingent, und weil sie in getrennten Prozessen
|
||||
* laufen und keinen gemeinsamen Zähler haben, ist es fest aufgeteilt:
|
||||
* Abruf und Steuerung laufen in getrennten Prozessen ohne gemeinsamen Zähler,
|
||||
* deshalb ist fest aufgeteilt:
|
||||
*
|
||||
* eigener CMD_API_KEY nur API_KEY
|
||||
* Abruf 18 / Stunde 14 / Stunde
|
||||
* Befehle 15 / Stunde 4 / Stunde
|
||||
* Reserve 2 / Stunde 2 / Stunde
|
||||
* Abruf 14 / Stunde (gatherSkodaData.py)
|
||||
* Befehle 4 / Stunde
|
||||
* Reserve 2 / Stunde
|
||||
*
|
||||
* gatherSkodaData.py stellt sich selbst um, sobald der zweite Schlüssel in
|
||||
* skoda.conf auftaucht - dort ist nichts von Hand nachzuziehen.
|
||||
*
|
||||
* Der zweite Schlüssel ist kein Freibrief, den Abruf zu beschleunigen: die
|
||||
* Kachelrichtlinie zielt auf die Last, nicht auf die Buchführung.
|
||||
* Ein eigener CMD_API_KEY trennt damit nicht die Kontingente, sondern nur,
|
||||
* wer womit fragt - er lässt sich einzeln widerrufen, ohne den Abruf
|
||||
* mitzunehmen. Welchen dieses Skript nimmt, entscheidet
|
||||
* skodaBefehlsSchluessel(): den eigenen, wenn es ihn gibt, sonst API_KEY.
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
* Nicht angebunden ist die Standheizung. Ihr Start verlangt laut Spec die
|
||||
@@ -46,24 +43,13 @@
|
||||
*/
|
||||
|
||||
require_once("../helper.php");
|
||||
// Wo skoda.conf liegt, wie sie aussieht, welcher Schlüssel die Befehle
|
||||
// schickt und wie viele je Stunde erlaubt sind: alles dort, damit der Reiter
|
||||
// in den Einstellungen und dieser Endpunkt dieselbe Wahrheit benutzen.
|
||||
require_once("../restricted/skodaKeys.php");
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
const API_BASIS = "https://public.api.connect.skoda-auto.cz/api/v1/vehicles/";
|
||||
const KONFIG = "/volume1/homes/wagner/SolarManager/skoda.conf";
|
||||
const KENNUNG = "SmartController-SkodaSteuerung/1.0 (+https://nas.el-wa.org/smart)";
|
||||
|
||||
/** Befehle je Stunde - siehe Kopfkommentar. Hängt davon ab, ob die
|
||||
* Steuerung einen eigenen Schlüssel hat oder ihn mit dem Abruf teilt. */
|
||||
const BEFEHLE_EIGENER_SCHLUESSEL = 15;
|
||||
const BEFEHLE_GETEILT = 4;
|
||||
|
||||
/** Wo der Stundenzähler liegt. Muss für den Webserver beschreibbar sein. */
|
||||
const ZAEHLER_ORTE = [
|
||||
"/volume1/web/smart/tiles", // falls per synoacltool freigegeben
|
||||
"/var/services/tmp/smart-tiles", // sonst
|
||||
];
|
||||
|
||||
/*
|
||||
* Die erlaubten Befehle. Der Schlüssel kommt aus dem Formular, der Pfad
|
||||
* niemals - so kann über den Parameter kein beliebiger Endpunkt der API
|
||||
@@ -93,18 +79,16 @@ if (!isset(BEFEHLE[$name])) {
|
||||
}
|
||||
$befehl = BEFEHLE[$name];
|
||||
|
||||
$konfig = konfigLesen();
|
||||
$konfig = skodaBefehlsSchluessel();
|
||||
if (!$konfig) {
|
||||
antwort(503, false, "Kein API-Schlüssel hinterlegt - skoda.conf fehlt.");
|
||||
antwort(503, false, "Kein API-Schlüssel hinterlegt - unter Einstellungen "
|
||||
. "▸ Fahrzeug lässt sich einer eintragen.");
|
||||
}
|
||||
|
||||
$grenze = $konfig["EIGENER"] ? BEFEHLE_EIGENER_SCHLUESSEL : BEFEHLE_GETEILT;
|
||||
if (!budgetFrei($grenze)) {
|
||||
if (!budgetFrei(SKODA_BEFEHLE)) {
|
||||
antwort(429, false, "Stündliches Kontingent für Befehle erschöpft ("
|
||||
. $grenze . " je Stunde)."
|
||||
. ($konfig["EIGENER"] ? "" : " Ein eigener CMD_API_KEY in skoda.conf"
|
||||
. " würde der Steuerung ein eigenes Kontingent geben.")
|
||||
. " Später erneut versuchen.");
|
||||
. SKODA_BEFEHLE . " je Stunde). Es teilt sich die 20 Anfragen der API"
|
||||
. " mit dem Abruf des Fahrzeugzustands. Später erneut versuchen.");
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- Ausführen */
|
||||
@@ -121,8 +105,9 @@ if ($name === "klima_start") {
|
||||
list($status, $inhalt) = senden($konfig, $befehl["pfad"], $rumpf);
|
||||
|
||||
if ($status === 202) {
|
||||
skodaAbrufAnstossen();
|
||||
antwort(200, true, $befehl["text"] . ". Das Fahrzeug führt den Befehl aus; "
|
||||
. "in der Anzeige erscheint es beim nächsten Abruf.");
|
||||
. "in der Anzeige erscheint es in einer halben Minute.");
|
||||
}
|
||||
antwort(200, false, fehlertext($status, $inhalt));
|
||||
|
||||
@@ -136,39 +121,19 @@ function antwort($code, $ok, $text)
|
||||
exit;
|
||||
}
|
||||
|
||||
/** API-Schlüssel und VIN aus skoda.conf lesen. Format: NAME=wert je Zeile. */
|
||||
function konfigLesen()
|
||||
{
|
||||
if (!is_readable(KONFIG)) {
|
||||
return null;
|
||||
}
|
||||
$werte = [];
|
||||
foreach (file(KONFIG, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $zeile) {
|
||||
$zeile = trim($zeile);
|
||||
if ($zeile === "" || $zeile[0] === "#" || strpos($zeile, "=") === false) {
|
||||
continue;
|
||||
}
|
||||
list($k, $v) = explode("=", $zeile, 2);
|
||||
$werte[strtoupper(trim($k))] = trim(trim($v), "\"'");
|
||||
}
|
||||
if (empty($werte["API_KEY"]) || empty($werte["VIN"])) {
|
||||
return null;
|
||||
}
|
||||
// Eigener Schluessel fuer die Steuerung, wenn hinterlegt - sonst der
|
||||
// gemeinsame. Welcher es wurde, entscheidet unten ueber das Budget.
|
||||
$werte["EIGENER"] = !empty($werte["CMD_API_KEY"]);
|
||||
$werte["SCHLUESSEL"] = $werte["EIGENER"] ? $werte["CMD_API_KEY"] : $werte["API_KEY"];
|
||||
return $werte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stundenzähler für Befehle. Gleiches Verfahren wie die Bremse in tile.php:
|
||||
* eine kleine Datei, gegen gleichzeitige Zugriffe mit flock abgesichert.
|
||||
*
|
||||
* Das Fenster ist fest: es beginnt mit dem ersten Befehl und läuft eine Stunde.
|
||||
* Die API zählt gleitend, hier stehen also im Grenzfall zwei Fenster
|
||||
* nebeneinander - garantiert sind die $grenze Befehle, mehr geht auf das Risiko
|
||||
* eines 429 von der API selbst.
|
||||
*/
|
||||
function budgetFrei($grenze)
|
||||
{
|
||||
$ort = null;
|
||||
foreach (ZAEHLER_ORTE as $kandidat) {
|
||||
foreach (SKODA_ARBEITSORTE as $kandidat) {
|
||||
if (is_dir($kandidat) && is_writable($kandidat)) {
|
||||
$ort = $kandidat;
|
||||
break;
|
||||
@@ -180,7 +145,7 @@ function budgetFrei($grenze)
|
||||
return true;
|
||||
}
|
||||
|
||||
$griff = fopen($ort . "/.befehle.json", "c+");
|
||||
$griff = fopen($ort . "/" . SKODA_ZAEHLER_DATEI, "c+");
|
||||
if (!$griff || !flock($griff, LOCK_EX)) {
|
||||
return true;
|
||||
}
|
||||
@@ -207,16 +172,16 @@ function budgetFrei($grenze)
|
||||
/** Den Befehl an die API schicken. Liefert [HTTP-Status, Rumpf]. */
|
||||
function senden($konfig, $pfad, $rumpf)
|
||||
{
|
||||
$url = API_BASIS . rawurlencode($konfig["VIN"]) . "/" . $pfad;
|
||||
$url = SKODA_API . rawurlencode($konfig["vin"]) . "/" . $pfad;
|
||||
$kopf = [
|
||||
"X-API-Key: " . $konfig["SCHLUESSEL"],
|
||||
"X-API-Key: " . $konfig["schluessel"],
|
||||
"Accept: application/json",
|
||||
];
|
||||
$ch = curl_init($url);
|
||||
$optionen = [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_USERAGENT => KENNUNG,
|
||||
CURLOPT_USERAGENT => SKODA_KENNUNG,
|
||||
CURLOPT_TIMEOUT => 20,
|
||||
CURLOPT_CONNECTTIMEOUT => 5,
|
||||
];
|
||||
@@ -257,7 +222,8 @@ function fehlertext($status, $inhalt)
|
||||
switch ($status) {
|
||||
case 0: return "Die Škoda-API ist nicht erreichbar.";
|
||||
case 400: return "Die API hat den Befehl abgelehnt." . $detail;
|
||||
case 401: return "Der API-Schlüssel ist abgelaufen - in der MyŠkoda-App erneuern.";
|
||||
case 401: return "Der API-Schlüssel ist abgelaufen - in der MyŠkoda-App "
|
||||
. "erneuern und unter Einstellungen ▸ Fahrzeug eintragen.";
|
||||
case 403: return "Der Schlüssel darf diesen Befehl nicht ausführen." . $detail;
|
||||
case 404: return "Fahrzeug nicht gefunden - stimmt die VIN in skoda.conf?";
|
||||
case 422: return "Das Fahrzeug kann das gerade nicht." . $detail;
|
||||
|
||||
Reference in New Issue
Block a user