Files
weerscherm/README.md
2026-09-11 15:13:01 +02:00

171 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <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:
```bash
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
```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 <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`):
```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/` 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` (branch `main`);
de role cloont of pullt als Linux-user `weerscherm`.
- `config.php` wordt gerenderd uit `roles/webapp/templates/weerscherm.config.php.j2` met de Netatmo-gegevens
uit de vault (`vault_weerscherm_netatmo_client_id`, `vault_weerscherm_netatmo_client_secret`).
- `storage_path` is `/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/` en `config.php` zijn niet via HTTP bereikbaar.
- `composer install` is op de server niet nodig: `src/autoload.php` laadt de klassen zelf.
- SSL via certbot in dezelfde playbook-run.
Nieuwe versie uitrollen:
```bash
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"):
```bash
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.txt` en `X-Robots-Tag: noindex` houden zoekmachines buiten de deur.
## 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
```