- 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.
81 lines
2.9 KiB
JavaScript
81 lines
2.9 KiB
JavaScript
/**
|
|
* 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 };
|