Weerscherm

Een PWA voor een iPad mini (liggend) in de woonkamer. Toont de actuele gegevens van een Netatmo-weerstation (buiten, drie binnenruimtes, regen) en een verwachting voor vijf dagen van Open-Meteo.

iPad (PWA) ──GET /api/data.php──▶ PHP-backend ──▶ Netatmo API
                                      │      └──▶ Open-Meteo API
                                      └── storage/ (tokens + cache)

Vereisten

  • PHP 8.3 met ext-curl en ext-json. Geen framework, geen runtime-dependencies.
  • Composer alleen voor PHPUnit (dev). Node 20+ alleen voor de JS-tests (dev).
  • Een webserver met public/ als document root. storage/ ligt daarbuiten en moet schrijfbaar zijn voor de webserver.

Installatie

git clone <repo> weerscherm
cd weerscherm
composer install            # alleen nodig voor tests; de app draait ook zonder vendor/
cp config.example.php config.php
mkdir -p storage && chmod 750 storage

Vul config.php in. De sleutels:

Sleutel Betekenis
data_source fixture (voorbeelddata, geen koppeling nodig) of live
netatmo_client_id, netatmo_client_secret uit de app op dev.netatmo.com
netatmo_device_id optioneel, _id van het basisstation; leeg = eerste station
timezone fallback als het station geen tijdzone meldt (standaard Europe/Berlin)
forecast_lat, forecast_lon optioneel; leeg = locatie van het station
indoor_modules optioneel, _id => label in de gewenste volgorde; leeg = Netatmo-namen
cache_ttl seconden per bron: stations 300, measure 900, forecast 3600
storage_path map voor tokens en cache, buiten de docroot

config.php en storage/ staan in .gitignore en komen nooit in git.

Netatmo-koppeling

  1. Maak een app aan op https://dev.netatmo.com. Zet client_id en client_secret in config.php.
  2. Genereer met de token generator op de app-pagina een token met scope read_station.
  3. Sla de refresh token op:
php bin/netatmo-init.php '<refresh_token>'

Dat schrijft storage/netatmo-token.json (mode 600). Bij het eerste verzoek wordt het access token ververst. De refresh token roteert bij elke verversing; de nieuwste wordt automatisch bewaard. Gebruik een oude nooit opnieuw.

Toont het scherm "Netatmo-koppeling verlopen", genereer dan een nieuwe token en voer stap 3 opnieuw uit.

Tests

composer test    # PHPUnit: mapping, regenberekening, tokenrotatie, cache, stale-fallback
npm test         # node --test: formattering, WMO-mapping, service worker

Lokaal draaien

Snel, zonder Docker:

php -S 127.0.0.1:8080 -t public

Open http://127.0.0.1:8080/. Met data_source = fixture is er geen koppeling nodig. Visuele check: een browservenster van exact 1133 × 744 naast de mockup (tab A).

Via de OrbStack shared-Apache-setup (weerscherm.test):

# eenmalig: hostnaam
sudo sh -c 'echo "127.0.0.1 weerscherm.test" >> /etc/hosts'
# shared apache herstarten (vhost en mount staan al in orbstack/shared)
cd /Volumes/development/sites/orbstack/shared && docker-compose up -d --build
# php-container van dit project
cd /Volumes/development/sites/orbstack/divers/weerscherm && docker-compose up -d --build

De bestanden staan in orbstack/divers/weerscherm/ (Dockerfile, compose) en orbstack/shared/apache/vhosts/weerscherm.conf.

Deploy (productie)

