Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
- Spec: docs/weerscherm-spec.md
- Mockup (ontwerp A is leidend): docs/design/weerscherm-layouts.html
- Implementatieplan: docs/superpowers/plans/2026-09-11-weerscherm.md
iPad (PWA) ──GET /api/data.php──▶ PHP-backend ──▶ Netatmo API
│ └──▶ Open-Meteo API
└── storage/ (tokens + cache)
Vereisten
- PHP 8.3 met
ext-curlenext-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
- Maak een app aan op https://dev.netatmo.com. Zet
client_idenclient_secretinconfig.php. - Genereer met de token generator op de app-pagina een token met scope
read_station. - 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/ op pensare-server, beheerd met Ansible
(infra-ansible-pensare, app weerscherm in inventories/production/host_vars/pensare-server.yml).
- Code komt uit de publieke Gitea-repo
https://git.pensare.nl/theo/weerscherm.git(branchmain); de role cloont of pullt als Linux-userweerscherm. config.phpwordt gerenderd uitroles/webapp/templates/weerscherm.config.php.j2met de Netatmo-gegevens uit de vault (vault_weerscherm_netatmo_client_id,vault_weerscherm_netatmo_client_secret).storage_pathis/var/www/weerscherm-storage(www-data, 0770), buiten de release-map, zodat de token en de cache deploys overleven.- Document root is
public/;src/,bin/enconfig.phpzijn niet via HTTP bereikbaar. composer installis op de server niet nodig:src/autoload.phplaadt de klassen zelf.- SSL via certbot in dezelfde playbook-run.
Nieuwe versie uitrollen:
git push origin main
cd /Volumes/development/sites/pensare/infra-ansible-pensare
ansible-playbook playbooks/deploy-app.yml -e app=weerscherm
Verhoog bij frontend-wijzigingen VERSION in public/sw.js; de app herlaadt zichzelf bij controllerchange
en in elk geval dagelijks rond 04:00.
Token opnieuw koppelen (melding "Netatmo-koppeling verlopen"):
ssh weerscherm@65.108.40.86 'cd /var/www/weerscherm && sudo -u www-data php8.3 bin/netatmo-init.php "<refresh_token>"'
Lukt sudo -u www-data niet als weerscherm, doe het dan als deploy met sudo. Controleer daarna
https://homedisplay.bahne-drei.de/api/data.php op "errors": [].
- De app staat bewust zonder login.
robots.txtenX-Robots-Tag: noindexhouden zoekmachines buiten de deur.
iPad-instellingen
- Open de URL in Safari, tik op Delen → Zet op beginscherm. Start de app daarna altijd vanaf het beginscherm (fullscreen, zonder Safari-balken).
- Instellingen → Scherm en helderheid → Automatisch vergrendelen → Nooit.
- Optioneel: Instellingen → Toegankelijkheid → Begeleide toegang inschakelen en starten met drie keer op de zijknop, zodat het scherm niet per ongeluk verlaten wordt.
- 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 testennpm testslagen.
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