no message
This commit is contained in:
@@ -15,6 +15,11 @@ Bei 0 A setzt das Gateway das dynamische Stromlimit auf 0. Positive Werte werden
|
|||||||
direkt als neues dynamisches Stromlimit gesendet. Positive Vorgaben unter 6 A
|
direkt als neues dynamisches Stromlimit gesendet. Positive Vorgaben unter 6 A
|
||||||
werden auf den technischen Mindestladestrom von 6 A angehoben.
|
werden auf den technischen Mindestladestrom von 6 A angehoben.
|
||||||
|
|
||||||
|
Die Gültigkeitsdauer des dynamischen Stromlimits wird aus dem Berechnungsintervall
|
||||||
|
der Ladestation abgeleitet. Das Gateway sendet nur bei einer Sollwertänderung,
|
||||||
|
bei fehlender Bestätigung durch Observation 48 oder rechtzeitig vor Ablauf der
|
||||||
|
Gültigkeitsdauer erneut. Die Mindestgültigkeit beträgt zwei Minuten.
|
||||||
|
|
||||||
Der benötigte WebSocket-Client wird nach dem Speichern der Easee-Zugangsdaten
|
Der benötigte WebSocket-Client wird nach dem Speichern der Easee-Zugangsdaten
|
||||||
automatisch erstellt und konfiguriert. Im WebSocket-Client muss keine URL
|
automatisch erstellt und konfiguriert. Im WebSocket-Client muss keine URL
|
||||||
eingetragen werden.
|
eingetragen werden.
|
||||||
|
|||||||
+117
-5
@@ -50,6 +50,7 @@ class EaseeGateway extends IPSModule
|
|||||||
$this->SetBuffer("Subscriptions", "{}");
|
$this->SetBuffer("Subscriptions", "{}");
|
||||||
$this->SetBuffer("SubscribedThisConnection", "{}");
|
$this->SetBuffer("SubscribedThisConnection", "{}");
|
||||||
$this->SetBuffer("PendingInvocations", "{}");
|
$this->SetBuffer("PendingInvocations", "{}");
|
||||||
|
$this->SetBuffer("CurrentCommandStates", "{}");
|
||||||
$this->SetValue("SubscriptionCount", 0);
|
$this->SetValue("SubscriptionCount", 0);
|
||||||
$this->SetConnected(false);
|
$this->SetConnected(false);
|
||||||
|
|
||||||
@@ -210,7 +211,8 @@ class EaseeGateway extends IPSModule
|
|||||||
return json_encode(
|
return json_encode(
|
||||||
$this->SetDynamicChargerCurrent(
|
$this->SetDynamicChargerCurrent(
|
||||||
$serialNumber,
|
$serialNumber,
|
||||||
(float)($request["amps"] ?? -1)
|
(float)($request["amps"] ?? -1),
|
||||||
|
max(1, (int)($request["pollIntervalSeconds"] ?? 5))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -499,7 +501,7 @@ class EaseeGateway extends IPSModule
|
|||||||
if (
|
if (
|
||||||
$serialNumber !== "" &&
|
$serialNumber !== "" &&
|
||||||
$valueExists &&
|
$valueExists &&
|
||||||
($observationID === 109 || $observationID === 120)
|
($observationID === 48 || $observationID === 109 || $observationID === 120)
|
||||||
) {
|
) {
|
||||||
$this->PublishObservation($serialNumber, $observationID, $value);
|
$this->PublishObservation($serialNumber, $observationID, $value);
|
||||||
}
|
}
|
||||||
@@ -543,7 +545,11 @@ class EaseeGateway extends IPSModule
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function SetDynamicChargerCurrent(string $serialNumber, float $amps): array
|
private function SetDynamicChargerCurrent(
|
||||||
|
string $serialNumber,
|
||||||
|
float $amps,
|
||||||
|
int $pollIntervalSeconds
|
||||||
|
): array
|
||||||
{
|
{
|
||||||
if ($serialNumber === "") {
|
if ($serialNumber === "") {
|
||||||
return ["success" => false, "error" => "Seriennummer fehlt"];
|
return ["success" => false, "error" => "Seriennummer fehlt"];
|
||||||
@@ -557,22 +563,128 @@ class EaseeGateway extends IPSModule
|
|||||||
$amps = 6;
|
$amps = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$timing = $this->CalculateCurrentCommandTiming($pollIntervalSeconds);
|
||||||
|
$states = $this->ReadBufferArray("CurrentCommandStates");
|
||||||
|
$state = isset($states[$serialNumber]) && is_array($states[$serialNumber])
|
||||||
|
? $states[$serialNumber]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
$lastAttempt = (int)($state["lastAttempt"] ?? 0);
|
||||||
|
$lastSuccess = (int)($state["lastSuccess"] ?? 0);
|
||||||
|
$desiredChanged =
|
||||||
|
!array_key_exists("amps", $state) ||
|
||||||
|
abs((float)$state["amps"] - $amps) > 0.01 ||
|
||||||
|
(int)($state["validityMinutes"] ?? 0) !== $timing["validityMinutes"];
|
||||||
|
|
||||||
|
$cache = $this->ReadAttributeArray("ObservationCache");
|
||||||
|
$observedAmps = isset($cache[$serialNumber]["48"])
|
||||||
|
? (float)$cache[$serialNumber]["48"]
|
||||||
|
: null;
|
||||||
|
$confirmed =
|
||||||
|
$observedAmps !== null &&
|
||||||
|
abs($observedAmps - $amps) <= 0.1;
|
||||||
|
|
||||||
|
$refreshDue =
|
||||||
|
$lastSuccess <= 0 ||
|
||||||
|
($now - $lastSuccess) >= $timing["refreshAfterSeconds"];
|
||||||
|
$retryDue =
|
||||||
|
!$confirmed &&
|
||||||
|
($lastAttempt <= 0 || ($now - $lastAttempt) >= $timing["retryAfterSeconds"]);
|
||||||
|
|
||||||
|
$states[$serialNumber] = [
|
||||||
|
"amps" => $amps,
|
||||||
|
"validityMinutes" => $timing["validityMinutes"],
|
||||||
|
"refreshAfterSeconds" => $timing["refreshAfterSeconds"],
|
||||||
|
"lastAttempt" => $lastAttempt,
|
||||||
|
"lastSuccess" => $lastSuccess,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!$desiredChanged && !$refreshDue && !$retryDue) {
|
||||||
|
$this->SetBuffer("CurrentCommandStates", json_encode($states));
|
||||||
|
$this->SendDebug(
|
||||||
|
"DynamicCurrent",
|
||||||
|
json_encode([
|
||||||
|
"serialNumber" => $serialNumber,
|
||||||
|
"requestedAmps" => $requestedAmps,
|
||||||
|
"effectiveAmps" => $amps,
|
||||||
|
"observedAmps" => $observedAmps,
|
||||||
|
"confirmed" => $confirmed,
|
||||||
|
"command" => "skipped",
|
||||||
|
"refreshInSeconds" => max(
|
||||||
|
0,
|
||||||
|
$timing["refreshAfterSeconds"] - ($now - $lastSuccess)
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
"success" => true,
|
||||||
|
"skipped" => true,
|
||||||
|
"confirmed" => $confirmed,
|
||||||
|
"observedAmps" => $observedAmps,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$this->SendDebug(
|
$this->SendDebug(
|
||||||
"DynamicCurrent",
|
"DynamicCurrent",
|
||||||
json_encode([
|
json_encode([
|
||||||
"serialNumber" => $serialNumber,
|
"serialNumber" => $serialNumber,
|
||||||
"requestedAmps" => $requestedAmps,
|
"requestedAmps" => $requestedAmps,
|
||||||
"effectiveAmps" => $amps,
|
"effectiveAmps" => $amps,
|
||||||
|
"observedAmps" => $observedAmps,
|
||||||
|
"confirmed" => $confirmed,
|
||||||
|
"validityMinutes" => $timing["validityMinutes"],
|
||||||
|
"refreshAfterSeconds" => $timing["refreshAfterSeconds"],
|
||||||
|
"command" => $desiredChanged
|
||||||
|
? "changed"
|
||||||
|
: ($refreshDue ? "refresh" : "retry"),
|
||||||
]),
|
]),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->AuthorizedApiRequest(
|
$states[$serialNumber]["lastAttempt"] = $now;
|
||||||
|
$result = $this->AuthorizedApiRequest(
|
||||||
"POST",
|
"POST",
|
||||||
"/api/chargers/" . rawurlencode($serialNumber) .
|
"/api/chargers/" . rawurlencode($serialNumber) .
|
||||||
"/commands/set_dynamic_charger_current",
|
"/commands/set_dynamic_charger_current",
|
||||||
json_encode(["amps" => $amps, "minutes" => 0])
|
json_encode([
|
||||||
|
"amps" => (int)round($amps),
|
||||||
|
"minutes" => $timing["validityMinutes"],
|
||||||
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($result["success"] ?? false) {
|
||||||
|
$states[$serialNumber]["lastSuccess"] = $now;
|
||||||
|
}
|
||||||
|
$this->SetBuffer("CurrentCommandStates", json_encode($states));
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function CalculateCurrentCommandTiming(int $pollIntervalSeconds): array
|
||||||
|
{
|
||||||
|
$pollIntervalSeconds = max(1, $pollIntervalSeconds);
|
||||||
|
|
||||||
|
// Mindestens zwei Minuten Gültigkeit. Bei langen Abrufintervallen gilt
|
||||||
|
// der Sollwert mindestens vier Zyklen lang.
|
||||||
|
$validitySeconds = max(120, $pollIntervalSeconds * 4);
|
||||||
|
$validityMinutes = (int)ceil($validitySeconds / 60);
|
||||||
|
$actualValiditySeconds = $validityMinutes * 60;
|
||||||
|
|
||||||
|
// Mindestens 30 Sekunden bzw. zwei Abrufzyklen Sicherheitsabstand.
|
||||||
|
$safetyMarginSeconds = max(30, $pollIntervalSeconds * 2);
|
||||||
|
$refreshAfterSeconds = max(
|
||||||
|
$pollIntervalSeconds,
|
||||||
|
$actualValiditySeconds - $safetyMarginSeconds
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
"validityMinutes" => $validityMinutes,
|
||||||
|
"refreshAfterSeconds" => $refreshAfterSeconds,
|
||||||
|
"retryAfterSeconds" => max(10, min(30, $pollIntervalSeconds * 2)),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function AuthorizedApiRequest(string $method, string $path, string $body): array
|
private function AuthorizedApiRequest(string $method, string $path, string $body): array
|
||||||
|
|||||||
@@ -574,6 +574,7 @@ class Ladestation_v2 extends IPSModule
|
|||||||
"action" => "SetDynamicChargerCurrent",
|
"action" => "SetDynamicChargerCurrent",
|
||||||
"serialNumber" => strtoupper(trim($this->ReadPropertyString("Seriennummer"))),
|
"serialNumber" => strtoupper(trim($this->ReadPropertyString("Seriennummer"))),
|
||||||
"amps" => $value,
|
"amps" => $value,
|
||||||
|
"pollIntervalSeconds" => max(1, $this->ReadPropertyInteger("Interval")),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!is_array($response) || !($response["success"] ?? false)) {
|
if (!is_array($response) || !($response["success"] ?? false)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user