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>
This commit is contained in:
73
plugin/config/configManager.js
Normal file
73
plugin/config/configManager.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Gestore centralizzato della configurazione del plugin.
|
||||
* Questa soluzione permette di leggere i valori di configurazione dinamicamente,
|
||||
* quindi cambiano in tempo reale quando l'utente li modifica dalle impostazioni del plugin.
|
||||
*/
|
||||
|
||||
let pluginOptions = {};
|
||||
|
||||
/**
|
||||
* Inizializza il ConfigManager con le opzioni del plugin.
|
||||
* Deve essere chiamato all'avvio del plugin.
|
||||
* @param {Object} options - Le opzioni passate da Signal K al plugin
|
||||
*/
|
||||
function init(options) {
|
||||
pluginOptions = options || {};
|
||||
console.log('[CONFIG] ConfigManager inizializzato');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottiene il Telegram Bot Token
|
||||
*/
|
||||
function getTelegramToken() {
|
||||
return process.env.TELEGRAM_BOT_TOKEN || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottiene il codice sensore
|
||||
*/
|
||||
function getSensorCode() {
|
||||
return pluginOptions.sensor_code || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottiene il nome sensore
|
||||
*/
|
||||
function getSensorName() {
|
||||
return pluginOptions.sensor_name || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottiene l'intervallo di invio dati (in millisecondi, converte da secondi)
|
||||
*/
|
||||
function getSendInterval() {
|
||||
const seconds = pluginOptions.sensor_interval || process.env.SEND_INTERVAL;
|
||||
return (typeof seconds === 'number' ? seconds : parseInt(seconds)) * 1000 || 60000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottiene il ritardo di riconnessione (in millisecondi, converte da secondi)
|
||||
*/
|
||||
function getReconnectDelay() {
|
||||
const seconds = pluginOptions.reconnect_delay || process.env.RECONNECT_DELAY;
|
||||
return (typeof seconds === 'number' ? seconds : parseInt(seconds)) * 1000 || 5000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottiene un valore di configurazione generico
|
||||
* @param {string} key - La chiave della configurazione
|
||||
* @param {*} defaultValue - Il valore di default
|
||||
*/
|
||||
function get(key, defaultValue = null) {
|
||||
return pluginOptions[key] !== undefined ? pluginOptions[key] : defaultValue;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init,
|
||||
getTelegramToken,
|
||||
getSensorCode,
|
||||
getSensorName,
|
||||
getSendInterval,
|
||||
getReconnectDelay,
|
||||
get
|
||||
};
|
||||
171
plugin/config/skFlow.js
Normal file
171
plugin/config/skFlow.js
Normal file
@@ -0,0 +1,171 @@
|
||||
let skApp = null;
|
||||
|
||||
/**
|
||||
* Inizializza il modulo con l'istanza dell'app Signal K.
|
||||
* Da chiamare una sola volta nel plugin.start()
|
||||
* @param {Object} app - l'istanza dell'applicazione Signal K
|
||||
*/
|
||||
function init(app) {
|
||||
skApp = app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pubblica un set di dati nel data browser di Signal K tramite i delta.
|
||||
* @param {Object} data - Oggetto JSON dove le chiavi sono i percorsi e i valori sono i dati da pubblicare
|
||||
*/
|
||||
function publish(data) {
|
||||
|
||||
//TODO: Controlla se serve aggiungere typeof skApp.handleMessage !== 'function' (controlla che esista la funzione handleMessage, ma in teoria esiste sempre)
|
||||
if (!skApp) {
|
||||
console.error('[SKFLOW] skApp non inizializzato')
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
console.error('[SKFLOW] Dati non validi')
|
||||
return;
|
||||
}
|
||||
|
||||
const values = Object.entries(data).map(([path, value]) => {
|
||||
return {
|
||||
path: path,
|
||||
value: value
|
||||
};
|
||||
});
|
||||
|
||||
//La funzione non continua se non ci sono dati
|
||||
//TODO: Controllare se serve davvero, non dovrebbe interrompersi già al check di data?
|
||||
if (values.length === 0) return;
|
||||
|
||||
// Viene creato un "Delta Update" con l'ID del plugin 'meb.plugin' e l'array di valori.
|
||||
skApp.handleMessage('meb.plugin', {
|
||||
updates: [
|
||||
{
|
||||
values: values
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottieni i dati dal Data-Browser di Signal K
|
||||
* @param {String} path - Il path Signal K
|
||||
* @returns {*} Il dato
|
||||
*/
|
||||
function get(path) {
|
||||
if (!skApp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const valObj = skApp.getSelfPath(path);
|
||||
return valObj ? valObj.value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottieni tutti i dati nel databrowser di Signal K che corrispondono ad un ID o ad una sorgente specifica.
|
||||
* @param {String} source - Il parametro da confrontare con il sorgente ($source o source) o ID del dato.
|
||||
* @returns {Object} Un oggetto contenente path e valori trovati.
|
||||
*/
|
||||
function getBySource(source) {
|
||||
if (!skApp) return {};
|
||||
|
||||
const results = {};
|
||||
const self = skApp.signalk?.self || skApp.signalk?.retrieve()?.vessels?.[skApp.selfId] || {};
|
||||
|
||||
if (!self || Object.keys(self).length === 0) {
|
||||
console.log('[SKFLOW] Nessun dato trovato nel databrowser');
|
||||
return results;
|
||||
}
|
||||
|
||||
const traverse = (obj, path = '') => {
|
||||
if (!obj || typeof obj !== 'object') return;
|
||||
|
||||
// Se l'oggetto ha una proprietà 'value', verifichiamo la sorgente
|
||||
if (Object.prototype.hasOwnProperty.call(obj, 'value')) {
|
||||
const hasSource = obj.$source === source || obj.source === source || obj.id === source;
|
||||
if (hasSource) {
|
||||
results[path] = obj.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Esplora i sotto-oggetti escludendo chiavi di sistema che non sono percorsi SK
|
||||
const skip = ['value', 'timestamp', '$source', 'source', 'meta', 'sentence', 'talker'];
|
||||
for (const key in obj) {
|
||||
if (skip.includes(key)) continue;
|
||||
const subPath = path ? `${path}.${key}` : key;
|
||||
traverse(obj[key], subPath);
|
||||
}
|
||||
};
|
||||
|
||||
traverse(self);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottieni tutti i dati nel databrowser di Signal K il cui path inizia con la stringa specificata.
|
||||
* @param {String} filterPath - La stringa con cui deve iniziare il path (es. "custom.plugin").
|
||||
* @returns {Object} Un oggetto contenente path e valori trovati.
|
||||
*/
|
||||
function getWithFilter(filterPath) {
|
||||
if (!skApp) return {};
|
||||
|
||||
const results = {};
|
||||
const self = skApp.signalk?.self || skApp.signalk?.retrieve()?.vessels?.[skApp.selfId] || {};
|
||||
|
||||
if (!self || Object.keys(self).length === 0) {
|
||||
return results;
|
||||
}
|
||||
|
||||
const traverse = (obj, path = '') => {
|
||||
if (!obj || typeof obj !== 'object') return;
|
||||
|
||||
// Se l'oggetto ha una proprietà 'value', verifichiamo se il path corrisponde al filtro
|
||||
if (Object.prototype.hasOwnProperty.call(obj, 'value')) {
|
||||
if (path.startsWith(filterPath)) {
|
||||
results[path] = obj.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Esplora i sotto-oggetti escludendo chiavi di sistema
|
||||
const skip = ['value', 'timestamp', '$source', 'source', 'meta', 'sentence', 'talker'];
|
||||
for (const key in obj) {
|
||||
if (skip.includes(key)) continue;
|
||||
const subPath = path ? `${path}.${key}` : key;
|
||||
|
||||
// Ottimizzazione: se `subPath` non inizia con `filterPath` E `filterPath` non inizia con `subPath`,
|
||||
// possiamo evitare di scendere in rami completamente irrilevanti.
|
||||
// (es. filter = "environment." e subPath = "navigation." -> skippa)
|
||||
if (!subPath.startsWith(filterPath) && !filterPath.startsWith(subPath)) continue;
|
||||
|
||||
traverse(obj[key], subPath);
|
||||
}
|
||||
};
|
||||
|
||||
traverse(self);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ottieni un oggetto con path e valori per una lista specifica di path.
|
||||
* @param {Array<String>} data - Un array contenente i path di Signal K da recuperare.
|
||||
* @returns {Object} Un oggetto JSON con elementi path: value.
|
||||
*/
|
||||
function getFrom(data) {
|
||||
if (!Array.isArray(data)) return {};
|
||||
|
||||
const results = {};
|
||||
for (const path of data) {
|
||||
results[path] = get(path);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init,
|
||||
publish,
|
||||
get,
|
||||
getBySource,
|
||||
getWithFilter,
|
||||
getFrom
|
||||
};
|
||||
53
plugin/config/skSettings.js
Normal file
53
plugin/config/skSettings.js
Normal file
@@ -0,0 +1,53 @@
|
||||
module.exports = {
|
||||
type: 'object',
|
||||
properties: { // impostazioni
|
||||
wthr_update_interval: {
|
||||
type: 'number',
|
||||
title: 'Aggiornamento meteo (in secondi)',
|
||||
description: 'Imposta ogni quanto verranno aggiornati i dati meteo attuali',
|
||||
default: 60, //1m
|
||||
},
|
||||
wthr_longterm_interval: {
|
||||
type: 'number',
|
||||
title: 'Aggiornamenti Previsioni (in minuti)',
|
||||
description: 'Imposta ogni quanti minuti verranno aggiornate le previsioni meteo fino a 7 ore',
|
||||
default: 60, //1h
|
||||
},
|
||||
telemetry_log_interval: {
|
||||
type: 'number',
|
||||
title: 'Registrazione dei dati (in secondi)',
|
||||
description: 'Imposta ogni quanto la telemetria della barca verrà registrata',
|
||||
default: 10, //10sec
|
||||
},
|
||||
telegam_token: {
|
||||
type: 'string',
|
||||
title: 'Telegram Bot Token',
|
||||
description: 'Inserisci il token del tuo bot Telegram per ricevere notifiche e interagire con la tua barca da remoto',
|
||||
default: '',
|
||||
},
|
||||
sensor_code: {
|
||||
type: 'string',
|
||||
title: 'Sensore',
|
||||
description: 'Inserisci un codice identificativo per inviare i dati al server',
|
||||
default: '',
|
||||
},
|
||||
sensor_name: {
|
||||
type: 'string',
|
||||
title: 'Nome Sensore',
|
||||
description: 'Inserisci un nome per il tuo sensore, che verrà visualizzato nel server',
|
||||
default: '',
|
||||
},
|
||||
sensor_interval: {
|
||||
type: 'number',
|
||||
title: 'Aggiornamento dati (in secondi)',
|
||||
description: 'Imposta ogni quanto i dati del sensore verranno inviati al server',
|
||||
default: 60, //1m
|
||||
},
|
||||
reconnect_delay: {
|
||||
type: 'number',
|
||||
title: 'Ritardo di riconnessione (in secondi)',
|
||||
description: 'Imposta il ritardo prima di tentare una nuova connessione al server',
|
||||
default: 10, //10sec
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user