635b3ce598
- Custom Next.js server starts MQTT listener on boot - Subscribes to zigbee2mqtt/Bodenfeuchte_1, stores soil_moisture in SQLite - API route /api/data returns last 6 hours of measurements - Frontend shows current value + Recharts line chart, auto-refresh every 60s - Dockerfile + docker-compose with persistent volume for SQLite DB Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
38 lines
1.1 KiB
TypeScript
38 lines
1.1 KiB
TypeScript
import Database from 'better-sqlite3';
|
|
import path from 'path';
|
|
|
|
const DB_PATH = path.join(process.cwd(), 'data', 'bodenfeuchte.db');
|
|
|
|
let db: Database.Database;
|
|
|
|
export function getDb(): Database.Database {
|
|
if (!db) {
|
|
db = new Database(DB_PATH);
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS measurements (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
timestamp INTEGER NOT NULL,
|
|
soil_moisture REAL NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_timestamp ON measurements(timestamp);
|
|
`);
|
|
}
|
|
return db;
|
|
}
|
|
|
|
export function insertMeasurement(soilMoisture: number): void {
|
|
const stmt = getDb().prepare(
|
|
'INSERT INTO measurements (timestamp, soil_moisture) VALUES (?, ?)'
|
|
);
|
|
stmt.run(Date.now(), soilMoisture);
|
|
}
|
|
|
|
export function getLast6Hours(): { timestamp: number; soil_moisture: number }[] {
|
|
const since = Date.now() - 6 * 60 * 60 * 1000;
|
|
return getDb()
|
|
.prepare(
|
|
'SELECT timestamp, soil_moisture FROM measurements WHERE timestamp >= ? ORDER BY timestamp ASC'
|
|
)
|
|
.all(since) as { timestamp: number; soil_moisture: number }[];
|
|
}
|