Files
signalk-plugin/plugin/tools/kiosk/control-socket.js
Giuseppe Raffa bb8d267cd4 Aggiunta stili CSS per Kiosk, struttura HTML per la Mappa e Riferimenti ai Sensori
• Creato un nuovo file CSS per gli stili del chiosco (kiosk) con variabili, stili per le schede (card) e animazioni.
• Aggiunto un file HTML per l'interfaccia della mappa utilizzando Mapbox, inclusi gli stili e il JavaScript per le funzionalità della mappa.
• Introdotto un file JSON per i riferimenti ai sensori, definendo percorsi ed elementi per i dati di temperatura, vento, onde, posizione, batteria, motore e sistema.

Co-authored-by: Copilot <copilot@github.com>
2026-04-23 16:19:11 +02:00

91 lines
3.6 KiB
JavaScript

/**
* Kiosk plugin page bootstrap:
* 1) legge config dal <meta> iniettato dal server del plugin
* 2) carica template attivo via API
* 3) apre WS locale SignalK per i valori live
* 4) apre WS "leggero" al realtime server per comandi dalla console
*/
(async function () {
function cfg(name, fallback) {
const m = document.querySelector(`meta[name="${name}"]`);
return (m && m.content) || fallback;
}
const apiUrl = cfg('api-url', 'https://api.mebboat.it');
const realtimeUrl = cfg('realtime-url', 'https://realtime.mebboat.it');
const realtimeWsUrl = cfg('realtime-ws-url', 'wss://realtime.mebboat.it');
const sensorCode = cfg('sensor-code', '');
const sensorName = cfg('sensor-name', '');
window.kiosk.init({ apiUrl, sensorCode, sensorName });
// 1) template iniziale
await window.kiosk.loadTemplate();
// 2) WS locale SignalK
try {
const skWs = new WebSocket(`ws://${location.host}/signalk/v1/stream?subscribe=all`);
skWs.onmessage = (ev) => {
let msg; try { msg = JSON.parse(ev.data); } catch { return; }
for (const u of msg.updates || []) for (const v of u.values || []) {
window.kiosk.updateValue(v.path, v.value);
}
};
skWs.onclose = () => setTimeout(() => location.reload(), 5000);
} catch (e) { console.error('[kiosk] signalk ws error', e); }
// 3) WS controllo verso realtime server
if (!sensorCode || !sensorName) { window.kiosk.setStatus('no sensor config', true); return; }
let controlWs = null;
let reconnectTm = null;
async function fetchSocketToken() {
const r = await fetch(`${realtimeUrl}/connect`, {
method: 'POST', headers: { 'Content-Type':'application/json' },
body: JSON.stringify({ name: sensorName, code: sensorCode })
});
if (!r.ok) return null;
const j = await r.json();
return j.s === 'ok' ? j.t : null;
}
async function connectControl() {
clearTimeout(reconnectTm);
const token = await fetchSocketToken();
if (!token) { reconnectTm = setTimeout(connectControl, 5000); return; }
const url = `${realtimeWsUrl}/kiosk?role=device&sensor=${encodeURIComponent(sensorName)}&token=${encodeURIComponent(token)}`;
controlWs = new WebSocket(url);
controlWs.onopen = () => {
controlWs.send(JSON.stringify({ t:'hello', templateId: window.kiosk.currentTemplateId() }));
};
controlWs.onmessage = async (ev) => {
let m; try { m = JSON.parse(ev.data); } catch { return; }
let ok = true, err = null;
try {
switch (m.t) {
case 'patch_box': ok = window.kiosk.patchBox(m.boxId, m.patch || {}); break;
case 'add_box': ok = window.kiosk.addBox(m.box); break;
case 'remove_box': ok = window.kiosk.removeBox(m.boxId); break;
case 'load_template': {
const tpl = await window.kiosk.loadTemplate(m.templateId);
ok = !!tpl;
break;
}
case 'apply_inline': ok = window.kiosk.applyInline(m.content); break;
case 'persist': ok = true; break; // no-op locale, la persistenza è server-side
case 'reload': location.reload(); return;
default: ok = false; err = 'unknown cmd';
}
} catch (e) { ok = false; err = e.message; }
if (m.cmdId) controlWs.send(JSON.stringify({ t:'ack', cmdId: m.cmdId, ok, err }));
};
controlWs.onclose = () => { reconnectTm = setTimeout(connectControl, 5000); };
controlWs.onerror = () => { try { controlWs.close(); } catch {} };
setInterval(() => { if (controlWs && controlWs.readyState === 1) controlWs.send(JSON.stringify({ t:'heartbeat' })); }, 25000);
}
connectControl();
})();