124 lines
4.9 KiB
JavaScript
124 lines
4.9 KiB
JavaScript
/**
|
||
* db.js – SQLite-Modul für Wetterdaten
|
||
*
|
||
* Exports:
|
||
* openDb(path) – DB öffnen / anlegen
|
||
* getLatestTs(db) – letzten archivierten Zeitstempel lesen
|
||
* insertRecord(db, record, source) – einzelnen Datensatz einfügen
|
||
* insertRecords(db, records, source) – Batch-Insert (Transaktion)
|
||
*/
|
||
|
||
import Database from "better-sqlite3";
|
||
|
||
// ── Schema ─────────────────────────────────────────────────────────────────
|
||
|
||
const SCHEMA = `
|
||
CREATE TABLE IF NOT EXISTS readings (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
ts INTEGER NOT NULL, -- Unix-Zeit in Sekunden (UTC)
|
||
source TEXT NOT NULL, -- 'archive' | 'loop'
|
||
temp_out REAL, -- °C
|
||
temp_out_high REAL, -- °C (nur Archiv)
|
||
temp_out_low REAL, -- °C (nur Archiv)
|
||
temp_in REAL, -- °C
|
||
hum_out INTEGER, -- %
|
||
hum_in INTEGER, -- %
|
||
wind_avg REAL, -- km/h
|
||
wind_high REAL, -- km/h (nur Archiv)
|
||
wind_dir TEXT, -- Himmelsrichtung
|
||
wind_high_dir TEXT, -- Himmelsrichtung (nur Archiv)
|
||
pressure REAL, -- hPa
|
||
rain REAL, -- mm (Archiv: Intervall; Loop: Tagessumme)
|
||
rain_rate REAL, -- mm/h
|
||
solar_rad INTEGER, -- W/m²
|
||
UNIQUE(ts, source)
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_readings_ts ON readings(ts);
|
||
`;
|
||
|
||
// ── Öffnen ─────────────────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Öffnet (oder erstellt) die SQLite-Datenbank und initialisiert das Schema.
|
||
* @param {string} dbPath – Pfad zur .db-Datei
|
||
* @returns {Database} better-sqlite3 Instanz
|
||
*/
|
||
export function openDb(dbPath) {
|
||
const db = new Database(dbPath);
|
||
db.pragma("journal_mode = WAL");
|
||
db.exec(SCHEMA);
|
||
return db;
|
||
}
|
||
|
||
// ── Lesen ──────────────────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Gibt den Unix-Zeitstempel (Sekunden) des neuesten Archiv-Eintrags zurück,
|
||
* oder null wenn die Tabelle leer ist.
|
||
*/
|
||
export function getLatestTs(db) {
|
||
const row = db.prepare(
|
||
"SELECT MAX(ts) AS ts FROM readings WHERE source = 'archive'"
|
||
).get();
|
||
return row?.ts ?? null;
|
||
}
|
||
|
||
// ── Schreiben ──────────────────────────────────────────────────────────────
|
||
|
||
const INSERT_SQL = `
|
||
INSERT OR IGNORE INTO readings
|
||
(ts, source, temp_out, temp_out_high, temp_out_low, temp_in,
|
||
hum_out, hum_in, wind_avg, wind_high, wind_dir, wind_high_dir,
|
||
pressure, rain, rain_rate, solar_rad)
|
||
VALUES
|
||
(@ts, @source, @temp_out, @temp_out_high, @temp_out_low, @temp_in,
|
||
@hum_out, @hum_in, @wind_avg, @wind_high, @wind_dir, @wind_high_dir,
|
||
@pressure, @rain, @rain_rate, @solar_rad)
|
||
`;
|
||
|
||
function toRow(record, source) {
|
||
return {
|
||
ts: Math.floor(record.time.getTime() / 1000),
|
||
source,
|
||
temp_out: record.tempOut ?? null,
|
||
temp_out_high: record.tempOutHigh ?? null,
|
||
temp_out_low: record.tempOutLow ?? null,
|
||
temp_in: record.tempIn ?? null,
|
||
hum_out: record.humOut ?? null,
|
||
hum_in: record.humIn ?? null,
|
||
wind_avg: record.windAvg ?? null,
|
||
wind_high: record.windHigh ?? null,
|
||
wind_dir: record.windDir ?? null,
|
||
wind_high_dir: record.windHighDir ?? null,
|
||
pressure: record.pressure ?? null,
|
||
rain: record.rain ?? null,
|
||
rain_rate: record.rainRate ?? null,
|
||
solar_rad: record.solarRad ?? null,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Fügt einen einzelnen Datensatz in die DB ein.
|
||
* Ignoriert Duplikate (gleiche ts + source) dank UNIQUE-Constraint.
|
||
*/
|
||
export function insertRecord(db, record, source) {
|
||
db.prepare(INSERT_SQL).run(toRow(record, source));
|
||
}
|
||
|
||
/**
|
||
* Fügt ein Array von Datensätzen in einer einzigen Transaktion ein.
|
||
* Gibt die Anzahl tatsächlich eingefügter Zeilen zurück.
|
||
*/
|
||
export function insertRecords(db, records, source) {
|
||
const stmt = db.prepare(INSERT_SQL);
|
||
const run = db.transaction(recs => {
|
||
let count = 0;
|
||
for (const r of recs) {
|
||
const info = stmt.run(toRow(r, source));
|
||
count += info.changes;
|
||
}
|
||
return count;
|
||
});
|
||
return run(records);
|
||
}
|