Files
Symcon_Belevo_Energiemanage…/Energy_Pie/module.php
2025-12-17 07:05:42 +01:00

267 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
class Energy_Pie extends IPSModule
{
private const ATTR_RANGE = 'Range';
private const ATTR_DATE = 'Date';
public function Create(): void
{
parent::Create();
// Source variables (logged counters, kWh)
$this->RegisterPropertyInteger('VarProduction', 0);
$this->RegisterPropertyInteger('VarConsumption', 0);
$this->RegisterPropertyInteger('VarFeedIn', 0);
$this->RegisterPropertyInteger('VarGrid', 0);
$this->RegisterPropertyString('DefaultRange', 'day');
// UI state
$this->RegisterAttributeString(self::ATTR_RANGE, 'day');
$this->RegisterAttributeString(self::ATTR_DATE, date('Y-m-d'));
// Enable HTML-SDK visualization tile
$this->SetVisualizationType(1);
}
public function ApplyChanges(): void
{
parent::ApplyChanges();
// Initialize attributes if needed
$range = $this->ReadAttributeString(self::ATTR_RANGE);
if ($range === '') {
$this->WriteAttributeString(self::ATTR_RANGE, $this->ReadPropertyString('DefaultRange'));
}
$date = $this->ReadAttributeString(self::ATTR_DATE);
if ($date === '' || !$this->isValidDate($date)) {
$this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d'));
}
// Push initial data
$this->RecalculateAndPush();
}
public function GetVisualizationTile(): string
{
$path = __DIR__ . '/module.html';
if (!file_exists($path)) {
return '<div style="padding:12px;font-family:sans-serif;">module.html fehlt</div>';
}
return file_get_contents($path);
}
public function RequestAction($Ident, $Value): void
{
switch ($Ident) {
case 'SetRange':
$range = (string)$Value;
if (!in_array($range, ['day', 'week', 'month', 'total'], true)) {
throw new Exception('Invalid range: ' . $range);
}
$this->WriteAttributeString(self::ATTR_RANGE, $range);
// UX: if switching to week, snap date to Monday (MoSo)
if ($range === 'week') {
$this->snapDateToWeekMonday();
}
// For total, date is irrelevant but we keep it stored
$this->RecalculateAndPush();
break;
case 'SetDate':
$date = (string)$Value;
if (!$this->isValidDate($date)) {
// ignore invalid date instead of crashing the UI
return;
}
$this->WriteAttributeString(self::ATTR_DATE, $date);
// If currently in week mode, snap to Monday immediately
if ($this->ReadAttributeString(self::ATTR_RANGE) === 'week') {
$this->snapDateToWeekMonday();
}
$this->RecalculateAndPush();
break;
case 'Prev':
case 'Next':
case 'Today':
$this->ShiftDate($Ident);
// If week mode, always keep Monday
if ($this->ReadAttributeString(self::ATTR_RANGE) === 'week') {
$this->snapDateToWeekMonday();
}
$this->RecalculateAndPush();
break;
case 'Refresh':
// optional manual refresh (keeps handy for debugging)
$this->RecalculateAndPush();
break;
default:
throw new Exception('Unknown Ident: ' . $Ident);
}
}
private function RecalculateAndPush(): void
{
$range = $this->ReadAttributeString(self::ATTR_RANGE);
$date = $this->ReadAttributeString(self::ATTR_DATE);
[$tStart, $tEnd] = $this->getRange($range, $date);
// --- PLACEHOLDER ---
// Replace later with archive delta calculations:
// $prod = $this->readDelta($this->ReadPropertyInteger('VarProduction'), $tStart, $tEnd);
// $cons = $this->readDelta($this->ReadPropertyInteger('VarConsumption'), $tStart, $tEnd);
// $feed = $this->readDelta($this->ReadPropertyInteger('VarFeedIn'), $tStart, $tEnd);
// $grid = $this->readDelta($this->ReadPropertyInteger('VarGrid'), $tStart, $tEnd);
$prod = 12.3;
$cons = 8.7;
$feed = 3.1;
$grid = 5.6;
$payload = [
'range' => $range,
'date' => $date,
'tStart' => $tStart,
'tEnd' => $tEnd,
'values' => [
'Produktion' => (float)$prod,
'Verbrauch' => (float)$cons,
'Einspeisung' => (float)$feed,
'Netz' => (float)$grid
]
];
$this->UpdateVisualizationValue($payload);
}
private function getRange(string $range, string $dateYmd): array
{
$now = time();
if ($range === 'total') {
// Later: use oldest log timestamp as start
return [0, $now];
}
$base = strtotime($dateYmd . ' 00:00:00');
if ($base === false) {
$base = strtotime(date('Y-m-d') . ' 00:00:00');
}
switch ($range) {
case 'day':
$start = $base;
$end = $start + 86400;
return [$start, $end];
case 'week':
// MoSo: start = Monday 00:00:00, end = next Monday 00:00:00
$dow = (int)date('N', $base); // 1=Mon..7=Sun
$start = $base - (($dow - 1) * 86400);
$end = $start + (7 * 86400);
return [$start, $end];
case 'month':
$start = strtotime(date('Y-m-01 00:00:00', $base));
$end = strtotime('+1 month', $start);
return [$start, $end];
default:
return [$base, $base + 86400];
}
}
private function ShiftDate(string $action): void
{
$range = $this->ReadAttributeString(self::ATTR_RANGE);
if ($range === 'total') {
return;
}
if ($action === 'Today') {
$this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d'));
return;
}
$date = $this->ReadAttributeString(self::ATTR_DATE);
$base = strtotime($date . ' 00:00:00');
if ($base === false) {
$base = strtotime(date('Y-m-d') . ' 00:00:00');
}
$sign = ($action === 'Prev') ? -1 : 1;
switch ($range) {
case 'day':
$base = strtotime(($sign === -1 ? '-1 day' : '+1 day'), $base);
break;
case 'week':
$base = strtotime(($sign === -1 ? '-7 day' : '+7 day'), $base);
break;
case 'month':
$base = strtotime(($sign === -1 ? '-1 month' : '+1 month'), $base);
break;
}
$this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d', $base));
}
private function snapDateToWeekMonday(): void
{
$date = $this->ReadAttributeString(self::ATTR_DATE);
if (!$this->isValidDate($date)) {
$date = date('Y-m-d');
$this->WriteAttributeString(self::ATTR_DATE, $date);
}
$base = strtotime($date . ' 00:00:00');
if ($base === false) {
$base = strtotime(date('Y-m-d') . ' 00:00:00');
}
$dow = (int)date('N', $base); // 1=Mon..7=Sun
$monday = $base - (($dow - 1) * 86400);
$this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d', $monday));
}
private function isValidDate(string $ymd): bool
{
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $ymd)) {
return false;
}
[$y, $m, $d] = array_map('intval', explode('-', $ymd));
return checkdate($m, $d, $y);
}
public function TestPush(): void
{
$this->RecalculateAndPush();
}
}
// Action helper for form.json button
function EP_TestPush(int $id): void
{
IPS_RequestAction($id, 'Refresh', true);
}
?>