Tik op een dag in de verwachtingsstrook voor een uuroverzicht van
temperatuur, regen en wind (richting + snelheid), om de 2 uur, 12
punten per dag. Sluit na 20 seconden vanzelf of bij een tik op de
overlay zelf.
Backend: Open-Meteo levert hourly-data in dezelfde call als daily
(geen extra verzoek, dezelfde 60min-cache). Nieuwe HourlyForecastMapper
groepeert per kalenderdatum en bemonstert om het andere uur, robuust
voor de zomertijddag (23/25 uur). OpenMeteoClient::dailyForecast()
hernoemd naar forecast(), geeft nu {daily, hourly} terug. Elke dag in
de forecast-array krijgt een hourly-lijst.
Frontend: temperatuurlijn hergebruikt sparklinePath (nu ook als
sparklinePoints voor exacte uitlijning met de 12-koloms grid), regen
als kleine staafjes, windpijl (nieuw icoon) wijst waarheen de wind
waait. 20s-balk via CSS transition met reflow-restart. Servicewerker
naar v5.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
67 lines
2.1 KiB
PHP
67 lines
2.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Weerscherm\Tests;
|
|
|
|
use Weerscherm\WeatherSource;
|
|
|
|
/** Testdubbel: geeft per methode een array terug of gooit de ingestelde exception, en telt de aanroepen. */
|
|
final class FakeSource implements WeatherSource
|
|
{
|
|
/** @var array<string, array|\Throwable> */
|
|
public array $responses = [];
|
|
|
|
/** @var array<string, int> */
|
|
public array $calls = ['stations' => 0, 'measure' => 0, 'pressure' => 0, 'forecast' => 0];
|
|
|
|
/** @var array<string, array> */
|
|
public array $lastArgs = [];
|
|
|
|
public static function fromFixtures(): self
|
|
{
|
|
$s = new self();
|
|
$s->responses = [
|
|
'stations' => json_decode((string) file_get_contents(__DIR__ . '/fixtures/getstationsdata.json'), true)['body'],
|
|
'measure' => json_decode((string) file_get_contents(__DIR__ . '/fixtures/getmeasure.json'), true)['body'],
|
|
'pressure' => json_decode((string) file_get_contents(__DIR__ . '/fixtures/getmeasure-pressure.json'), true)['body'],
|
|
'forecast' => array_intersect_key(
|
|
json_decode((string) file_get_contents(__DIR__ . '/fixtures/forecast.json'), true),
|
|
['daily' => null, 'hourly' => null],
|
|
),
|
|
];
|
|
return $s;
|
|
}
|
|
|
|
public function stationsData(): array
|
|
{
|
|
return $this->answer('stations', []);
|
|
}
|
|
|
|
public function measure(string $deviceId, string $moduleId, int $begin, int $end): array
|
|
{
|
|
return $this->answer('measure', func_get_args());
|
|
}
|
|
|
|
public function pressureHistory(string $deviceId, int $begin, int $end): array
|
|
{
|
|
return $this->answer('pressure', func_get_args());
|
|
}
|
|
|
|
public function forecast(float $lat, float $lon, string $timezone): array
|
|
{
|
|
return $this->answer('forecast', func_get_args());
|
|
}
|
|
|
|
private function answer(string $key, array $args): array
|
|
{
|
|
$this->calls[$key]++;
|
|
$this->lastArgs[$key] = $args;
|
|
$r = $this->responses[$key] ?? new \RuntimeException("geen response voor $key");
|
|
if ($r instanceof \Throwable) {
|
|
throw $r;
|
|
}
|
|
return $r;
|
|
}
|
|
}
|