Files
wetter-server/db.js

116 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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_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
pressure REAL, -- hPa
rain REAL, -- mm (Archiv: Intervall; Loop: Tagessumme)
rain_rate REAL, -- mm/h
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_in,
hum_out, hum_in, wind_avg, wind_high, wind_dir,
pressure, rain, rain_rate)
VALUES
(@ts, @source, @temp_out, @temp_in,
@hum_out, @hum_in, @wind_avg, @wind_high, @wind_dir,
@pressure, @rain, @rain_rate)
`;
function toRow(record, source) {
return {
ts: Math.floor(record.time.getTime() / 1000),
source,
temp_out: record.tempOut ?? null,
temp_in: record.tempIn ?? null,
hum_out: record.humOut ?? null,
hum_in: record.humIn ?? null,
wind_avg: record.windAvg ?? null,
wind_high: record.windGust ?? null,
wind_dir: record.windDir ?? null,
pressure: record.pressure ?? null,
rain: record.rain ?? null,
rain_rate: record.rainRate ?? 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);
}