119 lines
3.8 KiB
JavaScript
119 lines
3.8 KiB
JavaScript
import {cfg} from './config.mjs';
|
|
import { frostRisk } from './forecast.mjs';
|
|
|
|
async function query(c, q, start, end) {
|
|
const url = new URL('/api/v1/query_range', c.DB_HOST);
|
|
url.searchParams.set('query', q);
|
|
url.searchParams.set('step', '60s');
|
|
if (start) url.searchParams.set('start', start);
|
|
if (end) url.searchParams.set('end', end);
|
|
const res = await fetch(url);
|
|
const json = await res.json();
|
|
return json.data?.result || [];
|
|
}
|
|
|
|
async function queryInstant(c, q) {
|
|
const url = new URL('/api/v1/query', c.DB_HOST);
|
|
url.searchParams.set('query', q);
|
|
const res = await fetch(url);
|
|
const json = await res.json();
|
|
return json.data?.result || [];
|
|
}
|
|
|
|
function metricToFields(results) {
|
|
const out = {};
|
|
for (const r of results) {
|
|
if(r.metric.__name__.startsWith('system_')) continue;
|
|
const val = r.value?.[1] ?? r.values?.at(-1)?.[1];
|
|
if (val !== undefined) out[r.metric.__name__.replace('weather_', '')] = parseFloat(val);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
export async function queryCurrent() {
|
|
const c = cfg()
|
|
const results = await queryInstant(c, `{__name__!=""}`)
|
|
const fields = metricToFields(results)
|
|
const dp = fields.dew_point ?? null
|
|
return {
|
|
time: new Date(),
|
|
...fields,
|
|
frost_risk: frostRisk(fields.temperature ?? 99, dp ?? 99, fields.humidity ?? 0),
|
|
}
|
|
}
|
|
|
|
export async function queryHourly(start, end) {
|
|
const c = cfg();
|
|
const s = Math.floor(start.getTime() / 1000);
|
|
const e = Math.floor(end.getTime() / 1000);
|
|
const byTime = {};
|
|
|
|
const results = await query(c, `avg_over_time({__name__!=""}[1h])`, s, e);
|
|
results.forEach(r => {
|
|
r.values.forEach(([ts, val]) => {
|
|
if(r.metric.__name__.startsWith('system_')) return;
|
|
const time = new Date(ts * 1000).toISOString().slice(0, 13) + ':00';
|
|
if (!byTime[time]) byTime[time] = {time};
|
|
byTime[time][r.metric.__name__.replace('weather_', '')] = parseFloat(val);
|
|
});
|
|
});
|
|
return Object.values(byTime)
|
|
.sort((a, b) => a.time.localeCompare(b.time))
|
|
.map(r => ({ ...r, time: new Date(r.time) }));
|
|
}
|
|
|
|
export async function queryDaily(start, end) {
|
|
const c = cfg();
|
|
const s = Math.floor(start.getTime() / 1000);
|
|
const e = Math.floor(end.getTime() / 1000);
|
|
const byTime = {};
|
|
|
|
const [means, mins, maxs] = await Promise.all([
|
|
query(c, `avg_over_time({__name__!=""}[1d])`, s, e),
|
|
query(c, `min_over_time({__name__!=""}[1d])`, s, e),
|
|
query(c, `max_over_time({__name__!=""}[1d])`, s, e),
|
|
]);
|
|
|
|
means.forEach(r => {
|
|
r.values.forEach(([ts, val]) => {
|
|
if(r.metric.__name__.startsWith('system_')) return;
|
|
const time = new Date(ts * 1000).toISOString().slice(0, 10);
|
|
if (!byTime[time]) byTime[time] = {time};
|
|
byTime[time][r.metric.__name__.replace('weather_', '')] = parseFloat(val);
|
|
});
|
|
});
|
|
|
|
mins.forEach(r => {
|
|
if (r.metric.__name__ !== 'temperature') return;
|
|
r.values.forEach(([ts, val]) => {
|
|
const time = new Date(ts * 1000).toISOString().slice(0, 10);
|
|
if (!byTime[time]) byTime[time] = {time};
|
|
byTime[time].env_temp_min_c = parseFloat(val);
|
|
});
|
|
});
|
|
|
|
maxs.forEach(r => {
|
|
if (r.metric.__name__ !== 'temperature') return;
|
|
r.values.forEach(([ts, val]) => {
|
|
const time = new Date(ts * 1000).toISOString().slice(0, 10);
|
|
if (!byTime[time]) byTime[time] = {time};
|
|
byTime[time].env_temp_max_c = parseFloat(val);
|
|
});
|
|
});
|
|
|
|
return Object.values(byTime)
|
|
.sort((a, b) => a.time.localeCompare(b.time))
|
|
.map(r => ({ ...r, time: new Date(r.time) }));
|
|
}
|
|
|
|
export async function getCoords() {
|
|
const c = cfg();
|
|
const results = await queryInstant(c, `{__name__=~"latitude|longitude|altitude"}`);
|
|
const fields = metricToFields(results);
|
|
|
|
if (fields.latitude && fields.longitude) {
|
|
return {latitude: fields.latitude, longitude: fields.longitude, altitude: fields.altitude || c.ALTITUDE};
|
|
}
|
|
return {latitude: c.LATITUDE, longitude: c.LONGITUDE, altitude: c.ALTITUDE};
|
|
}
|