diff --git a/Abrechnung/module.php b/Abrechnung/module.php
index b98f3ee..bbc70fd 100644
--- a/Abrechnung/module.php
+++ b/Abrechnung/module.php
@@ -9,30 +9,24 @@ class Abrechnung extends IPSModule
{
parent::Create();
- // Eigenschaften
$this->RegisterPropertyString('Users', '[]');
$this->RegisterPropertyString('PowerMeters', '[]');
$this->RegisterPropertyString('WaterMeters', '[]');
$this->RegisterPropertyString('Tariffs', '[]');
- // Variablen
$this->RegisterVariableInteger('FromDate', 'Startdatum', '~UnixTimestamp', 1);
$this->RegisterVariableInteger('ToDate', 'Enddatum', '~UnixTimestamp', 2);
$this->RegisterVariableString('LastResult', 'Letzte Abrechnung', '', 3);
$this->EnableAction('FromDate');
$this->EnableAction('ToDate');
- // Abrechnungs-Button
$this->RegisterScript('StartBilling', 'Abrechnung starten', "InstanceID . ", 'StartBilling', ''); ?>");
-
- // 🧾 Media-Objekt für PDF-Ergebnis
$this->RegisterMediaDocument('InvoicePDF', 'Letzte Rechnung', 'pdf');
}
public function ApplyChanges()
{
parent::ApplyChanges();
- IPS_LogMessage('Abrechnung', 'Modul geladen');
}
private function RegisterMediaDocument($Ident, $Name, $Extension, $Position = 0)
@@ -50,8 +44,6 @@ class Abrechnung extends IPSModule
public function RequestAction($Ident, $Value)
{
- IPS_LogMessage('Abrechnung', "RequestAction: $Ident");
-
switch ($Ident) {
case 'FromDate':
case 'ToDate':
@@ -59,7 +51,6 @@ class Abrechnung extends IPSModule
break;
case 'StartBilling':
- IPS_LogMessage('Abrechnung', 'Starte Abrechnung...');
try {
$pdfContent = $this->GenerateInvoices();
if (!$pdfContent) {
@@ -70,10 +61,8 @@ class Abrechnung extends IPSModule
$mediaID = $this->GetIDForIdent('InvoicePDF');
IPS_SetMediaContent($mediaID, base64_encode($pdfContent));
SetValue($this->GetIDForIdent('LastResult'), 'Abrechnung vom ' . date('d.m.Y H:i'));
- IPS_LogMessage('Abrechnung', '✅ Abrechnung erfolgreich erstellt');
echo "✅ PDF erfolgreich erstellt.";
} catch (Throwable $e) {
- IPS_LogMessage('Abrechnung', '💥 Fehler: ' . $e->getMessage());
echo "❌ Ausnahmefehler: " . $e->getMessage();
}
break;
@@ -92,10 +81,7 @@ class Abrechnung extends IPSModule
$water = json_decode($this->ReadPropertyString('WaterMeters'), true);
$tariffs = json_decode($this->ReadPropertyString('Tariffs'), true);
- if (!class_exists('TCPDF')) {
- IPS_LogMessage('Abrechnung', '❌ TCPDF nicht gefunden');
- return false;
- }
+ if (!class_exists('TCPDF')) return false;
$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);
$pdf->SetCreator('IPSymcon Abrechnung');
@@ -137,12 +123,9 @@ class Abrechnung extends IPSModule
$pdf->writeHTML($html, true, false, true, false, '');
}
- // ====================== Stromkosten ======================
-
+ // ====================== Nebenkosten ======================
private function CalculatePowerCosts($powerMeters, $tariffs, $userId, $from, $to)
{
- IPS_LogMessage('Abrechnung', "⚡ Starte Stromkostenberechnung von " . date('d.m.Y H:i', $from) . " bis " . date('d.m.Y H:i', $to));
-
$html = "
| Zähler |
@@ -158,7 +141,6 @@ private function CalculatePowerCosts($powerMeters, $tariffs, $userId, $from, $to
Summe (CHF) |
";
- // Initialisierung
$acc = [];
$meters = [];
@@ -180,213 +162,233 @@ private function CalculatePowerCosts($powerMeters, $tariffs, $userId, $from, $to
}
if (empty($meters)) {
- IPS_LogMessage('Abrechnung', "⚠️ Keine Stromzähler für Benutzer $userId gefunden.");
$html .= "| Keine Stromzähler für diesen Benutzer |
";
return ['html'=>$html, 'sum'=>0.0];
}
// 15-Minuten-Schritte
- for ($ts = $from; $ts < $to; $ts += 900) {
- $slotEnd = min($to, $ts + 900);
+ for ($ts = $from; $ts < $to; $ts += 900) {
+ $slotEnd = min($to, $ts + 900);
- $pGrid = $this->getTariffPriceAt($tariffs, ['Netztarif'], $ts);
- $pSolar = $this->getTariffPriceAt($tariffs, ['Solartarif'], $ts);
- $pFeed = $this->getTariffPriceAt($tariffs, ['Einspeisetarif'], $ts);
+ $pGrid = $this->getTariffPriceAt($tariffs, ['Netztarif'], $ts);
+ $pSolar = $this->getTariffPriceAt($tariffs, ['Solartarif'], $ts);
+ $pFeed = $this->getTariffPriceAt($tariffs, ['Einspeisetarif'], $ts);
- $impTotal = 0.0;
- $expTotal = 0.0;
- $slot = [];
+ $impTotal = 0.0;
+ $expTotal = 0.0;
+ $slot = [];
- foreach ($meters as $name => $mm) {
- $impDelta = $expDelta = 0.0;
+ foreach ($meters as $name => $mm) {
+ $impDelta = $expDelta = 0.0;
- if (!empty($mm['imp']) && IPS_VariableExists((int)$mm['imp'])) {
- $impDelta = $this->readDelta((int)$mm['imp'], $ts, $slotEnd);
- }
- if (!empty($mm['exp']) && IPS_VariableExists((int)$mm['exp'])) {
- $expDelta = $this->readDelta((int)$mm['exp'], $ts, $slotEnd);
- }
+ if (!empty($mm['imp']) && IPS_VariableExists((int)$mm['imp'])) {
+ $impDelta = $this->readDelta((int)$mm['imp'], $ts, $slotEnd);
+ }
+ if (!empty($mm['exp']) && IPS_VariableExists((int)$mm['exp'])) {
+ $expDelta = $this->readDelta((int)$mm['exp'], $ts, $slotEnd);
+ }
- // Nur speichern, wenn wirklich Werte vorhanden
- if ($impDelta > 0 || $expDelta > 0) {
- $slot[$name] = ['imp' => $impDelta, 'exp' => $expDelta];
- $impTotal += $impDelta;
- $expTotal += $expDelta;
-
- IPS_LogMessage('Abrechnung', sprintf("🔹 %s: imp=%.5f, exp=%.5f", $name, $impDelta, $expDelta));
- }
- }
-
- if ($impTotal == 0 && $expTotal == 0) {
- IPS_LogMessage('Abrechnung', "⏭ Kein Verbrauch im Intervall " . date('H:i', $ts) . "–" . date('H:i', $slotEnd));
- continue;
- }
-
- // --- Verhältnis berechnen ---
- if ($impTotal <= $expTotal && $expTotal > 0) {
- $ratio = $impTotal / $expTotal;
- IPS_LogMessage('Abrechnung', sprintf("⚙️ Fall 1: imp<=exp | imp=%.4f exp=%.4f ratio=%.4f", $impTotal, $expTotal, $ratio));
- } elseif ($impTotal > 0) {
- $ratio = $expTotal / $impTotal;
- IPS_LogMessage('Abrechnung', sprintf("⚙️ Fall 2: imp>exp | imp=%.4f exp=%.4f ratio=%.4f", $impTotal, $expTotal, $ratio));
- } else {
- $ratio = 0;
- IPS_LogMessage('Abrechnung', "⚙️ Keine gültige Division (impTotal/expTotal=0)");
- }
-
- // --- Verbrauch berechnen ---
- foreach ($slot as $name => $v) {
- $imp = $v['imp'];
- $exp = $v['exp'];
-
- if ($impTotal <= $expTotal) {
- // Fall 1
- $acc[$name]['solar_bezug'] += $imp;
- $acc[$name]['solareinspeisung']+= (1 - $ratio) * $exp;
- $acc[$name]['solarverkauf'] += $ratio * $exp;
- if ($pSolar !== null) $acc[$name]['cost_solar'] += ($imp * $pSolar) / 100;
- if ($pFeed !== null) $acc[$name]['rev_feedin'] += ((1 - $ratio) * $exp * $pFeed) / 100;
- } else {
- // Fall 2
- $acc[$name]['solar_bezug'] += $ratio * $imp;
- $acc[$name]['netz_bezug'] += (1 - $ratio) * $imp;
- $acc[$name]['solarverkauf'] += $exp;
- if ($pGrid !== null) $acc[$name]['cost_grid'] += ((1 - $ratio) * $imp * $pGrid) / 100;
- if ($pSolar !== null) $acc[$name]['cost_solar'] += ($ratio * $imp * $pSolar) / 100;
- if ($pFeed !== null) $acc[$name]['rev_feedin'] += ($exp * $pFeed) / 100;
- }
-
- IPS_LogMessage('Abrechnung', sprintf(
- "➡️ %s | imp=%.4f exp=%.4f ratio=%.4f grid=%.3f solar=%.3f feed=%.3f",
- $name, $imp, $exp, $ratio, $pGrid ?? 0, $pSolar ?? 0, $pFeed ?? 0
- ));
- }
-}
-}
-
-
-
-
- // ====================== Nebenkosten ======================
-
-private function CalculateAdditionalCosts($waterMeters, $tariffs, $userId, $from, $to)
-{
- $html = "
-
- | Zähler | Typ | Start | Ende |
- Zähler Start | Zähler Ende | Verbrauch | Tarif (Rp) | Kosten (CHF) |
-
";
-
- $total = 0.0;
- $usedTariffs = [];
-
- foreach ($waterMeters as $m) {
- if ($m['user_id'] != $userId) continue;
- $type = $m['meter_type'] ?? 'Warmwasser';
- $cost = $this->AddMeterToPDFRow($m, $tariffs, $from, $to, $type);
- $html .= $cost['row'];
- $total += $cost['value'];
- $usedTariffs = array_merge($usedTariffs, $cost['tariffs']);
- }
-
- $html .= "
- | Total Nebenkosten: |
- " . number_format($total, 2) . " |
-
";
-
- // 👇 Tarifliste anhängen
- if (!empty($usedTariffs)) {
- $html .= "Angewendete Nebenkostentarife:
";
- foreach ($usedTariffs as $t) {
- $html .= "- " . date('d.m.Y', $t['start']) . " – " . date('d.m.Y', $t['end']) . ": " .
- number_format($t['price'], 2) . " Rp
";
- }
- $html .= "
";
- }
-
- return ['html' => $html, 'sum' => $total];
-}
-
- // ====================== Kernberechnung ======================
-
-private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type)
-{
- $rows = '';
- $totalCost = 0.0;
- $usedTariffs = []; // 👈 neu: Liste der verwendeten Tarife
- $varId = $meter['var_consumption'];
-
- if (!IPS_VariableExists($varId)) return ['row' => '', 'value' => 0, 'tariffs' => []];
-
- $filteredTariffs = array_filter($tariffs, fn($t) =>
- strtolower(trim($t['unit_type'] ?? '')) === strtolower(trim($type))
- );
-
- foreach ($filteredTariffs as &$t) {
- $t['start_ts'] = $this->toUnixTs($t['start'], false);
- $t['end_ts'] = $this->toUnixTs($t['end'], true);
- }
- unset($t);
- usort($filteredTariffs, fn($a, $b) => ($a['start_ts'] ?? 0) <=> ($b['start_ts'] ?? 0));
-
- $currentStart = $from;
- while ($currentStart < $to) {
- $activeTariff = null;
- foreach ($filteredTariffs as $t) {
- if (($t['start_ts'] ?? 0) <= $currentStart && $currentStart <= ($t['end_ts'] ?? 0)) {
- $activeTariff = $t;
- break;
+ if ($impDelta > 0 || $expDelta > 0) {
+ $slot[$name] = ['imp' => $impDelta, 'exp' => $expDelta];
+ $impTotal += $impDelta;
+ $expTotal += $expDelta;
}
}
- if (!$activeTariff) {
- $activeTariff = ['start_ts' => $currentStart, 'end_ts' => $to, 'price' => 0.0];
+ // 🧠 --- AB HIER gezieltes Debug-Logging ---
+ IPS_LogMessage('Abrechnung', sprintf(
+ "INTERVALL %s – %s | impTotal=%.5f expTotal=%.5f grid=%.3f solar=%.3f feed=%.3f",
+ date('H:i', $ts), date('H:i', $slotEnd),
+ $impTotal, $expTotal,
+ $pGrid ?? 0, $pSolar ?? 0, $pFeed ?? 0
+ ));
+
+ if ($impTotal == 0 && $expTotal == 0) continue;
+
+ if ($impTotal <= $expTotal && $expTotal > 0) {
+ $ratio = $impTotal / $expTotal;
+ IPS_LogMessage('Abrechnung', sprintf("→ Fall 1 (imp<=exp) ratio=%.4f", $ratio));
+ } elseif ($impTotal > 0) {
+ $ratio = $expTotal / $impTotal;
+ IPS_LogMessage('Abrechnung', sprintf("→ Fall 2 (imp>exp) ratio=%.4f", $ratio));
+ } else {
+ $ratio = 0;
}
- $tariffEnd = intval($activeTariff['end_ts']);
- $tariffPrice = floatval($activeTariff['price']);
+ foreach ($slot as $name => $v) {
+ $imp = $v['imp'];
+ $exp = $v['exp'];
- // 👇 Tarif in Liste aufnehmen (nur wenn noch nicht drin)
- $key = $activeTariff['start_ts'] . '-' . $activeTariff['end_ts'] . '-' . $tariffPrice;
- if (!isset($usedTariffs[$key])) {
- $usedTariffs[$key] = [
- 'start' => $activeTariff['start_ts'],
- 'end' => $activeTariff['end_ts'],
- 'price' => $tariffPrice
- ];
+ if ($impTotal <= $expTotal) {
+ $acc[$name]['solar_bezug'] += $imp;
+ $acc[$name]['solareinspeisung']+= (1 - $ratio) * $exp;
+ $acc[$name]['solarverkauf'] += $ratio * $exp;
+ if ($pSolar !== null) $acc[$name]['cost_solar'] += ($imp * $pSolar) / 100;
+ if ($pFeed !== null) $acc[$name]['rev_feedin'] += ((1 - $ratio) * $exp * $pFeed) / 100;
+ } else {
+ $acc[$name]['solar_bezug'] += $ratio * $imp;
+ $acc[$name]['netz_bezug'] += (1 - $ratio) * $imp;
+ $acc[$name]['solarverkauf'] += $exp;
+ if ($pGrid !== null) $acc[$name]['cost_grid'] += ((1 - $ratio) * $imp * $pGrid) / 100;
+ if ($pSolar !== null) $acc[$name]['cost_solar'] += ($ratio * $imp * $pSolar) / 100;
+ if ($pFeed !== null) $acc[$name]['rev_feedin'] += ($exp * $pFeed) / 100;
+ }
+
+ IPS_LogMessage('Abrechnung', sprintf(
+ " %s | imp=%.4f exp=%.4f ratio=%.4f → solarB=%.4f netzB=%.4f solarE=%.4f solarV=%.4f cGrid=%.4f cSolar=%.4f rFeed=%.4f",
+ $name, $imp, $exp, $ratio,
+ $acc[$name]['solar_bezug'], $acc[$name]['netz_bezug'],
+ $acc[$name]['solareinspeisung'], $acc[$name]['solarverkauf'],
+ $acc[$name]['cost_grid'], $acc[$name]['cost_solar'], $acc[$name]['rev_feedin']
+ ));
}
-
- $startValue = $this->GetValueAt($varId, $currentStart, true);
- $segmentEnd = ($tariffEnd < $to) ? $tariffEnd : $to;
- $endValue = $this->GetValueAt($varId, $segmentEnd, true);
-
- if ($startValue === null || $endValue === null) break;
-
- $verbrauch = max(0, $endValue - $startValue);
- $kosten = round(($tariffPrice / 100) * $verbrauch, 2);
- $totalCost += $kosten;
-
- $rows .= "
- | {$meter['name']} |
- {$type} |
- " . date('d.m.Y', $currentStart) . " |
- " . date('d.m.Y', $segmentEnd) . " |
- " . number_format($startValue, 2) . " |
- " . number_format($endValue, 2) . " |
- " . number_format($verbrauch, 2) . " |
- " . number_format($tariffPrice, 2) . " |
- " . number_format($kosten, 2) . " |
-
";
-
- if ($tariffEnd < $to) $currentStart = $tariffEnd + 1;
- else break;
}
- if ($rows === '') return ['row' => '', 'value' => 0, 'tariffs' => []];
+ // --- Tabelle am Ende aufbauen ---
+ $sum = 0.0;
+ foreach ($acc as $name => $a) {
+ $subtotal = $a['cost_grid'] + $a['cost_solar'] - $a['rev_feedin'];
+ $sum += $subtotal;
- return ['row' => $rows, 'value' => $totalCost, 'tariffs' => array_values($usedTariffs)];
+ $html .= "
+ | {$name} |
+ " . number_format($a['imp'], 3) . " |
+ " . number_format($a['exp'], 3) . " |
+ " . number_format($a['solar_bezug'], 3) . " |
+ " . number_format($a['netz_bezug'], 3) . " |
+ " . number_format($a['solareinspeisung'], 3) . " |
+ " . number_format($a['solarverkauf'], 3) . " |
+ " . number_format($a['cost_solar'], 2) . " |
+ " . number_format($a['cost_grid'], 2) . " |
+ " . number_format($a['rev_feedin'], 2) . " |
+ " . number_format($subtotal, 2) . " |
+
";
+ }
+
+ $html .= "
+ | Total Stromkosten: |
+ " . number_format($sum, 2) . " |
+
";
+
+ return ['html' => $html, 'sum' => $sum];
}
+ private function CalculateAdditionalCosts($waterMeters, $tariffs, $userId, $from, $to)
+ {
+ $html = "
+
+ | Zähler | Typ | Start | Ende |
+ Zähler Start | Zähler Ende | Verbrauch | Tarif (Rp) | Kosten (CHF) |
+
";
+
+ $total = 0.0;
+ $usedTariffs = [];
+
+ foreach ($waterMeters as $m) {
+ if ($m['user_id'] != $userId) continue;
+ $type = $m['meter_type'] ?? 'Warmwasser';
+ $cost = $this->AddMeterToPDFRow($m, $tariffs, $from, $to, $type);
+ $html .= $cost['row'];
+ $total += $cost['value'];
+ $usedTariffs = array_merge($usedTariffs, $cost['tariffs']);
+ }
+
+ $html .= "
+ | Total Nebenkosten: |
+ " . number_format($total, 2) . " |
+
";
+
+ if (!empty($usedTariffs)) {
+ $html .= "Angewendete Nebenkostentarife:
";
+ foreach ($usedTariffs as $t) {
+ $html .= "- " . date('d.m.Y', $t['start']) . " – " . date('d.m.Y', $t['end']) . ": " .
+ number_format($t['price'], 2) . " Rp
";
+ }
+ $html .= "
";
+ }
+
+ return ['html' => $html, 'sum' => $total];
+ }
+
+ // ====================== Kernlogik für Wasser/Wärme ======================
+
+ private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type)
+ {
+ $rows = '';
+ $totalCost = 0.0;
+ $usedTariffs = [];
+ $varId = $meter['var_consumption'];
+
+ if (!IPS_VariableExists($varId)) return ['row' => '', 'value' => 0, 'tariffs' => []];
+
+ $filteredTariffs = array_filter($tariffs, fn($t) =>
+ strtolower(trim($t['unit_type'] ?? '')) === strtolower(trim($type))
+ );
+
+ foreach ($filteredTariffs as &$t) {
+ $t['start_ts'] = $this->toUnixTs($t['start'], false);
+ $t['end_ts'] = $this->toUnixTs($t['end'], true);
+ }
+ unset($t);
+ usort($filteredTariffs, fn($a, $b) => ($a['start_ts'] ?? 0) <=> ($b['start_ts'] ?? 0));
+
+ $currentStart = $from;
+ while ($currentStart < $to) {
+ $activeTariff = null;
+ foreach ($filteredTariffs as $t) {
+ if (($t['start_ts'] ?? 0) <= $currentStart && $currentStart <= ($t['end_ts'] ?? 0)) {
+ $activeTariff = $t;
+ break;
+ }
+ }
+
+ if (!$activeTariff) {
+ $activeTariff = ['start_ts' => $currentStart, 'end_ts' => $to, 'price' => 0.0];
+ }
+
+ $tariffEnd = intval($activeTariff['end_ts']);
+ $tariffPrice = floatval($activeTariff['price']);
+
+ $key = $activeTariff['start_ts'] . '-' . $activeTariff['end_ts'] . '-' . $tariffPrice;
+ if (!isset($usedTariffs[$key])) {
+ $usedTariffs[$key] = [
+ 'start' => $activeTariff['start_ts'],
+ 'end' => $activeTariff['end_ts'],
+ 'price' => $tariffPrice
+ ];
+ }
+
+ $startValue = $this->GetValueAt($varId, $currentStart, true);
+ $segmentEnd = ($tariffEnd < $to) ? $tariffEnd : $to;
+ $endValue = $this->GetValueAt($varId, $segmentEnd, true);
+
+ if ($startValue === null || $endValue === null) break;
+
+ $verbrauch = max(0, $endValue - $startValue);
+ $kosten = round(($tariffPrice / 100) * $verbrauch, 2);
+ $totalCost += $kosten;
+
+ $rows .= "
+ | {$meter['name']} |
+ {$type} |
+ " . date('d.m.Y', $currentStart) . " |
+ " . date('d.m.Y', $segmentEnd) . " |
+ " . number_format($startValue, 2) . " |
+ " . number_format($endValue, 2) . " |
+ " . number_format($verbrauch, 2) . " |
+ " . number_format($tariffPrice, 2) . " |
+ " . number_format($kosten, 2) . " |
+
";
+
+ if ($tariffEnd < $to) $currentStart = $tariffEnd + 1;
+ else break;
+ }
+
+ if ($rows === '') return ['row' => '', 'value' => 0, 'tariffs' => []];
+
+ return ['row' => $rows, 'value' => $totalCost, 'tariffs' => array_values($usedTariffs)];
+ }
+
// ====================== Hilfsfunktionen ======================
private function GetValueAt($varId, $timestamp, $nearestAfter = true)
@@ -430,113 +432,49 @@ private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type)
return null;
}
-private function readDelta($varId, $tStart, $tEnd)
-{
- // Zeitangaben normalisieren
- if (!is_int($tStart)) $tStart = strtotime($tStart);
- if (!is_int($tEnd)) $tEnd = strtotime($tEnd);
+ private function readDelta($varId, $tStart, $tEnd)
+ {
+ if (!is_int($tStart)) $tStart = strtotime($tStart);
+ if (!is_int($tEnd)) $tEnd = strtotime($tEnd);
- // Archiv-Instanz finden
- $archiveID = @IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0];
- if (!$archiveID || !IPS_VariableExists($varId)) {
- IPS_LogMessage('Abrechnung', "❌ Archiv oder Variable $varId nicht gefunden");
- return 0.0;
- }
+ $archiveID = @IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0];
+ if (!$archiveID || !IPS_VariableExists($varId)) return 0.0;
- // Archivdaten abrufen (+/- 1 Tag Puffer)
- $values = @AC_GetLoggedValues($archiveID, $varId, $tStart - 86400, $tEnd + 86400, 0);
- $count = is_array($values) ? count($values) : 0;
- IPS_LogMessage('Abrechnung', sprintf("📘 readDelta(%d): Zeitraum %s – %s | %d Werte gefunden",
- $varId, date('d.m.Y H:i', $tStart), date('H:i', $tEnd), $count
- ));
+ $values = @AC_GetLoggedValues($archiveID, $varId, $tStart - 86400, $tEnd + 86400, 0);
+ if (empty($values)) return 0.0;
- // Wenn gar nichts gefunden → abbrechen
- if ($count === 0) {
- IPS_LogMessage('Abrechnung', "⚠️ Keine Archivwerte für $varId im Zeitraum.");
- return 0.0;
- }
+ usort($values, fn($a, $b) => intval($a['TimeStamp']) <=> intval($b['TimeStamp']));
+ $vStart = null;
+ $vEnd = null;
- // 🔍 Nur die ersten 10 Werte loggen, damit das Log nicht explodiert
- $maxLog = 10;
- $i = 0;
- foreach ($values as $v) {
- IPS_LogMessage('Abrechnung', sprintf(
- " 🕒 %s | %.5f",
- date('d.m.Y H:i:s', intval($v['TimeStamp'])), floatval($v['Value'])
- ));
- $i++;
- if ($i >= $maxLog) {
- IPS_LogMessage('Abrechnung', " ... (+ " . ($count - $i) . " weitere Werte)");
- break;
+ foreach ($values as $v) {
+ if ($v['TimeStamp'] <= $tStart) $vStart = $v['Value'];
+ if ($v['TimeStamp'] <= $tEnd) $vEnd = $v['Value'];
+ if ($v['TimeStamp'] > $tEnd) break;
}
+
+ if ($vStart === null) $vStart = floatval(GetValue($varId));
+ if ($vEnd === null) $vEnd = floatval(GetValue($varId));
+
+ $diff = $vEnd - $vStart;
+ return ($diff < 0) ? 0.0 : $diff;
}
- // 🔹 Sortieren und in Float umwandeln
- usort($values, fn($a, $b) => intval($a['TimeStamp']) <=> intval($b['TimeStamp']));
- foreach ($values as &$v) {
- $v['TimeStamp'] = intval($v['TimeStamp']);
- $v['Value'] = floatval($v['Value']);
- }
- unset($v);
+ private function getTariffPriceAt($tariffs, $typeSynonyms, $ts)
+ {
+ $wanted = array_map('strtolower', $typeSynonyms);
+ $cands = [];
+ foreach ($tariffs as $t) {
+ $u = strtolower(trim($t['unit_type'] ?? ''));
+ if (!in_array($u, $wanted)) continue;
- // 🔹 Letzter Wert <= Start
- $vStart = null;
- foreach ($values as $v) {
- if ($v['TimeStamp'] <= $tStart) $vStart = $v['Value'];
- if ($v['TimeStamp'] > $tStart) break;
- }
-
- // 🔹 Letzter Wert <= Ende
- $vEnd = null;
- foreach ($values as $v) {
- if ($v['TimeStamp'] <= $tEnd) $vEnd = $v['Value'];
- if ($v['TimeStamp'] > $tEnd) break;
- }
-
- // Fallback, falls keine gefunden
- if ($vStart === null) {
- $vStart = floatval(GetValue($varId));
- IPS_LogMessage('Abrechnung', "⚠️ Kein Startwert gefunden – nutze aktuellen Wert $vStart");
- }
- if ($vEnd === null) {
- $vEnd = floatval(GetValue($varId));
- IPS_LogMessage('Abrechnung', "⚠️ Kein Endwert gefunden – nutze aktuellen Wert $vEnd");
- }
-
- $diff = $vEnd - $vStart;
- if ($diff < 0) $diff = 0.0;
-
- IPS_LogMessage('Abrechnung', sprintf(
- "📈 ΔVar %d = %.5f (%.5f → %.5f) [%s → %s]",
- $varId, $diff, $vStart, $vEnd,
- date('d.m.Y H:i', $tStart), date('H:i', $tEnd)
- ));
-
- return $diff;
-}
-
-
-
-private function getTariffPriceAt($tariffs, $typeSynonyms, $ts)
-{
- // passender unit_type
- $wanted = array_map('strtolower', $typeSynonyms);
- $cands = [];
- foreach ($tariffs as $t) {
- $u = strtolower(trim($t['unit_type'] ?? ''));
- if (!in_array($u, $wanted)) continue;
-
- $s = $this->toUnixTs($t['start'], false);
- $e = $this->toUnixTs($t['end'], true);
- if (!$s || !$e) continue;
- if ($s <= $ts && $ts <= $e) {
- $cands[] = floatval($t['price']);
+ $s = $this->toUnixTs($t['start'], false);
+ $e = $this->toUnixTs($t['end'], true);
+ if (!$s || !$e) continue;
+ if ($s <= $ts && $ts <= $e) $cands[] = floatval($t['price']);
}
+ if (empty($cands)) return null;
+ return end($cands);
}
- if (empty($cands)) return null;
- // bei Überschneidungen: ersten / günstigsten / letzten – hier: letzten Eintrag priorisieren
- return end($cands);
-}
-
-
}
+?>