First commit

This commit is contained in:
2026-04-03 22:24:22 +02:00
commit d1cfee0dea
13 changed files with 1464 additions and 0 deletions

123
db.js Normal file
View File

@@ -0,0 +1,123 @@
/**
* 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);
}