171 lines
7.7 KiB
Markdown
171 lines
7.7 KiB
Markdown
# 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
|
||
```
|