Try every configured Met Office key per tier, don't assume which covers what
Forecast key -> hourly was tried and confirmed wrong (settings test route reported 'not subscribed'). New fetchWithKeys() tries Spot then Forecast for EVERY tier (hourly, three-hourly, daily) and keeps whichever succeeds, rather than assigning a specific key to a specific endpoint based on an unverified guess about Met Office's product catalog. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
4b535a34f4
commit
15a801b1f4
1 changed files with 35 additions and 12 deletions
|
|
@ -10,13 +10,15 @@
|
||||||
// other endpoints could actually serve. This matters because Met Office DataHub
|
// other endpoints could actually serve. This matters because Met Office DataHub
|
||||||
// sells "Global Spot" and "Forecast" as SEPARATE subscriptions with separate keys
|
// sells "Global Spot" and "Forecast" as SEPARATE subscriptions with separate keys
|
||||||
// (settings' met_office integration: api_key = Spot, forecast_api_key = Forecast,
|
// (settings' met_office integration: api_key = Spot, forecast_api_key = Forecast,
|
||||||
// optional) — only Spot (used here for daily) has ever been confirmed live (hvac,
|
// optional) — only Spot's daily endpoint has ever been confirmed live (hvac,
|
||||||
// 2026-08-25); hourly/three-hourly try forecast_api_key first (falling back to
|
// 2026-08-25). Which key actually covers hourly/three-hourly is NOT documented
|
||||||
// api_key if no separate Forecast key is configured), since Spot alone may not
|
// anywhere we could find, and a first guess (Forecast key -> hourly) was tried and
|
||||||
// actually be subscribed to those endpoints. Field names for hourly/three-hourly
|
// confirmed wrong via settings' met_office test route — so every tier just tries
|
||||||
// are taken from the published Met Office DataHub docs, still not verified live —
|
// whichever keys are configured (Spot then Forecast, see fetchWithKeys) and keeps
|
||||||
// a mismatch there just throws and that tier falls back to Open-Meteo, same as an
|
// whichever works, discovered at runtime rather than assumed. Field names for
|
||||||
// outright subscription failure.
|
// hourly/three-hourly are taken from the published Met Office DataHub docs, still
|
||||||
|
// not verified live — a mismatch there just throws and that tier falls back to
|
||||||
|
// Open-Meteo, same as an outright subscription failure.
|
||||||
import { pool, setStatus } from '../db.js'
|
import { pool, setStatus } from '../db.js'
|
||||||
import { iconFromMetOffice, iconFromOpenMeteo } from './icons.js'
|
import { iconFromMetOffice, iconFromOpenMeteo } from './icons.js'
|
||||||
|
|
||||||
|
|
@ -217,6 +219,25 @@ function openMeteoAstroPoints(dailyRows, astroKeys) {
|
||||||
.map(r => ({ date: r.dateKey, sunrise: r.sunrise, sunset: r.sunset }))
|
.map(r => ({ date: r.dateKey, sunrise: r.sunrise, sunset: r.sunset }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tries each configured key in turn against the same tier, returning the first
|
||||||
|
// success. Which of Spot/Forecast actually covers which endpoint isn't documented
|
||||||
|
// anywhere we could find, and a first guess (Forecast -> hourly specifically) was
|
||||||
|
// tried and confirmed wrong live via settings' met_office test route — so rather
|
||||||
|
// than assuming again, every tier just tries whatever keys are configured and
|
||||||
|
// keeps whichever one actually works, discovered at runtime instead of guessed at.
|
||||||
|
async function fetchWithKeys(fetchFn, keys) {
|
||||||
|
const errors = []
|
||||||
|
for (const [name, key] of keys) {
|
||||||
|
if (!key) continue
|
||||||
|
try {
|
||||||
|
return await fetchFn(key)
|
||||||
|
} catch (err) {
|
||||||
|
errors.push(`${name}: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(errors.join('; ') || 'no Met Office key configured')
|
||||||
|
}
|
||||||
|
|
||||||
// ── Storage ──────────────────────────────────────────────────────────────
|
// ── Storage ──────────────────────────────────────────────────────────────
|
||||||
async function replaceResolution(resolution, points) {
|
async function replaceResolution(resolution, points) {
|
||||||
const client = await pool.connect()
|
const client = await pool.connect()
|
||||||
|
|
@ -293,13 +314,15 @@ export async function refreshForecast(log = console) {
|
||||||
let hourlyPoints = [], threeHourlyPoints = [], dailyPoints = [], astroPoints = []
|
let hourlyPoints = [], threeHourlyPoints = [], dailyPoints = [], astroPoints = []
|
||||||
|
|
||||||
if (metOffice?.api_key) {
|
if (metOffice?.api_key) {
|
||||||
const spotKey = metOffice.api_key
|
// Spot first (the one confirmed working, for daily) then Forecast, for
|
||||||
const forecastKey = metOffice.forecast_api_key || metOffice.api_key
|
// every tier — see fetchWithKeys' comment for why this doesn't assume
|
||||||
|
// which key covers which endpoint.
|
||||||
|
const keys = [['Spot', metOffice.api_key], ['Forecast', metOffice.forecast_api_key]]
|
||||||
|
|
||||||
const [hourlyR, threeHourlyR, dailyR] = await Promise.allSettled([
|
const [hourlyR, threeHourlyR, dailyR] = await Promise.allSettled([
|
||||||
fetchMetOfficeHourly(lat, lon, forecastKey),
|
fetchWithKeys(key => fetchMetOfficeHourly(lat, lon, key), keys),
|
||||||
fetchMetOfficeThreeHourly(lat, lon, forecastKey, dayKeys),
|
fetchWithKeys(key => fetchMetOfficeThreeHourly(lat, lon, key, dayKeys), keys),
|
||||||
fetchMetOfficeDaily(lat, lon, spotKey, skipKeys, astroKeys),
|
fetchWithKeys(key => fetchMetOfficeDaily(lat, lon, key, skipKeys, astroKeys), keys),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (hourlyR.status === 'fulfilled') {
|
if (hourlyR.status === 'fulfilled') {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue