Files
Enelix-Utils/Energiediagramm/libs/EnergyChartCalculator.php

145 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Belevo\EnelixUtils\Energiediagramm;
use DateTimeImmutable;
use DateTimeZone;
use InvalidArgumentException;
final class EnergyChartCalculator
{
public const RANGES = ['day', 'week', 'month', 'year', 'total', 'custom'];
public static function isValidDate(string $date): bool
{
$value = DateTimeImmutable::createFromFormat('!Y-m-d', $date);
$errors = DateTimeImmutable::getLastErrors();
return $value !== false
&& ($errors === false || ($errors['warning_count'] === 0 && $errors['error_count'] === 0))
&& $value->format('Y-m-d') === $date;
}
/**
* @return array{0: int, 1: int}
*/
public static function rangeBounds(
string $range,
string $date,
string $customStart,
string $customEnd,
?int $now = null
): array {
if (!in_array($range, self::RANGES, true)) {
throw new InvalidArgumentException('Unbekannter Zeitraum.');
}
$now ??= time();
if ($range === 'total') {
return [0, $now];
}
if ($range === 'custom') {
$start = self::date($customStart);
$end = self::date($customEnd);
if ($start > $end) {
throw new InvalidArgumentException('Das Startdatum liegt nach dem Enddatum.');
}
return [$start->getTimestamp(), $end->modify('+1 day')->getTimestamp()];
}
$base = self::date($date);
switch ($range) {
case 'day':
$start = $base;
$end = $start->modify('+1 day');
break;
case 'week':
$start = $base->modify('monday this week');
$end = $start->modify('+1 week');
break;
case 'month':
$start = $base->modify('first day of this month');
$end = $start->modify('first day of next month');
break;
case 'year':
$start = $base->setDate((int) $base->format('Y'), 1, 1);
$end = $start->modify('+1 year');
break;
default:
throw new InvalidArgumentException('Unbekannter Zeitraum.');
}
return [$start->getTimestamp(), $end->getTimestamp()];
}
/**
* @return array{
* Produktion: float,
* Einspeisung: float,
* Netzbezug: float,
* Hausverbrauch: float,
* Eigenverbrauch: float,
* Eigenverbrauchsquote: float,
* Autarkiegrad: float
* }
*/
public static function metrics(
float $production,
float $feedIn,
float $grid,
?float $consumption = null
): array {
$production = max(0.0, $production);
$feedIn = max(0.0, $feedIn);
$grid = max(0.0, $grid);
$house = $consumption === null
? max(0.0, $production - $feedIn + $grid)
: max(0.0, $consumption);
$selfConsumption = min($production, max(0.0, $house - $grid));
return [
'Produktion' => $production,
'Einspeisung' => $feedIn,
'Netzbezug' => $grid,
'Hausverbrauch' => $house,
'Eigenverbrauch' => $selfConsumption,
'Eigenverbrauchsquote' => $production > 0.0
? min(100.0, ($selfConsumption / $production) * 100.0)
: 0.0,
'Autarkiegrad' => $house > 0.0
? min(100.0, ($selfConsumption / $house) * 100.0)
: 0.0,
];
}
public static function normalizeColor(string $color, string $fallback): string
{
$color = trim($color);
if (preg_match('/^#[0-9A-Fa-f]{6}$/', $color) === 1) {
return strtoupper($color);
}
if (preg_match('/^[0-9]+$/', $color) === 1) {
$legacyColor = (int) $color;
if ($legacyColor >= 0 && $legacyColor <= 0xFFFFFF) {
return sprintf('#%06X', $legacyColor);
}
}
return strtoupper($fallback);
}
private static function date(string $date): DateTimeImmutable
{
if (!self::isValidDate($date)) {
throw new InvalidArgumentException('Ungültiges Datum.');
}
return new DateTimeImmutable($date . ' 00:00:00', new DateTimeZone(date_default_timezone_get()));
}
}