/** * Kiosk plugin page bootstrap: * 1) legge config dal 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(); })();