diff --git a/Abrechnung/form.json b/Abrechnung/form.json index c38b831..92235cb 100644 --- a/Abrechnung/form.json +++ b/Abrechnung/form.json @@ -19,121 +19,116 @@ "name": "FooterText", "caption": "Fusszeile" }, - { - - "type": "List", - "name": "Users", - "caption": "Benutzerliste", - "add": true, - "delete": true, - "sortable": true, - "columns": [ - { "caption": "ID", "name": "id", "width": "10%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { "caption": "Name", "name": "name", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { "caption": "Adresse", "name": "address", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { "caption": "Ort", "name": "city", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } } - ] - + "type": "Label", + "caption": "🏦 QR-Einzahlungsschein Daten" }, - { - - "type": "List", - "name": "PowerMeters", - "caption": "Stromzählerliste", - "add": true, - "delete": true, - "sortable": true, - "columns": [ - { "caption": "ID", "name": "id", "width": "10%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { "caption": "Name", "name": "name", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { "caption": "Var. Verbrauch", "name": "var_consumption", "width": "20%", "add": 0, "edit": { "type": "SelectVariable" } }, - { "caption": "Var. Rückspeisung", "name": "var_feed", "width": "20%", "add": 0, "edit": { "type": "SelectVariable" } }, - { "caption": "Benutzer-ID", "name": "user_id", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } } - ] + "type": "ValidationTextBox", + "name": "CreditorName", + "caption": "Rechnungssteller Name" }, - { - - "type": "List", - "name": "WaterMeters", - "caption": "Verbrauchszählerliste", - "add": true, - "delete": true, - "sortable": true, - "columns": [ - { "caption": "ID", "name": "id", "width": "10%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { "caption": "Name", "name": "name", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { "caption": "Var. Verbrauch", "name": "var_consumption", "width": "20%", "add": 0, "edit": { "type": "SelectVariable" } }, - { "caption": "Benutzer-ID", "name": "user_id", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, - { - "caption": "Zählertyp", - "name": "meter_type", - "width": "20%", - "add": "Warmwasser", - "edit": { - "type": "Select", - "options": [ - { "caption": "Warmwasser", "value": "Warmwasser" }, - { "caption": "Kaltwasser", "value": "Kaltwasser" }, - { "caption": "Wärme", "value": "Wärme" } - ] - } - } - + "type": "ValidationTextBox", + "name": "CreditorAddress", + "caption": "Rechnungssteller Strasse/Nr." + }, + { + "type": "ValidationTextBox", + "name": "CreditorCity", + "caption": "Rechnungssteller PLZ/Ort" + }, + { + "type": "ValidationTextBox", + "name": "BankIBAN", + "caption": "Bank IBAN" + }, + { + "type": "List", + "name": "Users", + "caption": "Benutzerliste", + "add": true, + "delete": true, + "sortable": true, + "columns": [ + { "caption": "ID", "name": "id", "width": "10%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { "caption": "Name", "name": "name", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { "caption": "Adresse", "name": "address", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { "caption": "Ort", "name": "city", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } } ] }, - { - - "type": "List", - "name": "Tariffs", - "caption": "Tarifübersicht", - "add": true, - "delete": true, - "sortable": true, - "columns": [ - { - "caption": "Startdatum", - "name": "start", - "width": "20%", - "add": "", - "edit": { "type": "SelectDate" } - }, - { - "caption": "Enddatum", - "name": "end", - "width": "20%", - "add": "", - "edit": { "type": "SelectDate" } - }, - { - "caption": "Tarif (Rp/Einheit)", - "name": "price", - "width": "20%", - "add": 0, - "edit": { "type": "NumberSpinner", "digits": 3, "minimum": 0 } - }, - { - "caption": "Einheit", - "name": "unit_type", - "width": "20%", - "add": "Netztarif", - "edit": { - "type": "Select", - "options": [ - { "caption": "Netztarif", "value": "Netztarif" }, - { "caption": "Einspeisetarif", "value": "Einspeisetarif" }, - { "caption": "Solartarif", "value": "Solartarif" }, - { "caption": "Warmwasser", "value": "Warmwasser" }, - { "caption": "Kaltwasser", "value": "Kaltwasser" }, - { "caption": "Wärme", "value": "Wärme" } - ] - } - } - ] + "type": "List", + "name": "PowerMeters", + "caption": "Stromzählerliste", + "add": true, + "delete": true, + "sortable": true, + "columns": [ + { "caption": "ID", "name": "id", "width": "10%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { "caption": "Name", "name": "name", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { "caption": "Var. Verbrauch", "name": "var_consumption", "width": "20%", "add": 0, "edit": { "type": "SelectVariable" } }, + { "caption": "Var. Rückspeisung", "name": "var_feed", "width": "20%", "add": 0, "edit": { "type": "SelectVariable" } }, + { "caption": "Benutzer-ID", "name": "user_id", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } } + ] + }, + { + "type": "List", + "name": "WaterMeters", + "caption": "Verbrauchszählerliste", + "add": true, + "delete": true, + "sortable": true, + "columns": [ + { "caption": "ID", "name": "id", "width": "10%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { "caption": "Name", "name": "name", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { "caption": "Var. Verbrauch", "name": "var_consumption", "width": "20%", "add": 0, "edit": { "type": "SelectVariable" } }, + { "caption": "Benutzer-ID", "name": "user_id", "width": "20%", "add": "", "edit": { "type": "ValidationTextBox" } }, + { + "caption": "Zählertyp", + "name": "meter_type", + "width": "20%", + "add": "Warmwasser", + "edit": { + "type": "Select", + "options": [ + { "caption": "Warmwasser", "value": "Warmwasser" }, + { "caption": "Kaltwasser", "value": "Kaltwasser" }, + { "caption": "Wärme", "value": "Wärme" } + ] + } } - + ] + }, + { + "type": "List", + "name": "Tariffs", + "caption": "Tarifübersicht", + "add": true, + "delete": true, + "sortable": true, + "columns": [ + { "caption": "Startdatum", "name": "start", "width": "20%", "add": "", "edit": { "type": "SelectDate" } }, + { "caption": "Enddatum", "name": "end", "width": "20%", "add": "", "edit": { "type": "SelectDate" } }, + { "caption": "Tarif (Rp/Einheit)", "name": "price", "width": "20%", "add": 0, "edit": { "type": "NumberSpinner", "digits": 3, "minimum": 0 } }, + { + "caption": "Einheit", + "name": "unit_type", + "width": "20%", + "add": "Netztarif", + "edit": { + "type": "Select", + "options": [ + { "caption": "Netztarif", "value": "Netztarif" }, + { "caption": "Einspeisetarif", "value": "Einspeisetarif" }, + { "caption": "Solartarif", "value": "Solartarif" }, + { "caption": "Warmwasser", "value": "Warmwasser" }, + { "caption": "Kaltwasser", "value": "Kaltwasser" }, + { "caption": "Wärme", "value": "Wärme" } + ] + } + } + ] + } ] -} +} \ No newline at end of file diff --git a/Abrechnung/module.php b/Abrechnung/module.php index d61faf1..447f250 100644 --- a/Abrechnung/module.php +++ b/Abrechnung/module.php @@ -44,6 +44,12 @@ class Abrechnung extends IPSModule $this->RegisterPropertyString('PropertyText', 'Liegenschaft'); $this->RegisterPropertyString('FooterText', 'Belevo AG • 6122 Menznau • www.belevo.ch'); + // Neue Felder für den QR-Schein + $this->RegisterPropertyString('CreditorName', 'Belevo AG'); + $this->RegisterPropertyString('CreditorAddress', 'Musterstrasse 1'); + $this->RegisterPropertyString('CreditorCity', '6122 Menznau'); + $this->RegisterPropertyString('BankIBAN', ''); + $this->RegisterVariableInteger('FromDate', 'Startdatum', '~UnixTimestamp', 1); $this->RegisterVariableInteger('ToDate', 'Enddatum', '~UnixTimestamp', 2); $this->RegisterVariableString('LastResult', 'Letzte Abrechnung', '', 3); @@ -139,7 +145,11 @@ class Abrechnung extends IPSModule foreach ($users as $user) { $pdf->AddPage(); - $this->BuildUserInvoice($pdf, $user, $power, $water, $tariffs, $from, $to); + // Hole das berechnete Total aus der Rechnung + $grandTotal = $this->BuildUserInvoice($pdf, $user, $power, $water, $tariffs, $from, $to); + + // Generiere den Einzahlungsschein, wenn eine IBAN vorhanden ist + $this->BuildQRBill($pdf, $user, $grandTotal); } return $pdf->Output('Abrechnung.pdf', 'S'); @@ -223,6 +233,136 @@ class Abrechnung extends IPSModule "; $pdf->writeHTML($html, true, false, true, false, ''); + + // WICHTIG: Total zurückgeben für den Einzahlungsschein + return $grandTotal; + } + + // ====================== QR-Einzahlungsschein (Schweiz) ====================== + + private function BuildQRBill($pdf, $user, $amount) + { + $iban = str_replace(' ', '', $this->ReadPropertyString('BankIBAN')); + + // Kein Einzahlungsschein, wenn IBAN leer ist oder Betrag <= 0 + if (empty($iban) || $amount <= 0) { + return; + } + + $pdf->AddPage(); + $pdf->SetAutoPageBreak(false); + + // Position des Einzahlungsscheins unten auf A4 (Höhe 105mm) + $yStart = 192; + + // Trennlinien zeichnen + $pdf->SetLineStyle(['dash' => '2,2', 'color' => [0, 0, 0]]); + $pdf->Line(0, $yStart, 210, $yStart); // Horizontal + $pdf->Line(62, $yStart, 62, 297); // Vertikal + $pdf->SetLineStyle(['dash' => 0, 'color' => [0, 0, 0]]); // Reset + + $creditorName = $this->ReadPropertyString('CreditorName'); + $creditorCity = $this->ReadPropertyString('CreditorCity'); + $bankIBAN_formatted = wordwrap($iban, 4, ' ', true); + + // --- LINKER TEIL: Empfangsschein --- + $pdf->SetFont('dejavusans', 'B', 11); + $pdf->SetXY(5, $yStart + 5); + $pdf->Cell(52, 5, 'Empfangsschein', 0, 1); + + $pdf->SetFont('dejavusans', 'B', 6); + $pdf->SetXY(5, $yStart + 12); + $pdf->Cell(52, 3, 'Konto / Zahlbar an', 0, 1); + $pdf->SetFont('dejavusans', '', 6); + $pdf->SetX(5); $pdf->Cell(52, 3, $bankIBAN_formatted, 0, 1); + $pdf->SetX(5); $pdf->Cell(52, 3, $creditorName, 0, 1); + $pdf->SetX(5); $pdf->Cell(52, 3, $creditorCity, 0, 1); + + $pdf->SetFont('dejavusans', 'B', 6); + $pdf->SetXY(5, $yStart + 35); + $pdf->Cell(52, 3, 'Zahlbar durch', 0, 1); + $pdf->SetFont('dejavusans', '', 6); + $pdf->SetX(5); $pdf->Cell(52, 3, $user['name'] ?? '', 0, 1); + $pdf->SetX(5); $pdf->Cell(52, 3, $user['address'] ?? '', 0, 1); + $pdf->SetX(5); $pdf->Cell(52, 3, $user['city'] ?? '', 0, 1); + + $pdf->SetFont('dejavusans', 'B', 6); + $pdf->SetXY(5, $yStart + 60); + $pdf->Cell(15, 3, 'Währung', 0, 0); + $pdf->Cell(20, 3, 'Betrag', 0, 1); + $pdf->SetFont('dejavusans', '', 8); + $pdf->SetX(5); + $pdf->Cell(15, 4, 'CHF', 0, 0); + $pdf->Cell(20, 4, number_format($amount, 2, '.', ''), 0, 1); + + // --- RECHTER TEIL: Zahlteil --- + $pdf->SetFont('dejavusans', 'B', 11); + $pdf->SetXY(118, $yStart + 5); + $pdf->Cell(50, 5, 'Zahlteil', 0, 1); + + // QR Code String generieren & rendern (46x46mm) + $qrString = $this->GenerateQRString($iban, $amount, $user); + $style = ['border' => false, 'padding' => 0, 'fgcolor' => [0, 0, 0], 'bgcolor' => false]; + $pdf->write2DBarcode($qrString, 'QRCODE,M', 67, $yStart + 17, 46, 46, $style, 'N'); + + // Schweizer Kreuz über den QR Code zeichnen (7x7mm Block) + $pdf->SetFillColor(0, 0, 0); + $pdf->Rect(67 + 19.5, $yStart + 17 + 19.5, 7, 7, 'F'); + $pdf->SetFillColor(255, 255, 255); + $pdf->Rect(67 + 19.5 + 3, $yStart + 17 + 19.5 + 1, 1, 5, 'F'); + $pdf->Rect(67 + 19.5 + 1, $yStart + 17 + 19.5 + 3, 5, 1, 'F'); + + $pdf->SetFont('dejavusans', 'B', 8); + $pdf->SetXY(118, $yStart + 17); + $pdf->Cell(80, 4, 'Konto / Zahlbar an', 0, 1); + $pdf->SetFont('dejavusans', '', 8); + $pdf->SetX(118); $pdf->Cell(80, 4, $bankIBAN_formatted, 0, 1); + $pdf->SetX(118); $pdf->Cell(80, 4, $creditorName, 0, 1); + $pdf->SetX(118); $pdf->Cell(80, 4, $creditorCity, 0, 1); + + $pdf->SetFont('dejavusans', 'B', 8); + $pdf->SetXY(118, $yStart + 40); + $pdf->Cell(80, 4, 'Zahlbar durch', 0, 1); + $pdf->SetFont('dejavusans', '', 8); + $pdf->SetX(118); $pdf->Cell(80, 4, $user['name'] ?? '', 0, 1); + $pdf->SetX(118); $pdf->Cell(80, 4, $user['address'] ?? '', 0, 1); + $pdf->SetX(118); $pdf->Cell(80, 4, $user['city'] ?? '', 0, 1); + + $pdf->SetFont('dejavusans', 'B', 8); + $pdf->SetXY(67, $yStart + 68); + $pdf->Cell(15, 4, 'Währung', 0, 0); + $pdf->Cell(20, 4, 'Betrag', 0, 1); + $pdf->SetFont('dejavusans', '', 10); + $pdf->SetX(67); + $pdf->Cell(15, 5, 'CHF', 0, 0); + $pdf->Cell(20, 5, number_format($amount, 2, '.', ''), 0, 1); + } + + private function GenerateQRString($iban, $amount, $user) + { + // Daten nach SIX Swiss Payment Standard beschneiden (max 70 Zeichen pro Zeile) + $creditorName = mb_substr($this->ReadPropertyString('CreditorName'), 0, 70); + $creditorStr = mb_substr($this->ReadPropertyString('CreditorAddress'), 0, 70); + $creditorCity = mb_substr($this->ReadPropertyString('CreditorCity'), 0, 70); + + $debtorName = mb_substr($user['name'] ?? '', 0, 70); + $debtorStr = mb_substr($user['address'] ?? '', 0, 70); + $debtorCity = mb_substr($user['city'] ?? '', 0, 70); + + $amountStr = number_format($amount, 2, '.', ''); + + // EPC / Swiss QR Code String Format + $lines = [ + 'SPC', '0200', '1', $iban, 'K', + $creditorName, $creditorStr, $creditorCity, '', '', 'CH', + '', '', '', '', '', '', '', // Ultimate Creditor (leer) + $amountStr, 'CHF', 'K', + $debtorName, $debtorStr, $debtorCity, '', '', 'CH', + 'NON', '', // Reference Type (NON = Ohne Referenz) + 'EPD', 'Abrechnung Liegenschaft' + ]; + + return implode("\r\n", $lines); } // ====================== Stromkosten (15-Minuten, alle User) ====================== @@ -396,121 +536,121 @@ class Abrechnung extends IPSModule } } -private function GetCalculatedPowerCosts($userId) -{ - $html = " - + /* Neue Formatierungen */ + .num { + text-align: right; + font-family: monospace; + width: 90px; + } + .subtotal-row { + border-top: 1px solid black; + font-weight: bold; + } + .total-row { + border-top: 3px double black; + font-weight: bold; + background-color: #ddd; + } + - - - - - - - - - - - - - - - "; +
IDImport (kWh)Export (kWh)ZEV-Haus (kWh)Netz-Haus (kWh)Solar-Netz (kWh)Solar-ZEV (kWh)Kauf Solar (CHF)Kauf Netz (CHF)Verkauf Netz(CHF)Verkauf ZEV(CHF)Total CHF
+ + + + + + + + + + + + + + "; - if (empty($this->powerCostCache) || !isset($this->powerCostCache[$userId])) { - $html .= "
IDImport (kWh)Export (kWh)ZEV-Haus (kWh)Netz-Haus (kWh)Solar-Netz (kWh)Solar-ZEV (kWh)Kauf Solar (CHF)Kauf Netz (CHF)Verkauf Netz(CHF)Verkauf ZEV(CHF)Total CHF
Keine Stromzähler für diesen Benutzer
"; - return ['html' => $html, 'sum' => 0.0]; + if (empty($this->powerCostCache) || !isset($this->powerCostCache[$userId])) { + $html .= "Keine Stromzähler für diesen Benutzer"; + return ['html' => $html, 'sum' => 0.0]; + } + + $sum = 0.0; + $rowIndex = 0; + + foreach ($this->powerCostCache[$userId] as $name => $a) { + + $subtotal = $a['cost_grid'] + $a['cost_solar'] - ($a['rev_feedin'] + $a['rev_zev']); + $sum += $subtotal; + + $rowClass = ($rowIndex % 2 === 0) ? "row-even" : "row-odd"; + $rowIndex++; + + // Datenzeile + $html .= " + {$a['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($a['rev_zev'], 2, '.', "'") . " + " . number_format($subtotal, 2, '.', "'") . " + "; + + // Leerzeile + $html .= ""; + } + + // Gesamttotal + $html .= " + + Total + + + + + + + + + + + " . number_format($sum, 2, '.', "'") . " + + "; + + return ['html' => $html, 'sum' => $sum]; } - $sum = 0.0; - $rowIndex = 0; - - foreach ($this->powerCostCache[$userId] as $name => $a) { - - $subtotal = $a['cost_grid'] + $a['cost_solar'] - ($a['rev_feedin'] + $a['rev_zev']); - $sum += $subtotal; - - $rowClass = ($rowIndex % 2 === 0) ? "row-even" : "row-odd"; - $rowIndex++; - - // Datenzeile - $html .= " - {$a['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($a['rev_zev'], 2, '.', "'") . " - " . number_format($subtotal, 2, '.', "'") . " - "; - - // Leerzeile - $html .= ""; - } - - // Gesamttotal - $html .= " - - Total - - - - - - - - - - - " . number_format($sum, 2, '.', "'") . " - - "; - - return ['html' => $html, 'sum' => $sum]; -} - // ====================== Nebenkosten Wasser/Wärme ====================== private function CalculateAdditionalCosts($waterMeters, $tariffs, $userId, $from, $to) @@ -638,62 +778,61 @@ private function GetCalculatedPowerCosts($userId) // ====================== Hilfsfunktionen ====================== -private function GetValueAt($varId, $timestamp, $nearestAfter = true) -{ - $archiveID = @IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0]; - if (!$archiveID || !IPS_VariableExists($varId)) { - return null; + private function GetValueAt($varId, $timestamp, $nearestAfter = true) + { + $archiveID = @IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0]; + if (!$archiveID || !IPS_VariableExists($varId)) { + return null; + } + + if ($nearestAfter) { + // Erster Wert NACH oder GENAU ab Timestamp + $values = @AC_GetLoggedValues( + $archiveID, + $varId, + $timestamp, // start + time(), // end + 1 // LIMIT + ); + } else { + // Letzter Wert DAVOR oder GENAU bis Timestamp + $values = @AC_GetLoggedValues( + $archiveID, + $varId, + 0, // start + $timestamp, // end + 1 // LIMIT + ); + } + + if (!empty($values)) { + return (float)$values[0]['Value']; + } + + // Fallback → Live-Wert + return (float)GetValue($varId); } - if ($nearestAfter) { - // Erster Wert NACH oder GENAU ab Timestamp - $values = @AC_GetLoggedValues( - $archiveID, - $varId, - $timestamp, // start - time(), // end - 1 // LIMIT - ); - } else { - // Letzter Wert DAVOR oder GENAU bis Timestamp - $values = @AC_GetLoggedValues( - $archiveID, - $varId, - 0, // start - $timestamp, // end - 1 // LIMIT - ); + private function getDeltaFromArchive(int $varId, int $tStart, int $tEnd): float + { + // Werte holen + $startValue = $this->GetValueAt($varId, $tStart, false); + $endValue = $this->GetValueAt($varId, $tEnd, false); + + if ($startValue === null || $endValue === null) { + return 0.0; + } + + // Delta berechnen + $diff = $endValue - $startValue; + + if ($diff < 0) { + $diff = 0.0; + } + + return (float)$diff; } - if (!empty($values)) { - return (float)$values[0]['Value']; - } - - // Fallback → Live-Wert - return (float)GetValue($varId); -} - - -private function getDeltaFromArchive(int $varId, int $tStart, int $tEnd): float -{ - // Werte holen - $startValue = $this->GetValueAt($varId, $tStart, false); - $endValue = $this->GetValueAt($varId, $tEnd, false); - - if ($startValue === null || $endValue === null) { - return 0.0; - } - - // Delta berechnen - $diff = $endValue - $startValue; - - if ($diff < 0) { - $diff = 0.0; - } - - return (float)$diff; -} - private function toUnixTs($val, $endOfDay = false) { if (is_int($val)) { @@ -724,52 +863,6 @@ private function getDeltaFromArchive(int $varId, int $tStart, int $tEnd): float return null; } - private function readDelta($varId, $tStart, $tEnd) - { - if (!is_int($tStart)) { - $tStart = strtotime($tStart); - } - if (!is_int($tEnd)) { - $tEnd = strtotime($tEnd); - } - - $archiveID = @IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0]; - if (!$archiveID || !IPS_VariableExists($varId)) { - return 0.0; - } - - $values = @AC_GetLoggedValues($archiveID, $varId, $tStart - 86400, $tEnd + 86400, 0); - if (empty($values)) { - return 0.0; - } - - usort($values, fn($a, $b) => (int)$a['TimeStamp'] <=> (int)$b['TimeStamp']); - $vStart = null; - $vEnd = null; - - 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 = (float)GetValue($varId); - } - if ($vEnd === null) { - $vEnd = (float)GetValue($varId); - } - - $diff = $vEnd - $vStart; - return ($diff < 0) ? 0.0 : $diff; - } - private function getTariffPriceAt($tariffs, $typeSynonyms, $ts) { $wanted = array_map('strtolower', $typeSynonyms); @@ -851,4 +944,4 @@ private function getDeltaFromArchive(int $varId, int $tStart, int $tEnd): float return $path; } } -?> +?> \ No newline at end of file