# 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](docs/weerscherm-spec.md) - Mockup (ontwerp A is leidend): [docs/design/weerscherm-layouts.html](docs/design/weerscherm-layouts.html) - Implementatieplan: [docs/superpowers/plans/2026-09-11-weerscherm.md](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-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 ```bash git clone 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 . 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: ```bash php bin/netatmo-init.php '' ``` 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 ```bash composer test # PHPUnit: mapping, regenberekening, tokenrotatie, cache, stale-fallback npm test # node --test: formattering, WMO-mapping, service worker ``` ## Lokaal draaien Snel, zonder Docker: ```bash php -S 127.0.0.1:8080 -t public ``` Open . 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`): ```bash # 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 ''` 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 ```