Fujimod
Try your camera firmware in the browser and build Fujimod SD boot files.
Live hyperlocal weather data from a private station in Midtown Manhattan, New York. Updated every 5 minutes.
Translation pending
LifestylePython · AstroGitHub @jtannahillOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Live hyperlocal weather station dashboard for Midtown Manhattan: Astro on Cloudflare Workers, AWS CDK and Lambda, analog and rain-probability models.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Live hyperlocal weather dashboard for Midtown Manhattan, New York. Data from a private Ambient Weather WS-2902 station, updated every 5 minutes.Read the full README ↗Live hyperlocal weather dashboard for Midtown Manhattan, New York. Data from a private Ambient Weather WS-2902 station, updated every 5 minutes.
Live: wx.jamestannahill.com · API: api.wx.jamestannahill.com/current
client:visible): ~90 KB of chart JS only loads when the chart scrolls into viewPublic, read-only, no authentication required.
| Endpoint | Description |
|---|---|
GET /current | Latest reading with all ML signals and metadata |
GET /history?hours=N | Last N hours of readings (max 720), downsampled for longer ranges, with per-slot baselines and std dev |
GET /rain-events?days=N | Parsed rain events from the last N days (default 30) |
GET /daily-summaries?days=N | Pre-computed daily summaries from the last N days (default 30) |
GET /nearby | Latest snapshot of nearby personal weather stations |
GET /health | Health check: data freshness and poller status |
GET /forecast | Standalone analog forecast |
GET /records | Standalone station records |
GET /climate-context | NOAA/ERA5 climate context |
GET /alerts | Active NWS alerts for station point |
GET /export?days=N&format=csv | Bulk export (json or csv) |
GET /openapi.json | OpenAPI 3 specification |
The analog forecast, station records, and daily summary are embedded in the GET /current response (forecast, station_records, daily_summary); there are no standalone endpoints for them.
Ambient Weather WS-2902
│ (Wi-Fi, every 5 min)
▼
Ambient Weather Cloud API
│
▼
wx-poller (Lambda, EventBridge 5-min)
• Fetches reading
• Range + stuck-sensor validation
• NOAA METAR fetch → UHI delta
• Welford rolling stats (mean + variance per MM-HH slot)
• Writes to wx-readings (90-day TTL)
│
├──► wx-forecaster (Lambda, 30-min)
│ • Nearest-neighbor analog forecast on 90-day hourly data
│ • Evaluates previous forecast accuracy → MAE stored
│
├──► wx-ml-fitter (Lambda, weekly Sun 3AM UTC)
│ • Scans 90-day readings, labels rain events
│ • Fits logistic regression w/ class weighting
│ • Stores coefficients to wx-ml-models if F1 ≥ 0.05
│
├──► wx-summarizer (Lambda, daily 5AM UTC)
│ • Processes last 30 days (idempotent)
│ • Computes daily stats + prose summary
│ • Writes to wx-daily-summaries
│
├──► wx-records-tracker (Lambda, weekly Sun 2AM UTC)
│ • Scans 90-day readings
│ • Computes per-month station records
│ • Writes to wx-station-records
│
├──► wx-alerter (Lambda, 15-min)
│ • Evaluates anomaly thresholds
│ • Sends SES email on trigger (2h cooldown)
│
├──► wx-climate-bootstrap (Lambda, one-time)
│ • Backfills 156yr NOAA Central Park daily records (noaa-ghcn-pds S3)
│ • Backfills 85yr ERA5 reanalysis via Open-Meteo
│ • Writes to wx-climate-normals
│
└──► wx-climate-updater (Lambda, daily 6AM UTC)
• Appends yesterday's NOAA + ERA5 observations
• Keeps climate baseline current
wx-api (Lambda, API Gateway HTTP API → CloudFront)
• /current : live reading + all ML signals + climate context + daily summary + records
• /history : downsampled readings with baselines + std dev
• /rain-events: parsed rain events
• /daily-summaries: pre-computed day summaries
• /nearby : latest nearby-stations snapshot
Cloudflare Worker (Astro 6 SSR) → wx.jamestannahill.com (dashboard)
• Hero + 12 conditions cards SSR'd at request time from /current
→ first paint shows real values, AI crawlers see the whole dashboard
• Edge-cached 60s (s-maxage=60, stale-while-revalidate=300)
• /og.png generated on demand at the edge via Satori + resvg-wasm
(workers-og); a real snapshot keyed by ?v= is edge-cached 5 min,
a degraded render (API miss) is served uncached so the next
crawler scrape recovers the current snapshot; no AWS dependency
• Chart shipped as Preact island, hydrates on client:visible
• Partytown moves GA off the main thread; /docs prefetched on hover
API Gateway + CloudFront → api.wx.jamestannahill.com
| Layer | Technology |
|---|---|
| Station | Ambient Weather WS-2902, Wi-Fi gateway |
| Compute | AWS Lambda (Python 3.12, arm64): API + ingest only |
| Scheduling | AWS EventBridge |
| Storage | AWS DynamoDB (on-demand, 12 tables) |
| API | AWS API Gateway HTTP API |
| CDN | AWS CloudFront (API only); Cloudflare Workers (dashboard + OG) |
| Dashboard | Astro 6 server-rendered on Cloudflare Workers, Preact island for the chart, vanilla JS for everything else, uPlot, NHG Display font; design rules in astro/DESIGN.md |
| OG image | Generated at the edge via workers-og (Satori + resvg-wasm), bundled NHG Display TTF |
| IaC | AWS CDK (Python) |
| AWS SES | |
| Secrets | AWS Secrets Manager |
| Climate data | 156yr NOAA GHCN-Daily (Central Park) + 85yr ERA5 reanalysis |
| Forecast | Apple WeatherKit API |
| Active alerts | NOAA National Weather Service API |
| Airport METAR | NOAA aviationweather.gov |
| Table | Key | Purpose |
|---|---|---|
wx-readings | station_id + timestamp | Raw 5-min readings, 90-day TTL |
wx-daily-stats | station_id + month_hour | Rolling mean + variance per MM-HH baseline slot |
wx-alerts | alert_type | Alert debounce state |
wx-forecasts | station_id | Latest pre-computed analog forecast |
wx-forecast-accuracy | station_id + evaluated_at | Per-evaluation MAE + running mean |
wx-uhi-seasonal | station_id + month | Monthly rolling UHI delta averages |
wx-ml-models | model_id | Fitted logistic regression weights + metrics |
wx-daily-summaries | station_id + date | Daily stats + prose summary |
wx-station-records | station_id + month | Per-month extreme records with dates |
wx-climate-doy | mmdd | NOAA GHCN per-day-of-year distributions (156yr Central Park) |
wx-climate-hourly | doy + hour | ERA5 per-DOY-hour distributions (85yr reanalysis) |
Logistic regression: p = σ(w·x + b). Features: humidity (normalized), 1-hour pressure trend, dew-point depression, sin/cos of hour-of-day. Weekly refit via gradient descent (300 epochs, L2 regularization, class weighting n_neg/n_pos). Current model: F1=0.396, recall=82%, n=24,348.
1-hour buckets of the last 90 days → normalize to [0,1] → 6-hour fingerprint → nearest 5 Euclidean neighbors → average their next 3-hour trajectories. Confidence = inverse of mean neighbor distance, normalized to 0 to 100%.
Welford's online algorithm accumulates per-slot variance alongside the rolling mean. Transition to EMA variance at 8,640 samples (30 days). Standard deviation is stored as std_{field} per MM-HH slot and served via /history.
On each /current call, climate_context.py loads the NOAA + ERA5 normals for today's calendar date and computes a daily_verdict for both temp_high and temp_low. Each verdict includes: the observed value, the long-run p5/p50/p95 percentiles in °F, a z-score, and a label (much hotter / warmer / normal / cooler / much colder). The dashboard renders deviation bars that start at the historical average and extend to today's value, with the full p5→max(p95, value) range as the track.
lambdas/
shared/ # DynamoDB client, Secrets Manager, UHI fetch
wx_poller/ # 5-min station data collection
wx_api/ # REST API + all signal computation
wx_alerter/ # Anomaly alert emails
wx_bootstrap/ # One-time ERA5 + WU backfill (readings)
wx_climate_bootstrap/ # One-time NOAA GHCN + ERA5 climate normals backfill
wx_climate_updater/ # Daily NOAA + ERA5 climate update
wx_forecaster/ # Analog forecast + accuracy tracking
wx_ml_fitter/ # Weekly logistic regression training
wx_summarizer/ # Daily prose summaries
wx_records_tracker/ # Weekly station records
astro/
src/pages/
index.astro # SSR dashboard (hero + conditions grid pre-rendered)
docs.astro # Prerendered (export const prerender = true)
og.png.ts # Edge-rendered OG image (workers-og + Satori)
sitemap.xml.ts # Dynamic sitemap (lastmod = render date)
404.astro # Branded 404 page
src/middleware.ts # Security headers (HSTS, nosniff, frame, referrer) on all SSR responses
src/layouts/ # Base.astro: shared head, schema, fonts
src/components/
Chart.tsx # Preact island for uPlot chart (client:visible)
SchemaOrgIndex.astro / SchemaOrgDocs.astro
src/og-fonts/ # NHG Display TTFs bundled into the worker for OG render
public/ # app.js, style.css, llms.txt, _headers, weatherkit.png
astro.config.mjs # cloudflare adapter, partytown, preact, prefetch
cdk/
wx_stack.py # CDK stack (Lambdas, DynamoDB, API Gateway, API CloudFront)
app.py # CDK entry point
scripts/
deploy_worker.sh # Build Astro, patch wrangler.json, wrangler deploy
# Deploy AWS infrastructure (Lambdas, DynamoDB, API Gateway, etc.)
cd cdk && npx cdk deploy --require-approval never
# Deploy dashboard Worker to Cloudflare
# - Builds Astro (SSR for /, prerender for /docs, served from docs.html)
# - Patches the auto-generated wrangler.json (name, vars, custom domain)
# - wrangler deploy → wx.jamestannahill.com
bash scripts/deploy_worker.sh
First-time setup needs npx wrangler login (one-shot OAuth) and a DNS record
for wx.jamestannahill.com that doesn't conflict with the Custom Domain.
Estimated cost: about $4 to $6 per month AWS, Worker on the free tier.