no message
This commit is contained in:
+47
-23
@@ -17,10 +17,10 @@ class PV_Forecast extends IPSModule
|
|||||||
$this->RegisterPropertyString("RefreshTime", "06:00"); // HH:MM
|
$this->RegisterPropertyString("RefreshTime", "06:00"); // HH:MM
|
||||||
$this->RegisterPropertyBoolean("ActualIsWatt", true);
|
$this->RegisterPropertyBoolean("ActualIsWatt", true);
|
||||||
|
|
||||||
// Timer fires RequestAction(UpdateForecast)
|
// Timer -> ruft RequestAction(UpdateForecast) auf
|
||||||
$this->RegisterTimer("UpdateForecastTimer", 0, 'IPS_RequestAction($_IPS["TARGET"], "UpdateForecast", 0);');
|
$this->RegisterTimer("UpdateForecastTimer", 0, 'IPS_RequestAction($_IPS["TARGET"], "UpdateForecast", 0);');
|
||||||
|
|
||||||
// WebHook endpoint
|
// WebHook Endpoint
|
||||||
$this->RegisterHook("/hook/solcastcompare");
|
$this->RegisterHook("/hook/solcastcompare");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class PV_Forecast extends IPSModule
|
|||||||
$this->SetTimerInterval("UpdateForecastTimer", 60 * 1000);
|
$this->SetTimerInterval("UpdateForecastTimer", 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hook (optional, falls Auto-Register bei dir klappt)
|
// optional: auto-register hook (wenn GUID passt)
|
||||||
$this->RegisterHook("/hook/solcastcompare");
|
$this->RegisterHook("/hook/solcastcompare");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@ class PV_Forecast extends IPSModule
|
|||||||
$this->HandleScheduledUpdate();
|
$this->HandleScheduledUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("Unknown Ident: " . $Ident);
|
throw new Exception("Unknown Ident: " . $Ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,26 +160,38 @@ class PV_Forecast extends IPSModule
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zeitraum: HEUTE (lokal)
|
$tzLocal = new DateTimeZone(date_default_timezone_get());
|
||||||
$tz = new DateTimeZone(date_default_timezone_get());
|
$tzUtc = new DateTimeZone('UTC');
|
||||||
$startDT = new DateTime('today', $tz);
|
|
||||||
$endDT = new DateTime('tomorrow', $tz);
|
|
||||||
|
|
||||||
$start = $startDT->getTimestamp();
|
// Lokaler Tag (für Archiv / Anzeige)
|
||||||
$end = $endDT->getTimestamp();
|
$startLocal = new DateTime('today', $tzLocal);
|
||||||
$now = time();
|
$endLocal = new DateTime('tomorrow', $tzLocal);
|
||||||
|
|
||||||
$forecast = $this->GetForecastSeriesFiltered($start, $end);
|
$startLocalTs = $startLocal->getTimestamp();
|
||||||
$actual = $this->GetActualSeriesFromArchive($start, $end, 1800, $now);
|
$endLocalTs = $endLocal->getTimestamp();
|
||||||
|
|
||||||
|
// Lokaler Tagesbereich als UTC-Grenzen (für Solcast "Z")
|
||||||
|
$startUtcTs = (clone $startLocal)->setTimezone($tzUtc)->getTimestamp();
|
||||||
|
$endUtcTs = (clone $endLocal)->setTimezone($tzUtc)->getTimestamp();
|
||||||
|
|
||||||
|
$nowTs = time();
|
||||||
|
|
||||||
|
// Forecast: komplett für HEUTE (lokaler Tag), korrekt über UTC gefiltert
|
||||||
|
$forecast = $this->GetForecastSeriesFilteredUtc($startUtcTs, $endUtcTs);
|
||||||
|
|
||||||
|
// Actual: komplett HEUTE lokal, aber nach "jetzt" abbrechen (null)
|
||||||
|
$actual = $this->GetActualSeriesFromArchive($startLocalTs, $endLocalTs, 1800, $nowTs);
|
||||||
|
|
||||||
$out = [
|
$out = [
|
||||||
"meta" => [
|
"meta" => [
|
||||||
"forecast_cached_at" => (int)$this->GetBuffer("ForecastTS"),
|
"forecast_cached_at" => (int)$this->GetBuffer("ForecastTS"),
|
||||||
"bucket_seconds" => 1800,
|
"bucket_seconds" => 1800,
|
||||||
"actual_is_watt" => (bool)$this->ReadPropertyBoolean("ActualIsWatt"),
|
"actual_is_watt" => (bool)$this->ReadPropertyBoolean("ActualIsWatt"),
|
||||||
"start" => $start * 1000,
|
"start_local" => $startLocalTs * 1000,
|
||||||
"end" => $end * 1000,
|
"end_local" => $endLocalTs * 1000,
|
||||||
"now" => $now * 1000
|
"start_utc" => $startUtcTs * 1000,
|
||||||
|
"end_utc" => $endUtcTs * 1000,
|
||||||
|
"now" => $nowTs * 1000
|
||||||
],
|
],
|
||||||
"series" => [
|
"series" => [
|
||||||
"forecast" => $forecast,
|
"forecast" => $forecast,
|
||||||
@@ -192,7 +205,7 @@ class PV_Forecast extends IPSModule
|
|||||||
|
|
||||||
// ----------------- Series: Forecast (Solcast) -----------------
|
// ----------------- Series: Forecast (Solcast) -----------------
|
||||||
|
|
||||||
private function GetForecastSeriesFiltered(int $startTs, int $endTs): array
|
private function GetForecastSeriesFilteredUtc(int $startUtcTs, int $endUtcTs): array
|
||||||
{
|
{
|
||||||
$raw = $this->GetBuffer("ForecastRaw");
|
$raw = $this->GetBuffer("ForecastRaw");
|
||||||
if ($raw === "") {
|
if ($raw === "") {
|
||||||
@@ -208,11 +221,12 @@ class PV_Forecast extends IPSModule
|
|||||||
foreach ($data["estimated_actuals"] as $row) {
|
foreach ($data["estimated_actuals"] as $row) {
|
||||||
if (!isset($row["period_end"], $row["pv_power_rooftop"])) continue;
|
if (!isset($row["period_end"], $row["pv_power_rooftop"])) continue;
|
||||||
|
|
||||||
$ts = strtotime($row["period_end"]); // UTC korrekt
|
// period_end ist UTC ("Z")
|
||||||
|
$ts = strtotime($row["period_end"]);
|
||||||
if ($ts === false) continue;
|
if ($ts === false) continue;
|
||||||
|
|
||||||
// nur HEUTE (lokal)
|
// Filter: "heute lokal" als UTC-Grenzen
|
||||||
if ($ts < $startTs || $ts >= $endTs) continue;
|
if ($ts < $startUtcTs || $ts >= $endUtcTs) continue;
|
||||||
|
|
||||||
// Solcast: kW
|
// Solcast: kW
|
||||||
$val = (float)$row["pv_power_rooftop"];
|
$val = (float)$row["pv_power_rooftop"];
|
||||||
@@ -244,10 +258,11 @@ class PV_Forecast extends IPSModule
|
|||||||
|
|
||||||
$logged = AC_GetLoggedValues($archiveId, $varId, $startTs, $endTs, 0);
|
$logged = AC_GetLoggedValues($archiveId, $varId, $startTs, $endTs, 0);
|
||||||
if (!is_array($logged) || count($logged) === 0) {
|
if (!is_array($logged) || count($logged) === 0) {
|
||||||
return [];
|
// trotzdem Raster mit null zurückgeben, damit Linie sauber endet
|
||||||
|
return $this->BuildNullRaster($startTs, $endTs, $bucketSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bucket End -> [sum, count]
|
// bucketEnd -> [sum, count]
|
||||||
$buckets = [];
|
$buckets = [];
|
||||||
foreach ($logged as $row) {
|
foreach ($logged as $row) {
|
||||||
if (!isset($row["TimeStamp"], $row["Value"])) continue;
|
if (!isset($row["TimeStamp"], $row["Value"])) continue;
|
||||||
@@ -266,10 +281,10 @@ class PV_Forecast extends IPSModule
|
|||||||
$series = [];
|
$series = [];
|
||||||
$isWatt = (bool)$this->ReadPropertyBoolean("ActualIsWatt");
|
$isWatt = (bool)$this->ReadPropertyBoolean("ActualIsWatt");
|
||||||
|
|
||||||
// sauberes Raster über den Tag, Zukunft => null
|
// Sauberes Raster über den Tag, nach "jetzt" -> null
|
||||||
for ($t = $startTs + $bucketSeconds; $t <= $endTs; $t += $bucketSeconds) {
|
for ($t = $startTs + $bucketSeconds; $t <= $endTs; $t += $bucketSeconds) {
|
||||||
|
|
||||||
// Zukunft: null (Linie bricht)
|
// Zukunft: null, damit Istlinie nicht parallel bis 24:00 weiterläuft
|
||||||
if ($t > $nowTs + $bucketSeconds) {
|
if ($t > $nowTs + $bucketSeconds) {
|
||||||
$series[] = [$t * 1000, null];
|
$series[] = [$t * 1000, null];
|
||||||
continue;
|
continue;
|
||||||
@@ -293,6 +308,15 @@ class PV_Forecast extends IPSModule
|
|||||||
return $series;
|
return $series;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function BuildNullRaster(int $startTs, int $endTs, int $bucketSeconds): array
|
||||||
|
{
|
||||||
|
$series = [];
|
||||||
|
for ($t = $startTs + $bucketSeconds; $t <= $endTs; $t += $bucketSeconds) {
|
||||||
|
$series[] = [$t * 1000, null];
|
||||||
|
}
|
||||||
|
return $series;
|
||||||
|
}
|
||||||
|
|
||||||
private function GetArchiveInstanceID(): int
|
private function GetArchiveInstanceID(): int
|
||||||
{
|
{
|
||||||
$list = IPS_GetInstanceListByModuleID(self::ARCHIVE_GUID);
|
$list = IPS_GetInstanceListByModuleID(self::ARCHIVE_GUID);
|
||||||
|
|||||||
Reference in New Issue
Block a user