feat: Implement rulesets and layout management for kiosk plugin
- Added rulesets manager to handle various data types and updates via HTTP and WebSocket. - Introduced layout store for managing kiosk layouts with caching and server synchronization. - Enhanced dashboard and data routes to support new layout and ruleset features. - Updated kiosk HTML and JavaScript to utilize new layout rendering and data binding. - Removed obsolete map route and integrated map functionality into the new tile renderer. - Improved Telegram commands to reflect changes in data structure and logging. - Refactored weather fetching intervals to prevent multiple instances. - Added SSE stream for real-time layout updates in the kiosk.
This commit is contained in:
80
plugin/tools/kiosk/server-layout-store.js
Normal file
80
plugin/tools/kiosk/server-layout-store.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Store del kiosk layout lato plugin.
|
||||
*
|
||||
* - cache su disco: data/kiosk-layout.json
|
||||
* - bootstrap HTTP: GET {API_URL}/kiosklayouts/sensor/<SENSOR_ID>/active
|
||||
* - apply push WS: invocato da socket.js quando arriva _t:'kiosk_layout_update'
|
||||
* - SSE stream: espone un emitter consumato dalla route /meb/kiosk/stream
|
||||
*
|
||||
* Il plugin NON sceglie il layout: lo riceve dal server e lo mostra.
|
||||
*/
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const EventEmitter = require('events');
|
||||
const configManager = require('../../config/configManager');
|
||||
|
||||
const cacheFile = path.join(__dirname, '../../../data/kiosk-layout.json');
|
||||
const emitter = new EventEmitter();
|
||||
|
||||
let current = null;
|
||||
|
||||
async function ensureDir() {
|
||||
try { await fs.mkdir(path.dirname(cacheFile), { recursive: true }); } catch {}
|
||||
}
|
||||
|
||||
async function loadCache() {
|
||||
try {
|
||||
const raw = await fs.readFile(cacheFile, 'utf-8');
|
||||
return JSON.parse(raw);
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
async function saveCache() {
|
||||
if (!current) return;
|
||||
try { await ensureDir(); await fs.writeFile(cacheFile, JSON.stringify(current, null, 2)); }
|
||||
catch (err) { console.warn('[KIOSK|STORE] save cache failed:', err.message); }
|
||||
}
|
||||
|
||||
async function init() {
|
||||
current = await loadCache();
|
||||
if (current) {
|
||||
console.log(`[KIOSK|STORE] cache caricata v=${current.version || '?'} (${current.content?.tiles?.length || 0} tiles)`);
|
||||
} else {
|
||||
console.log('[KIOSK|STORE] nessun layout in cache');
|
||||
}
|
||||
}
|
||||
|
||||
async function bootstrapFromServer() {
|
||||
const API_URL = process.env.API_URL;
|
||||
if (!API_URL) { console.warn('[KIOSK|STORE] API_URL non configurato'); return; }
|
||||
const sensorId = configManager.getSensorName() || process.env.SENSOR_ID;
|
||||
if (!sensorId) { console.warn('[KIOSK|STORE] SENSOR_ID non configurato'); return; }
|
||||
try {
|
||||
const r = await fetch(`${API_URL}/kiosklayouts/sensor/${encodeURIComponent(sensorId)}/active`, {
|
||||
signal: AbortSignal.timeout(8000)
|
||||
});
|
||||
if (!r.ok) { if (r.status !== 404) console.warn('[KIOSK|STORE] bootstrap', r.status); return; }
|
||||
const layout = await r.json();
|
||||
await applyRemote(layout);
|
||||
} catch (err) {
|
||||
console.warn('[KIOSK|STORE] bootstrap err:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applica un layout (dedup per id+version), salva cache, emette 'update'.
|
||||
*/
|
||||
async function applyRemote(layout) {
|
||||
if (!layout || !layout.content) return false;
|
||||
if (current && current.id === layout.id && current.version === layout.version) return false;
|
||||
current = layout;
|
||||
await saveCache();
|
||||
console.log(`[KIOSK|STORE] applicato v=${layout.version || '?'} (${layout.content.tiles?.length || 0} tiles)`);
|
||||
emitter.emit('update', current);
|
||||
return true;
|
||||
}
|
||||
|
||||
function get() { return current; }
|
||||
function onUpdate(fn) { emitter.on('update', fn); return () => emitter.off('update', fn); }
|
||||
|
||||
module.exports = { init, bootstrapFromServer, applyRemote, get, onUpdate };
|
||||
Reference in New Issue
Block a user