diff --git a/src/components/CurrentWeather.svelte b/src/components/CurrentWeather.svelte index 3ff513f..0a2f1b7 100644 --- a/src/components/CurrentWeather.svelte +++ b/src/components/CurrentWeather.svelte @@ -36,6 +36,8 @@ const unit = $derived(app.settings.units === 'imperial' ? '°F' : '°C') const windUnit = $derived(app.settings.units === 'imperial' ? 'mph' : 'km/h') + const fbPressure = $derived(!!data?._fallback?.pressure) + const fbUv = $derived(!!data?._fallback?.uv) {#if current} @@ -69,12 +71,12 @@ {Math.round(current.wind_speed_10m)} {windUnit} {windCompass(current.wind_direction_10m)} -
+
🌡️ Pressure {Math.round(current.pressure_msl || 0)} hPa
-
+
☀️ UV Index {current.uv_index != null ? Math.round(current.uv_index) : '--'} diff --git a/src/components/WeatherDetail.svelte b/src/components/WeatherDetail.svelte index d243f34..8d5ceb0 100644 --- a/src/components/WeatherDetail.svelte +++ b/src/components/WeatherDetail.svelte @@ -9,6 +9,8 @@ const unit = $derived(app.settings.units === 'imperial' ? '°F' : '°C') const windUnit = $derived(app.settings.units === 'imperial' ? 'mph' : 'km/h') + const fbPressure = $derived(!!data?._fallback?.pressure) + const fbUv = $derived(!!data?._fallback?.uv) function windCompass(deg) { if (deg == null) return '--' @@ -95,7 +97,7 @@
Pressure - {Math.round(current.pressure_msl || 0)} hPa + {Math.round(current.pressure_msl || 0)} hPa
Feels Like @@ -117,7 +119,7 @@
UV Index - + {current.uv_index != null ? Math.round(current.uv_index) : '--'} {#if current.uv_index != null} {getUVLevel(current.uv_index)} @@ -134,7 +136,7 @@
Max UV Today - {daily?.uv_index_max?.[0] != null ? Math.round(daily.uv_index_max[0]) : '--'} + {daily?.uv_index_max?.[0] != null ? Math.round(daily.uv_index_max[0]) : '--'}
diff --git a/src/lib/api/nws.js b/src/lib/api/nws.js index 5b76598..4828c66 100644 --- a/src/lib/api/nws.js +++ b/src/lib/api/nws.js @@ -252,3 +252,29 @@ export async function fetchForecastNWS(lat, lon, units = 'imperial') { return normalizeNWS({ points, forecast, hourly, grid, timeZone: tz, units }) } + +/** + * Backfill fields that NWS does not provide (pressure, UV index) from an + * Open-Meteo response, tagging them so the UI can show a tooltip that the + * value came from the non-selected source. + */ +export function mergeFallbackData(nwsData, omData) { + const fallback = {} + const cur = nwsData.current || {} + const omCur = omData?.current || {} + if (omCur.pressure_msl != null) { + cur.pressure_msl = omCur.pressure_msl + fallback.pressure = true + } + if (omCur.uv_index != null) { + cur.uv_index = omCur.uv_index + fallback.uv = true + } + const omDaily = omData?.daily || {} + if (omDaily.uv_index_max?.length && nwsData.daily) { + nwsData.daily.uv_index_max = omDaily.uv_index_max + fallback.uv = true + } + if (Object.keys(fallback).length) nwsData._fallback = fallback + return nwsData +} diff --git a/src/lib/stores/app.svelte.js b/src/lib/stores/app.svelte.js index 9ff75b9..7f9eda5 100644 --- a/src/lib/stores/app.svelte.js +++ b/src/lib/stores/app.svelte.js @@ -4,7 +4,7 @@ import { getLocations, loadSettings } from '../storage/db.js' import { fetchForecast } from '../api/weather.js' -import { fetchForecastNWS } from '../api/nws.js' +import { fetchForecastNWS, mergeFallbackData } from '../api/nws.js' export class AppStore { // Location state @@ -116,6 +116,14 @@ export class AppStore { if (source === 'nws') { try { data = await fetchForecastNWS(location.lat, location.lon, units) + // NWS omits pressure + UV index — backfill those from Open-Meteo and + // flag them so the UI can tooltip that they came from the other source. + try { + const om = await fetchForecast(location.lat, location.lon, units) + data = mergeFallbackData(data, om) + } catch (omErr) { + console.warn('Open-Meteo fields unavailable:', omErr.message) + } } catch (nwsErr) { // NWS is US-only and can be unavailable — fall back to Open-Meteo // so the app keeps working rather than breaking. diff --git a/tests/nws.test.js b/tests/nws.test.js index 1f11a12..dcf5e43 100644 --- a/tests/nws.test.js +++ b/tests/nws.test.js @@ -8,6 +8,7 @@ import { dateKey, localTimeString, normalizeNWS, + mergeFallbackData, } from '../src/lib/api/nws.js' const FIX = (name) => JSON.parse(readFileSync(path.join('tests/fixtures/nws', name), 'utf8')) @@ -103,3 +104,26 @@ describe('normalizeNWS (recorded live fixture for Rogers County, OK)', () => { expect(m.current.wind_speed_10m).toBeCloseTo(10 * 1.609344, 1) }) }) + +describe('mergeFallbackData', () => { + it('backfills pressure + UV from Open-Meteo and tags them (non-selected source)', () => { + const nws = { + _source: 'nws', + current: { temperature_2m: 103, pressure_msl: null, uv_index: null }, + daily: { time: ['2026-08-16'], uv_index_max: [] }, + } + const om = { current: { pressure_msl: 1011.2, uv_index: 7.4 }, daily: { uv_index_max: [7.2, 6, 5] } } + const r = mergeFallbackData(nws, om) + expect(r.current.pressure_msl).toBe(1011.2) + expect(r.current.uv_index).toBe(7.4) + expect(r.daily.uv_index_max).toEqual([7.2, 6, 5]) + expect(r._fallback).toMatchObject({ pressure: true, uv: true }) + }) + + it('does not tag when Open-Meteo lacks the fields', () => { + const nws = { current: { pressure_msl: null, uv_index: null }, daily: {} } + const om = { current: { pressure_msl: null, uv_index: null }, daily: {} } + const r = mergeFallbackData(nws, om) + expect(r._fallback).toBeUndefined() + }) +})