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:
72
plugin/tools/kiosk/data-binder.js
Normal file
72
plugin/tools/kiosk/data-binder.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* data-binder.js — apre una singola sottoscrizione SignalK delta locale
|
||||
* e smista i valori ai tile per path.
|
||||
*
|
||||
* Espone window.dataBinder con:
|
||||
* subscribe(path, fn) → unsubscribe()
|
||||
* getLatest(path) → ultimo valore visto (o null)
|
||||
* onConnState(fn) → notifica connesso/disconnesso
|
||||
*/
|
||||
(function () {
|
||||
const subs = new Map(); // path -> Set<fn>
|
||||
const latest = new Map(); // path -> { value, ts }
|
||||
const stateListeners = new Set();
|
||||
let ws = null;
|
||||
let reconnectTimer = null;
|
||||
|
||||
function notify(path, value) {
|
||||
latest.set(path, { value, ts: Date.now() });
|
||||
const ss = subs.get(path);
|
||||
if (!ss) return;
|
||||
for (const fn of ss) { try { fn(value); } catch (e) { console.warn(e); } }
|
||||
}
|
||||
|
||||
function notifyState(s) { for (const fn of stateListeners) { try { fn(s); } catch (_) {} } }
|
||||
|
||||
function connect() {
|
||||
const url = `ws://${location.host}/signalk/v1/stream?subscribe=all`;
|
||||
try { ws = new WebSocket(url); }
|
||||
catch (err) { console.error('[BINDER] WS create:', err); scheduleReconnect(); return; }
|
||||
|
||||
ws.onopen = () => { notifyState({ connected: true }); console.log('[BINDER] WS connected'); };
|
||||
ws.onerror = (e) => console.warn('[BINDER] WS error', e);
|
||||
ws.onclose = () => { notifyState({ connected: false }); scheduleReconnect(); };
|
||||
ws.onmessage = (ev) => {
|
||||
let msg;
|
||||
try { msg = JSON.parse(ev.data); } catch { return; }
|
||||
if (!msg.updates) return;
|
||||
for (const u of msg.updates) {
|
||||
if (!u.values) continue;
|
||||
for (const v of u.values) {
|
||||
if (!v.path) continue;
|
||||
notify(v.path, v.value);
|
||||
// espandi position in lat/lon per chi si iscrive a quelli
|
||||
if (v.path === 'navigation.position' && v.value && typeof v.value === 'object') {
|
||||
if (v.value.latitude != null) notify('navigation.position.latitude', v.value.latitude);
|
||||
if (v.value.longitude != null) notify('navigation.position.longitude', v.value.longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function scheduleReconnect() {
|
||||
if (reconnectTimer) return;
|
||||
reconnectTimer = setTimeout(() => { reconnectTimer = null; connect(); }, 3000);
|
||||
}
|
||||
|
||||
function subscribe(path, fn) {
|
||||
if (!subs.has(path)) subs.set(path, new Set());
|
||||
subs.get(path).add(fn);
|
||||
const last = latest.get(path);
|
||||
if (last) { try { fn(last.value); } catch (_) {} }
|
||||
return () => subs.get(path)?.delete(fn);
|
||||
}
|
||||
|
||||
function getLatest(path) { return latest.get(path)?.value ?? null; }
|
||||
function onConnState(fn) { stateListeners.add(fn); return () => stateListeners.delete(fn); }
|
||||
|
||||
connect();
|
||||
|
||||
window.dataBinder = { subscribe, getLatest, onConnState };
|
||||
})();
|
||||
Reference in New Issue
Block a user