Files
the-nexus/modules/data/weather.js
Alexander Whitestone a1eb0ebb90
Some checks failed
CI / validate (pull_request) Failing after 14s
CI / auto-merge (pull_request) Has been skipped
[modularization] Phase 2: Extract data layer — gitea, weather, bitcoin, loaders
Created modules/data/ with 4 data modules:
- data/gitea.js: Centralized Gitea API (commits, PRs, agent status)
- data/weather.js: Open-Meteo weather data fetch
- data/bitcoin.js: Blockstream block height polling
- data/loaders.js: Static file loaders (portals.json, sovereignty-status.json, SOUL.md)

Updated consumers to use data modules instead of direct fetch():
- panels.js: Uses data/gitea.js for agent status
- heatmap.js: Uses data/gitea.js for commit data
- extras.js: Uses data/gitea.js for timelapse, data/bitcoin.js for block height
- portals.js: Uses data/loaders.js for portals.json
- effects.js: Uses data/loaders.js for sovereignty-status.json
- oath.js: Uses data/loaders.js for SOUL.md
- audio.js: Uses data/loaders.js for SOUL.md

Deduplication wins:
- Gitea token centralized (was hardcoded in 3 files)
- SOUL.md now cached (was fetched 3x independently)
- Nexus commits shared cache (was fetched 2x independently)

Refs #421
2026-03-24 17:25:51 -04:00

35 lines
1.9 KiB
JavaScript

// modules/data/weather.js — Open-Meteo weather fetch
// Writes to: weatherState (returned), scene effects applied by caller
const WEATHER_LAT = 43.2897;
const WEATHER_LON = -72.1479;
const WEATHER_REFRESH_MS = 15 * 60 * 1000;
function weatherCodeToLabel(code) {
if (code === 0) return { condition: 'Clear', icon: '☀️' };
if (code <= 2) return { condition: 'Partly Cloudy', icon: '⛅' };
if (code === 3) return { condition: 'Overcast', icon: '☁️' };
if (code >= 45 && code <= 48) return { condition: 'Fog', icon: '🌫️' };
if (code >= 51 && code <= 57) return { condition: 'Drizzle', icon: '🌦️' };
if (code >= 61 && code <= 67) return { condition: 'Rain', icon: '🌧️' };
if (code >= 71 && code <= 77) return { condition: 'Snow', icon: '❄️' };
if (code >= 80 && code <= 82) return { condition: 'Showers', icon: '🌦️' };
if (code >= 85 && code <= 86) return { condition: 'Snow Showers', icon: '🌨️' };
if (code >= 95 && code <= 99) return { condition: 'Thunderstorm', icon: '⛈️' };
return { condition: 'Unknown', icon: '🌀' };
}
export async function fetchWeatherData() {
const url = `https://api.open-meteo.com/v1/forecast?latitude=${WEATHER_LAT}&longitude=${WEATHER_LON}&current=temperature_2m,weather_code,wind_speed_10m,cloud_cover&temperature_unit=fahrenheit&wind_speed_unit=mph&forecast_days=1`;
const res = await fetch(url);
if (!res.ok) throw new Error('weather fetch failed');
const data = await res.json();
const cur = data.current;
const code = cur.weather_code;
const { condition, icon } = weatherCodeToLabel(code);
const cloudcover = typeof cur.cloud_cover === 'number' ? cur.cloud_cover : 50;
return { code, temp: cur.temperature_2m, wind: cur.wind_speed_10m, condition, icon, cloudcover };
}
export { WEATHER_REFRESH_MS };