Productie-URL: https://homedisplay.bahne-drei.de/

  • Git op de self-hosted Gitea, squash-and-merge naar main, deploy via DeployPad.
  • Document root is public/. src/, storage/, config.php en bin/ zijn dan niet via HTTP bereikbaar.
  • storage/ moet deploys overleven: maak er in DeployPad een gedeelde map van (shared folder) of zet storage_path in config.php naar een map buiten de release-map, bijvoorbeeld /var/www/weerscherm-storage. De map moet schrijfbaar zijn voor de webserver-gebruiker (www-data).
  • config.php is per omgeving en staat niet in git; zet hem ook in de gedeelde map en symlink, of maak hem eenmalig aan in de release-root.
  • composer install is op de server niet nodig: src/autoload.php laadt de klassen zelf als vendor/ ontbreekt.
  • HTTPS is vereist voor de service worker en "Zet op beginscherm".
  • Na de eerste deploy: php bin/netatmo-init.php '<refresh_token>' op de server, en controleer https://homedisplay.bahne-drei.de/api/data.php op "errors": [].
  • De app staat bewust zonder login. Kies een niet-voor-de-hand-liggend (sub)domein; robots.txt en X-Robots-Tag: noindex houden zoekmachines buiten de deur.

Nieuwe versie van de frontend? Verhoog VERSION in public/sw.js; de app herlaadt zichzelf bij controllerchange en in elk geval dagelijks rond 04:00.

iPad-instellingen

  1. Open de URL in Safari, tik op Delen → Zet op beginscherm. Start de app daarna altijd vanaf het beginscherm (fullscreen, zonder Safari-balken).
  2. Instellingen → Scherm en helderheid → Automatisch vergrendelen → Nooit.
  3. Optioneel: Instellingen → Toegankelijkheid → Begeleide toegang inschakelen en starten met drie keer op de zijknop, zodat het scherm niet per ongeluk verlaten wordt.
  4. Houd de iPad liggend; in portretstand verschijnt "Draai de iPad een kwartslag".

Gedrag bij storingen

Situatie Wat het scherm doet
Meting ouder dan 30 minuten of Netatmo tijdelijk onbereikbaar "meting HH:MM · verouderd" in oranje, laatste data blijft staan
Server of wifi onbereikbaar laatste data blijft staan, kopregel toont "geen verbinding sinds HH:MM"; herstelt vanzelf
Refresh token ongeldig (netatmo_auth) melding over de bovenzone met de herstelinstructie
Open-Meteo onbereikbaar laatst gecachte verwachting, forecast_stale: true
Allereerste start zonder data "Gegevens laden…", daarna een concrete foutmelding

Acceptatiecriteria (spec §11)

  • Op de iPad mini, gestart vanaf het beginscherm, draait de app fullscreen en klopt de layout met ontwerp A.
  • Alle waarden komen overeen met de Netatmo-app: buiten, 3× binnen, min/max met tijdstippen, regen vandaag, de afgelopen 3 dagen en het maandtotaal.
  • De verwachting toont 5 dagen vanaf morgen met het juiste icoon per WMO-code.
  • De data ververst zichzelf. Na middernacht staat de nieuwe datum er binnen 5 minuten.
  • Wifi uit: de laatste data blijft staan met "geen verbinding sinds". Wifi aan: herstelt vanzelf.
  • De Netatmo-token ververst meerdere dagen achter elkaar zonder handmatig ingrijpen.
  • Geen secrets in git, in de browser of in responses. storage/ is niet via HTTP bereikbaar.
  • composer test en npm test slagen.

Structuur

public/            document root: index.html, styles.css, app.js, format.js, icons.js, sw.js, manifest, icons/, api/data.php
src/               Config, Http, TokenStore, NetatmoClient, OpenMeteoClient, Cache, mappers, DashboardService
bin/               netatmo-init.php
storage/           tokens en cache (niet in git, niet in docroot)
tests/             PHPUnit + JSON-fixtures; tests/js voor node --test
docs/              spec, mockup, plan
S
Description
Weerscherm: iPad-PWA met Netatmo-stationsdata en Open-Meteo-verwachting
Readme
516 KiB
Languages
PHP 65.1%
JavaScript 25.8%
CSS 6.3%
HTML 2.8%