Station tab: explain what the station is
Publish Container / publish (push) Successful in 2m13s

- '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>
This commit is contained in:
Nate EmmertandClaude Sonnet 5.5 committed 2026-10-15 15:16:33 -04:00
1 parent eaff2f55c8
commit dde59e3865
3 files changed
+58 -3

No files matched your search

+2 -2
View File
@@ -35,7 +35,7 @@
<a class="brand" href="/" aria-label="NeCloud Weather home"><span class="brand-mark">☁︎</span><span class="brand-name">NeCloud</span></a>
<nav class="tabs" aria-label="Sections">
<button class="tab active" data-tab="conditions">Now</button>
<button class="tab" data-tab="station">Station</button>
<button class="tab" data-tab="station" title="Live readings from a personal weather station">Station</button>
<button class="tab" data-tab="unified-map">Map</button>
<button class="tab" data-tab="alerts">Alerts<span class="alert-badge hidden" id="alert-badge-nav"></span></button>
</nav>
@@ -86,7 +86,7 @@
<div id="cur-icon" class="hero-icon"></div>
<div id="cur-desc" class="hero-desc"></div>
<div id="cur-precip" class="hero-sub"></div>
<div id="station-badge" class="station-badge hidden">📡 <span id="station-badge-name">My Station</span></div>
<div id="station-badge" class="station-badge hidden" title="These readings come from a personal weather station on site, not the forecast model. See the Station tab.">📡 <span id="station-badge-name">My Station</span></div>
</div>
<div class="hero-center">
<div class="hero-temp"><span id="cur-temp">--</span><span class="deg">°F</span></div>
+15
View File
@@ -75,3 +75,18 @@
.sl-empty button { margin-top: 1rem; font: inherit; font-weight: 600; padding: .6rem 1.2rem; min-height: 44px; border-radius: 10px; border: 1px solid var(--sl-accent); background: transparent; color: var(--sl-accent); cursor: pointer; }
.sl-link { display: inline-flex; align-items: center; gap: .3rem; font-size: .78rem; font-weight: 600; color: var(--sl-muted); text-decoration: none; padding: .32rem .75rem; border: 1px solid var(--sl-border); border-radius: 8px; }
.sl-link:hover { color: var(--sl-accent); border-color: var(--sl-accent); }
/* What is this station? */
.sl-about { background: var(--sl-surface); border: 1px solid var(--sl-border); border-radius: 14px; margin-bottom: 0.9rem; overflow: hidden; }
.sl-about summary { display: flex; align-items: center; gap: 0.6rem; min-height: 48px; padding: 0 1rem; cursor: pointer; list-style: none; font-size: 0.9rem; font-weight: 600; color: var(--sl-text); }
.sl-about summary::-webkit-details-marker { display: none; }
.sl-about-ico { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; background: color-mix(in srgb, var(--sl-accent) 18%, transparent); color: var(--sl-accent); font-size: 0.95rem; }
.sl-about-chev { margin-left: auto; color: var(--sl-muted); font-size: 1.2rem; transition: transform .2s; }
.sl-about[open] .sl-about-chev { transform: rotate(90deg); }
.sl-about-body { padding: 0 1rem 1rem; font-size: 0.88rem; line-height: 1.55; color: var(--sl-text); }
.sl-about-body p { margin: 0 0 0.6rem; }
.sl-about-h { font-size: 0.62rem; font-weight: 700; letter-spacing: .11em; text-transform: uppercase; color: var(--sl-muted); margin: 0.9rem 0 0.35rem !important; }
.sl-sensors { margin: 0 0 0.3rem; padding: 0; list-style: none; display: grid; gap: 0.4rem; }
.sl-sensors li { padding-left: 0; color: var(--sl-text); }
.sl-about-foot { margin-top: 0.8rem !important; padding-top: 0.7rem; border-top: 1px solid var(--sl-border); font-size: 0.8rem; color: var(--sl-muted); }
.sl-about-foot a { color: var(--sl-accent); font-weight: 600; }
+41 -1
View File
@@ -52,6 +52,22 @@ const SHELL = `
<span class="sl-live" id="sl-live"><span class="sl-dot"></span><span id="sl-live-text">Live</span></span>
<span><span class="sl-time" id="sl-time"></span></span>
</div>
<details class="sl-about" id="sl-about">
<summary><span class="sl-about-ico">ⓘ</span><span>What is this station?</span><span class="sl-about-chev">›</span></summary>
<div class="sl-about-body">
<p><b id="sl-about-name">This station</b> is a <b>personal weather station</b>: real instruments on the owner's property that measure the weather <i>right here, right now</i>. These numbers are measurements, not a forecast or a computer model.</p>
<p class="sl-about-h">What it measures</p>
<ul id="sl-sensors" class="sl-sensors"><li>Waiting for the first reading…</li></ul>
<p class="sl-about-h">How it fits with the rest of the app</p>
<ul class="sl-sensors">
<li><b>Now</b> shows a forecast model for the city you picked. <b>Station</b> shows what one spot is actually measuring.</li>
<li>When you see <b>📡 Your station</b> on the Now tab, its temperature, wind and rain come from this station.</li>
<li>It reports about every minute or two. <b>Live</b> means the last reading is under 15 minutes old; <b>Offline</b> means older.</li>
<li>Because it's one spot, it can differ a little from the airport or the forecast, and that's normal.</li>
</ul>
<p class="sl-about-foot">History, records and charts, plus a side-by-side with the official National Weather Service station, are on the <a href="/dashboard">Station Dashboard</a> (sign-in required).</p>
</div>
</details>
<section class="sl-hero">
<div class="sl-hero-dots"></div><div class="sl-hero-sweep"></div><div class="sl-hero-ring"></div><div class="sl-hero-ring2"></div>
<div class="sl-hero-body">
@@ -109,10 +125,21 @@ const setAll = (sel, v) => root.querySelectorAll(sel).forEach((el) => { el.textC
let shellReady = false;
let lastDir = null;
// "What is this station?" starts open for new visitors and stays closed once dismissed
function initAbout() {
const d = $('sl-about');
if (!d) return;
let closed = false; try { closed = localStorage.getItem('stationAboutClosed') === '1'; } catch {}
d.open = !closed;
d.addEventListener('toggle', () => { try { localStorage.setItem('stationAboutClosed', d.open ? '0' : '1'); } catch {} });
}
function ensureShell() {
if (shellReady) return;
root.innerHTML = SHELL;
shellReady = true;
initAbout();
drawCompass(null, null);
}
@@ -178,6 +205,19 @@ window.renderStationTab = function renderStationTab(device) {
const name = (typeof state !== 'undefined' && state.stationName) || device.info?.name || 'My Station';
set('sl-name', name);
// Describe the instruments this particular station really has
set('sl-about-name', name);
const has = (...k) => k.some((f) => d[f] != null);
const sensors = [
has('tempf', 'humidity') && '🌡 <b>Temperature &amp; humidity</b>: a shaded outdoor sensor',
has('windspeedmph', 'winddir', 'windgustmph') && '💨 <b>Wind</b>: speed, gusts and direction (anemometer and wind vane)',
has('dailyrainin', 'rainratein') && '🌧 <b>Rain</b>: a rain gauge that tips with each bit of rain',
has('baromrelin') && '🧭 <b>Pressure</b>: a barometer (falling pressure often means weather is coming)',
has('uv', 'solarradiation') && '☀️ <b>Sun</b>: UV and solar-radiation sensors',
has('tempinf', 'humidityin') && '🏠 <b>Indoor</b>: a sensor inside the house',
].filter(Boolean);
if (sensors.length) $('sl-sensors').innerHTML = sensors.map((x) => `<li>${x}</li>`).join('');
const fresh = d.dateutc && Date.now() - d.dateutc < 15 * 60 * 1000;
const live = $('sl-live');
live.classList.toggle('stale', !fresh);
@@ -230,7 +270,7 @@ window.stationTabMessage = function (kind, detail) {
return;
}
if (kind === 'unconfigured') {
showMessage('No station connected', 'Add your Ambient Weather API keys in Settings to see live readings from your own station here.');
showMessage('No station connected', 'This tab shows live readings from a <b>personal weather station</b> (a real device on someone\'s property that measures conditions on the spot, not a forecast). Add your Ambient Weather API keys in Settings to connect yours.');
} else {
showMessage('Station unavailable', detail || 'Could not reach your station right now. It will keep retrying.');
}