diff --git a/Energy_Pie/module.php b/Energy_Pie/module.php index 75d6e1e..0e0a229 100644 --- a/Energy_Pie/module.php +++ b/Energy_Pie/module.php @@ -153,48 +153,96 @@ class Energy_Pie extends IPSModule $this->RecalculateAndPush(); return $this->GetVisualizationTile(); } - private function readDelta(int $varId, int $tStart, int $tEnd, array &$dbg): float - { - $dbg = [ - 'varId' => $varId, - 'archiveId' => 0, - 'count' => 0, - 'first' => null, - 'last' => null, - 'vStart' => null, - 'vEnd' => null - ]; - if ($varId <= 0 || !IPS_VariableExists($varId)) { - return 0.0; - } - $archiveID = IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0] ?? 0; - $dbg['archiveId'] = $archiveID; - if ($archiveID <= 0) { - return 0.0; - } - $values = @AC_GetLoggedValues($archiveID, $varId, $tStart - 86400, $tEnd + 86400, 0); - if (empty($values)) { - return 0.0; - } - usort($values, static fn($a, $b) => (int)$a['TimeStamp'] <=> (int)$b['TimeStamp']); - $dbg['count'] = count($values); - $dbg['first'] = (float)$values[0]['Value']; - $dbg['last'] = (float)$values[count($values) - 1]['Value']; - $vStart = null; - $vEnd = null; - foreach ($values as $v) { - $ts = (int)$v['TimeStamp']; - if ($ts <= $tStart) $vStart = (float)$v['Value']; - if ($ts <= $tEnd) $vEnd = (float)$v['Value']; - if ($ts > $tEnd) break; - } - if ($vStart === null) $vStart = $dbg['first']; - if ($vEnd === null) $vEnd = $dbg['last']; - $dbg['vStart'] = $vStart; - $dbg['vEnd'] = $vEnd; - $diff = $vEnd - $vStart; - return ($diff < 0) ? 0.0 : (float)$diff; +private function readDelta(int $varId, int $tStart, int $tEnd, array &$dbg): float +{ + $dbg = [ + 'varId' => $varId, + 'archiveId' => 0, + 'count' => 0, // hier: wie viele AC-Calls erfolgreich Werte lieferten (nicht Anzahl Rohwerte) + 'vStart' => null, + 'vEnd' => null, + 'tsStart' => null, + 'tsEnd' => null + ]; + + if ($varId <= 0 || !IPS_VariableExists($varId)) { + return 0.0; } + + $archiveID = IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0] ?? 0; + $dbg['archiveId'] = $archiveID; + if ($archiveID <= 0) { + return 0.0; + } + + // Liefert den letzten geloggten Wert <= $toTs. + // Wir starten mit einem kleinen Suchfenster (1 Tag) und vergrößern bei Bedarf. + $getLastUpTo = function (int $toTs) use ($archiveID, $varId): ?array { + $windows = [ + 86400, // 1 Tag + 7 * 86400, // 1 Woche + 31 * 86400, // 1 Monat + 365 * 86400, // 1 Jahr + ]; + + foreach ($windows as $w) { + $from = $toTs - $w; + if ($from < 0) $from = 0; + + // Limit = 1: wir wollen nur einen Wert + $vals = @AC_GetLoggedValues($archiveID, $varId, $from, $toTs, 1); + if (!empty($vals)) { + // In der Praxis liefert Symcon bei Limit=1 den "letzten" im Bereich. + // Wir nehmen trotzdem defensiv den Eintrag mit dem größten TimeStamp. + $best = $vals[0]; + $bestTs = (int)$best['TimeStamp']; + foreach ($vals as $v) { + $ts = (int)$v['TimeStamp']; + if ($ts > $bestTs) { $bestTs = $ts; $best = $v; } + } + return $best; + } + } + + // letzter Versuch: gesamtes Archiv bis toTs (kann bei "total" groß sein, aber passiert nur wenn oben nix gefunden wurde) + $vals = @AC_GetLoggedValues($archiveID, $varId, 0, $toTs, 1); + if (!empty($vals)) { + $best = $vals[0]; + $bestTs = (int)$best['TimeStamp']; + foreach ($vals as $v) { + $ts = (int)$v['TimeStamp']; + if ($ts > $bestTs) { $bestTs = $ts; $best = $v; } + } + return $best; + } + + return null; + }; + + // vStart = letzter Wert <= tStart + $a = $getLastUpTo($tStart); + if ($a !== null) { + $dbg['count']++; + $dbg['vStart'] = (float)$a['Value']; + $dbg['tsStart'] = (int)$a['TimeStamp']; + } else { + return 0.0; + } + + // vEnd = letzter Wert <= tEnd + $b = $getLastUpTo($tEnd); + if ($b !== null) { + $dbg['count']++; + $dbg['vEnd'] = (float)$b['Value']; + $dbg['tsEnd'] = (int)$b['TimeStamp']; + } else { + return 0.0; + } + + $diff = (float)$dbg['vEnd'] - (float)$dbg['vStart']; + return ($diff < 0) ? 0.0 : $diff; +} + private function getLastLogTimestamp(int $varId): int { if ($varId <= 0 || !IPS_VariableExists($varId)) {