infra_wiki

Weather Hub

A web dashboard on offsite that shows the owner's WeatherFlow Tempest weather stations, on the kiosk displays and on phones.

live

The Weather Hub is a web dashboard for the owner's WeatherFlow Tempest weather stations. A Tempest station reports its readings to WeatherFlow's cloud. The hub app on the offsite Kubernetes cluster reads them from that cloud. The kiosk displays on homepi4 and weatherpi4 show the hub full-screen, and phones install it as a web app.

Use it#

SurfaceAddressWho can reach it
Dashboardhttps://hub.lolwtf.caClients that route to offsite's load-balancer range, with no sign-in
KiosksThe displays of homepi4 and weatherpi4People at the displays
Snapshot APIGET https://hub.lolwtf.ca/api/weatherSame as the dashboard

The dashboard shows each station's current readings against their 24-hour low and high. With two stations, the headline shows the temperature difference between them. A station that BURNSAFE_COUNTIES places in a county also shows that county's fire restriction for the day.

Limits#

  • The dashboard is up to about a minute behind WeatherFlow. The 24-hour lows and highs refresh every 5 minutes.
  • The Restart App item in the refresh menu restarts the server through POST /api/exit, and it needs no sign-in. The server accepts the request only from a LAN, tailnet or loopback address, and a GET or a cross-site request gets an error. The hub has no public tunnel, so a caller on the internet never reaches it.
  • The hub hides the station IDs in TEMPESTWX_IGNORE_STATIONS in the HelmRelease.
  • The fire restriction is up to 10 minutes behind BurnSafe. BurnSafe has no API, so a change to the page's county table hides the restriction until the parser in apps/hub/app/lib/burnsafe.ts follows it.

How it works#

One server process polls the WeatherFlow REST API every 30 seconds. It reads the latest observation of each station that the tokens in TEMPESTWX_TOKENS can read, and caches one snapshot. The traffic to WeatherFlow does not grow with the number of displays. Clients fetch the snapshot from /api/weather every 30 seconds.

A second loop reads the BurnSafe page every 10 minutes. BURNSAFE_COUNTIES in the HelmRelease maps each station ID to a county, and /api/weather carries each mapped station's restriction. When the variable is unset, the hub does not fetch the page.

Each build has a build ID, and the snapshot carries the server's build ID. When the two build IDs differ, the page reloads itself, at most once every 5 minutes. A kiosk shows a new deploy with no restart. The service worker at /sw.js serves the last cached page when the network drops.

Flux deploys the hub with the first-party app Helm chart, which gives it its own Gateway. The hub-env ExternalSecret reads TEMPESTWX_TOKENS from 1Password.

Restart access#

apps/hub/app/lib/exit-guard.ts decides each /api/exit request. It takes the caller from the last X-Forwarded-For entry, which the offsite Gateway appends, so an earlier entry that a caller forges does not count. It allows private, tailnet (100.64.0.0/10) and loopback ranges. Set EXIT_ALLOWED_CIDRS on the HelmRelease to a comma-separated CIDR list to narrow the ranges. A request that carries no forwarded address is refused.

Operate#

No alert rule is specific to the hub. The default kube-prometheus-stack rules on offsite cover its pod. If a kiosk shows an error page or a blank screen, see Repair a kiosk.

Reference#

  • Source: apps/hub/
  • Manifests: clusters/offsite/apps/hub/
  • Chart: packages/charts/app/
  • Kiosk URL: hubUrl in nix/lib/fleet.nix
  • Image: ghcr.io/jonpulsifer/hub

Edit on GitHub

↑↓ move ↵ open esc close