feat: DashboardService with per-source cache and stale fallback, FixtureSource

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-11 14:39:03 +02:00
co-authored by Claude Fable 5.1
parent 89e1e544c5
commit 216b6616fc
6 changed files with 471 additions and 1 deletions
+74
View File
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Weerscherm;
/**
* Vaste voorbeelddata, relatief aan het huidige moment, zodat de frontend zonder Netatmo-koppeling
* gebouwd en bekeken kan worden en altijd "vandaag" laat zien.
*/
final class FixtureSource implements WeatherSource
{
private const RAIN_PER_DAY = [4.3, 0, 3.2, 5.1, 0, 0, 15.5, 1.2, 6.8, 0.0, 2.4, 0.6, 3.9, 0, 1.1];
private readonly \DateTimeImmutable $now;
public function __construct(?\DateTimeImmutable $now = null, private readonly string $timezone = 'Europe/Berlin')
{
$this->now = ($now ?? new \DateTimeImmutable('now'))->setTimezone(new \DateTimeZone($this->timezone));
}
public function stationsData(): array
{
$at = fn(string $time) => (int) $this->now->modify($time)->format('U');
$today = fn(string $hm) => (int) $this->now->setTime((int) substr($hm, 0, 2), (int) substr($hm, 3, 2))->format('U');
return ['devices' => [[
'_id' => '70:ee:50:00:00:01', 'type' => 'NAMain', 'station_name' => 'Thuis', 'module_name' => 'Woonkamer', 'reachable' => true,
'place' => ['timezone' => $this->timezone, 'location' => [6.55, 51.82]],
'dashboard_data' => ['time_utc' => $at('-2 minutes'), 'Temperature' => 21.3, 'min_temp' => 20.4, 'max_temp' => 22.1,
'date_min_temp' => $today('05:50'), 'date_max_temp' => $today('13:40'), 'temp_trend' => 'stable'],
'modules' => [
['_id' => '02:00:00:00:00:01', 'type' => 'NAModule1', 'module_name' => 'Buiten', 'reachable' => true,
'dashboard_data' => ['time_utc' => $at('-3 minutes'), 'Temperature' => 16.4, 'min_temp' => 9.2, 'max_temp' => 19.8,
'date_min_temp' => $today('06:10'), 'date_max_temp' => $today('13:05'), 'temp_trend' => 'down']],
['_id' => '03:00:00:00:00:02', 'type' => 'NAModule4', 'module_name' => 'Slaapkamer', 'reachable' => true,
'dashboard_data' => ['time_utc' => $at('-4 minutes'), 'Temperature' => 18.9, 'min_temp' => 18.1, 'max_temp' => 19.6, 'temp_trend' => 'up']],
['_id' => '03:00:00:00:00:03', 'type' => 'NAModule4', 'module_name' => 'Kantoor', 'reachable' => true,
'dashboard_data' => ['time_utc' => $at('-4 minutes'), 'Temperature' => 20.2, 'min_temp' => 19.0, 'max_temp' => 21.0, 'temp_trend' => 'stable']],
['_id' => '05:00:00:00:00:04', 'type' => 'NAModule3', 'module_name' => 'Regenmeter', 'reachable' => true,
'dashboard_data' => ['time_utc' => $at('-3 minutes'), 'Rain' => 0, 'sum_rain_1' => 0, 'sum_rain_24' => 2.4]],
],
]]];
}
public function measure(string $deviceId, string $moduleId, int $begin, int $end): array
{
$body = [];
$count = count(self::RAIN_PER_DAY);
foreach (self::RAIN_PER_DAY as $i => $mm) {
$day = $this->now->setTime(3, 17)->modify('-' . ($count - 1 - $i) . ' days');
if ($day->getTimestamp() >= $begin) {
$body[(string) $day->getTimestamp()] = [$mm];
}
}
return $body;
}
public function forecast(float $lat, float $lon, string $timezone): array
{
$dates = [];
for ($i = 0; $i < 6; $i++) {
$dates[] = $this->now->modify("+$i days")->format('Y-m-d');
}
return [
'time' => $dates,
'weather_code' => [3, 2, 0, 61, 80, 95],
'temperature_2m_max' => [17.9, 18.3, 20.1, 17.4, 15.2, 16.0],
'temperature_2m_min' => [11.0, 10.1, 11.3, 12.0, 9.4, 8.1],
'precipitation_sum' => [0.0, 0.4, 0.0, 8.2, 3.1, 0.2],
'precipitation_probability_max' => [10, 20, 5, 80, 60, 15],
];
}
}