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/ 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:

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.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
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%