RainViewer's satellite feed is empty, so the Clouds toggle never drew anything and ground
radar doesn't reach open ocean. Use NASA GIBS GOES-West/East infrared tiles instead,
auto-enabled when flying to a storm, default off otherwise.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Main app
- No backdrop-filter over the live map, animated sky, or sticky headings (it re-blurs every
frame; on iPhone that is a classic stutter/lock-up); top/bottom bars keep a light 8px blur
- Sky effect only draws while the hero is on screen and the Now tab is active, ~30fps cap,
fewer particles on phones; fixed a stale animation-frame id that could stop restarts
Dashboard
- Charts are built only when the Charts tab is opened (was ~11 charts on every page load),
and the 2-minute refresh no longer rebuilds them in the background
- On phones: max 360 points per chart (was 720), no entry animation, device pixel ratio capped at 2
- Lighter header blur, none on small buttons/cards
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 'What is this station?' card: a personal weather station measuring real conditions on site
(not a forecast), how it differs from the Now tab, update cadence, what Live/Offline mean
- Lists the instruments this station actually reports (temp/humidity, wind, rain, pressure,
sun, indoor), using the station's real name
- Open for new visitors, remembers once closed; empty state and Now-tab badge/tab hover explain it too
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The ⌖ button reported 'Location access denied' for every failure, including timeouts and
no-GPS-fix errors that have nothing to do with permission.
- Specific messages for denied / no fix / timeout, with the exact iPhone steps (Safari tab vs
installed web app) and the error code for diagnosis
- One patient high-accuracy retry for slow or cold GPS fixes (a real denial is not retried)
- Live permission status line under the search row (allowed / not asked / blocked)
- Mentions Safari content blockers as a possible cause
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A reverse proxy in front of the app serves stale copies of static files for hours (live app.js and
sw.js were old next to new HTML). iOS also showed a letter tile (N / S) because its separate
icon download failed.
- HTML pages are served through a versioner: every local script/stylesheet/icon/manifest gets a
content-hash query (app.js?v=…), so any change is a URL no proxy or phone has cached
- Service worker URL versioned too; SW offline lookups tolerate the query string; cache v15
- The 180px apple-touch-icon is embedded in each page as a data: URI, so the Home Screen icon
no longer depends on a second network request
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
iOS showed a letter tile ('N') when adding to the Home Screen. The live files were fine, but a
reverse proxy in front of the app serves static files with a long cache lifetime (live icon.svg
and sw.js were stale) and iOS remembers a failed icon per URL.
- New versioned URLs no proxy/phone has cached; old paths kept for existing installs
- apple-touch-icon declared at 180/167/152/120 (opaque 8-bit RGB PNGs) for app and dashboard
- 180px icon also listed in both manifests; notification icons and root fallbacks use v2
- Service worker cache bumped
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- setup/recover/settings pages now declare the dashboard touch icon and 'Station' title
- /apple-touch-icon.png, /apple-touch-icon-precomposed.png and /favicon.ico serve real icons
(iOS falls back to these when a page declares none; they used to 404/204)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Redrawn sun-and-cloud icon in the current palette; artwork sits inside the maskable safe zone
(the old one was clipped by round/squircle masks and used the old warm colours)
- Separate 'any' and 'maskable' manifest entries; opaque iOS touch icons
- Station Dashboard gets its own chart icon so both installed apps are distinguishable
- Vector favicon + PNG fallback; icons verified served by both manifests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Hurricane / tropical storm fixes
- NHC intensity is in knots: show real mph (80 kt = 92 mph) instead of mislabelled 'mph'
- Real Saffir-Simpson category from wind (was always cat 1); subtropical, post-tropical and
potential-cyclone types no longer shown as 'depression'; each storm shows movement
(direction + mph, previously a non-existent field), distance from you, update time, advisory link
- Forecast track points coloured by their own forecast strength (not the current class)
- Map click handlers registered once (popups stacked on every refresh); overlays wait for the
map to settle instead of silently not drawing while tiles load; stale renders superseded
- Subtropical/tropical advice added to alert guidance; escaped storm names
Alert cards (active alerts only)
- What it's for, area, window, time remaining, plain-language meaning, what to do, and a
Full details expander (headline, text, instructions, severity/urgency/certainty, issuer)
- No longer drops alerts beyond 6 per severity group; expired alerts are filtered out
- Shared plain-language alert glossary (alert-info.js, 64 products incl. tropical and surge)
now used by both the app and the dashboard history
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Map fills everything between the top bar and bottom nav; no page scroll on this tab
- Floating round controls (layers, my location, full screen), readout chips top-left,
layers sheet beside the controls, weather summary + radar timeline floating at the bottom
- Safe-area aware in full-screen mode; zoom buttons tucked away on touch devices
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Map fills the screen (was a 56vh strip); timeline and details sit below it
- No longer snaps back to the main-page radar's location/zoom on every refresh or tab switch;
starts as a wider regional view, remembers pan/zoom, and recentres only when the location changes
- New 'My location' button
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Resolves ZIP (default 24550) to the nearest NWS observation station; logs every observation
(normalised imperial values + raw METAR), back-fills ~7 days, snapshots the hourly forecast
every 3h, and logs alerts for that point
- Chart overlay (temp, humidity/dew point, wind, pressure) with an NWS toggle in the History bar
- 'Official NWS observation' card on the Now tab comparing against your station
- Settings: change the reference ZIP and see logging status
- Exact-unit conversions for NWS values (verified against known conversions)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Install banner (phones), Settings guide and dialog with iPhone/iPad and Android steps,
native Install button where supported; steps also on the dashboard Account page
- apple-touch-icon now a PNG (iOS ignores SVG icons)
- Shared pwa-safe.css: dark cap behind the status bar/Dynamic Island in the installed app,
inset-aware offline banner, pull-to-refresh, bottom bars, full-screen map, dialogs
- Landscape-aware content gutters; measured top-bar height; viewport-fit=cover on every page
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Plain-language meaning for common NWS products, shown in the row and expanded view
- Details column: reason, area, severity, effective window; click for full text/instructions
- New alerts log area, severity, urgency, certainty and sender
- Older entries are filled in from NWS by alert id (cached in data/alert-details.json);
alerts NWS no longer serves fall back to the built-in meaning
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Wettest/driest/hottest/coldest/windiest month and wettest/hottest/coldest/windiest year,
each naming the period (e.g. 'September 2026') and flagging partial periods
- Average-based records prefer complete periods; counterparts say what they're waiting for
- 'Records by calendar month (any year)' table with the date/year of each record
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- /api/station/history?range=all returns everything on disk, bucketed (cached 60s)
- Year-aware axis labels for 1y/2y/3y/All
- Pressure tendency widens its change window to the bucket size on long ranges and says so
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- overscroll-behavior moved from <body> to <html>: with html+body both overflow-x:hidden,
body is a scroll container and the rule blocked wheel scrolling from reaching the page
- sunTimes returned the neighbouring day's sunrise/sunset (julian-day rounding), so midday
looked like night; align results to the requested local day
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- One slate/blue palette across the app, dashboard, account/login pages (data colours
stay orange = warm, blue = cool on the dashboard)
- Slim sticky top bar with location chip, gear (settings) and ? (learn); 4-tab nav
(Now, Station, Map, Alerts) with a thumb-reach bottom bar on phones
- Hero sky gradient by condition/time with rain, storm, snow and star effects
(respects reduced motion); keeps the searched city and badges when temps come from your station
- One-line summary, 'Next 2 hours' bars and rain countdown from 15-minute precipitation,
rain/cold-aware 'Bring' chips; yesterday + 'vs usual' context from the station
- Scrubbable hourly chart (temp, rain chance, wind), 7-day range bars, AQI gauge,
details tiles instead of drawers, taller radar with location marker + alert polygons
- Skeleton loading instead of a full-screen spinner, sticky section headers, safe areas
- Server: minutely_15 precipitation, public /api/station/summary
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- New Station tab (top tabs, bottom nav, swipe order) carrying the old /conditions look:
radar-ring hero, condition pill, wind compass, atmosphere/precip/solar cards, indoor
- Scoped sl-* styles and station-live.js; units follow the app's metric/imperial setting
- Empty/error states when no station is connected
- /conditions now 301-redirects to /?tab=station; removed conditions.html,
/api/conditions/public and the old My Station drawer
- /api/ambient-config also returns the station name
- Dashboard and settings links updated; SW cache v8; manifest shortcut for Station
- Drops an indoor-temp block in applyStationOverlay that referenced an out-of-scope helper
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- data/users.json store; existing single account migrates to first admin
- Roles: admin (settings, import, users) and viewer (read-only, own password)
- Live role/disabled lookup; per-user session epoch so password changes,
resets and disabling sign that user out everywhere
- Login brute-force lockout (5 fails -> 5 min), constant-time unknown-user check
- /api/users CRUD with last-admin and self-lockout safeguards
- Account page (own password + admin user management); role-aware header
- Min password length 8; recovery token resets the primary admin and clears lockouts
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Keep per-day extremes in data/records.json (merge-only) and derive all-time,
notable-day, monthly and yearly records from it; rebuild from raw files at startup
- Use station maxdailygust for true gust extremes
- Add /api/station/backup.jsonl, records-backup.json and records/rebuild
- Dashboard: safe-area insets, 44px touch targets, scrollable nav/range bars,
chart touch-action, standalone PWA manifest + apple meta tags
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Move renderHurricaneOverlay call to after track layers so position
circles render on top of the track line and dots
- Add map.loaded() guard to avoid overlay being silently skipped
if the map style hasn't finished initializing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Parse NHC forecast advisory text (FORECAST VALID / OUTLOOK VALID lines)
to extract real forecast positions at 9–117h intervals with wind speeds
- Render forecast track as dashed line + clickable dots (wind on click)
- Auto-fit map bounds to show the full 5-day track after loading
- Fix buildStyle to use OpenFreeMap vector tile URLs without API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace CARTO basemap tiles (now require API key) with OpenFreeMap
vector tiles (positron for light, liberty for dark) — no key needed
- Add click handlers on hurricane panel cards to fly to storm on map
- Add flyToHurricane(): switches to map tab, enables hurricane overlay,
flies to storm coords, shows popup with storm details
- Add /api/hurricanes/track proxy to fetch NHC GIS 5-day forecast track
line and render it as a dashed overlay on the map
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Show a prompt linking to Settings instead of silently hiding the
alerts section when no lat/lon is saved
- Clear the alert cache when location is saved so alerts appear
immediately without waiting for the 5-minute cache to expire
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extracts API keys, alert thresholds, station name/location, data
export/import, and password change into a standalone settings page.
Dashboard gains a Settings nav link; the accordion at the bottom
is removed along with its now-orphaned JS and CSS.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /api/dashboard/alert-history?days=N reads daily .jsonl files from
data/alerts/ and returns entries newest-first. Dashboard gets a lazy-loaded
collapsible panel with a table showing time, type (NWS/threshold badge),
event name, and details, with 7d/30d/90d range selector.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Visual enhancements:
- Sticky header: backdrop-filter blur + box-shadow on scroll
- Live green pulsing dot next to Updated timestamp
- Section cards: gradient border (accent→border→cool via padding-box trick)
- Stat tiles: hover lift (translateY -2px + shadow)
- Skeleton shimmer on all stat tiles until first data arrives
- Value flash animation (fade+slide up) on every data refresh
- Hero temperature: count-up animation when value changes
- Fix set() to preserve trend arrow spans (was being wiped by textContent=)
- Wind rose petals: entrance animation growing in over 650ms
- Chart cards + today bar: hover lift
- Records panel: gold border + ★ badge on records set within last 30 days
- Range/refresh/settings buttons: already had press+glow from prior commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Range buttons, refresh, and settings action buttons now get:
- backdrop-filter: blur(10px) frosted glass base
- cubic-bezier(.2,0,0,1) iOS-style easing
- box-shadow glow ring on hover
- scale(0.95) press effect on :active
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- :active scale(0.95) press feel on all nav buttons
- box-shadow glow ring on hover (accent @ 20% opacity)
- cubic-bezier(.2,0,0,1) iOS-style snap easing on all transitions
- backdrop-filter: blur(10px) frosted glass base on all buttons
- Dashboard → arrow wraps in <span class="arr"> and slides 2px right on hover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
color-mix(accent 18%, transparent) keeps text readable and gives a
ghost-button tint instead of a full solid fill.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace plain text/opacity links with bordered pill buttons that fill
with accent color on hover — consistent across main app, conditions,
and dashboard headers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Main app: split tagline into attribution + Station/Dashboard nav pills (always visible, styled with border/hover)
- Conditions page: add Dashboard → link and ← App back-link in header
- Dashboard: add Conditions link, rename App → ← Weather in header nav
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
drawWindRose used canvas.width (physical pixels) for coordinates while
the context was already pre-scaled by devicePixelRatio. On iPhones
(dpr=2/3) this placed the entire rose off-screen. Fix by dividing by
dpr to get CSS dimensions before computing cx/cy/R.
Also make sWindDir stat always show a value ('—' fallback when winddir
is null) and add null guards on sEventRainTile/sTotalRainTile to
prevent a silent throw from aborting updateLatest() early.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Restore tempinf/humidityin tiles from PWS data on index.html (removed
with thermostat cleanup but still available from Ambient station)
- Dashboard: replace two flat secondary-stat grids with labeled section
cards — Wind, Precipitation, Climate, Indoor — for clearer visual
hierarchy and category separation
- Indoor section card includes Ecobee portal link; appears only when
station reports indoor sensor data
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ecobee API is closed to new developer registrations and iOS sub-daily
automations are no longer available, making the HomeKit push approach
unworkable. Removes all related code:
- server.js: HomeKit push endpoints, token management, data files
- dashboard.html: thermostat section card, indoor stat tiles, indoor
charts, HomeKit settings panel, HVAC badge CSS, all related JS
- index.html: Indoor Temp stat card
- app.js: indoor sensor display logic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add Ecobee thermostat integration to dashboard
- Server: PIN-based OAuth flow to authorize Ecobee access without
exposing redirect URIs; tokens stored server-side and auto-refreshed
- Server: /api/ecobee/config, /api/ecobee/auth/pin,
/api/ecobee/auth/complete, /api/ecobee/data, /api/ecobee/runtime
endpoints behind dashboard auth
- Dashboard: Ecobee section card showing indoor temp, humidity,
heat/cool setpoints, HVAC mode, equipment status badge, and
per-sensor temperature readings from remote sensors
- Dashboard: stacked bar chart of daily heating/cooling runtime hours
for the past 7 days
- Dashboard: Ecobee setup panel with API key entry and step-by-step
PIN authorization flow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMhuFaecxraQzUPN4h9nsB
* Replace ecobee API integration with HomeKit push endpoint
Ecobee removed public developer API access, so pivot to HomeKit:
- Add POST /api/homekit/push accepting thermostat data from iOS Shortcuts
- Add GET /api/homekit/data returning latest reading to dashboard
- Add GET /api/homekit/config and POST /api/homekit/config/regenerate for token management
- Auto-generate auth token on first run, persist to data/homekit-config.json
- Auto-convert Celsius→Fahrenheit if temp < 50 (HomeKit always returns Celsius)
- Update dashboard section card to show temp, setpoints, humidity, mode, HVAC state
- Replace ecobee settings panel with push URL, token copy, and Shortcut setup instructions
- Remove runtime chart (not available without API access)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Move wind direction to its own line below "mph" so it doesn't
wrap or get clipped on narrow stat tiles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hero section with 5.5rem temperature, feels-like, live wind description
- Humidity, pressure, solar radiation, wind sparklines drawn from history data
- Today at a glance bar: today's high/low temp, rain total, peak gust
- Tile color tinting by value: temp (hot/warm/cool/cold), UV severity, wind, rain rate
- Wind cardinal direction shown in hero subtitle and secondary wind tile
- Chart lines now have subtle gradient area fill (non-dashed series only)
- Chart height increased from 200px to 240px; cards get hover shadow
- Section cards with light headers group conditions, totals, and history
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Max Gust Today tile (maxdailygust from Ambient)
- Feels Like In tile (feelsLikein, Ambient-computed)
- Use Ambient's dewPointin directly instead of computing from tempinf/humidityin
- Event Rain tile (eventrainin, shows only when > 0)
- Total Rain lifetime tile (totalrainin)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- NWS active alerts shown as color-coded cards at top of dashboard (5-min cache), auto-refresh every 5 min
- Station location (lat/lon) configurable in settings; falls back to push subscription coordinates
- Consecutive dry/wet streak tile in extended stats (60-day lookback, 0.01" rain threshold)
- Indoor sensor tiles (Temp In, Humidity In, Dew Point In) auto-show when station reports tempinf/humidityin
- Indoor temperature and humidity history charts when indoor data is present in selected range
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>