Aggiunto collegamento al server

This commit is contained in:
Giuseppe Raffa
2026-03-11 15:25:03 +01:00
parent c37f30e4ea
commit 41f33ce181
51 changed files with 3088 additions and 4414 deletions

View File

@@ -0,0 +1,57 @@
const dataHub = require('../../tools/dataHub');
function formatSensorData() {
const sensorSnapshot = dataHub.getSensorData();
const data = { timestamp: new Date().toISOString(), ...(sensorSnapshot || {}) };
let output = `📊 *Dashboard Sensori*\n`;
output += `_Ultimo aggiornamento: ${new Date().toLocaleTimeString('it-IT')}_\n\n`;
let isDataEmpty = true;
for (const [key, value] of Object.entries(data)) {
if (key === 'timestamp') continue;
isDataEmpty = false;
let formattedKey = key.replace(/_/g, ' ');
// Prima lettera maiuscola
formattedKey = formattedKey.charAt(0).toUpperCase() + formattedKey.slice(1);
const formattedValue = (value !== null && value !== undefined)
? (typeof value === 'number' ? value.toFixed(2) : value)
: 'N/A';
output += `🔹 *${formattedKey}:* ${formattedValue}\n`;
}
if (isDataEmpty) {
output += `_Nessun dato configurato o letto. Controlla sensors.references.json_\n`;
}
return output;
}
module.exports = {
command: 'dashboard',
description: 'Mostra i sensori live (dal file references)',
pattern: /\/dashboard/,
execute: async (bot, msg) => {
const chatId = msg.chat.id;
const text = formatSensorData();
await bot.sendMessage(chatId, text, {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{ text: "⚡️ Avvia Live Tracker (2s)", callback_data: 'dashboard-live-start' }
],
[
{ text: "🔄 Ricarica Dati", callback_data: 'dashboard-refresh' }
]
]
}
});
},
formatSensorData // Esportato per riuso nel refresh e nel live
};

View File

@@ -0,0 +1,58 @@
const dataHub = require('../../tools/dataHub');
/**
* Formatta i dati sensore in un messaggio Telegram leggibile.
* @returns {string} Testo formattato Markdown
*/
function formatSensorData() {
const sensors = dataHub.getSensorData();
if (!sensors) {
return 'Nessun dato sensore disponibile.\nI sensori potrebbero non essere ancora attivi.';
}
let text = '*Dati Sensori*\n';
text += `_${new Date().toLocaleTimeString('it-IT')}_\n\n`;
let hasData = false;
for (const [key, value] of Object.entries(sensors)) {
if (key.startsWith('_')) continue; // Skip campi interni
hasData = true;
let label = key.replace(/_/g, ' ');
label = label.charAt(0).toUpperCase() + label.slice(1);
const formatted = (value !== null && value !== undefined)
? (typeof value === 'number' ? value.toFixed(2) : String(value))
: 'N/A';
text += `*${label}:* ${formatted}\n`;
}
if (!hasData) {
text += '_Nessun dato configurato. Controlla sensors.references.json_\n';
}
return text;
}
module.exports = {
command: 'data',
description: 'Mostra i dati sensori attuali',
pattern: /\/data/,
execute: async (bot, msg) => {
const chatId = msg.chat.id;
const text = formatSensorData();
await bot.sendMessage(chatId, text, {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: 'Aggiorna', callback_data: 'data-refresh' }]
]
}
});
},
formatSensorData
};

View File

@@ -0,0 +1,84 @@
const dataHub = require('../../tools/dataHub');
/**
* Formatta tutti i dati (sensori + meteo) per il live tracker.
* @returns {string} Testo formattato Markdown
*/
function formatLiveData() {
const sensors = dataHub.getSensorData();
const { forecast, sea } = dataHub.getWeatherData();
let text = '*LIVE - Dati Completi*\n';
text += `_${new Date().toLocaleTimeString('it-IT')}_\n\n`;
// Sezione sensori
if (sensors) {
text += '*Sensori:*\n';
for (const [key, value] of Object.entries(sensors)) {
if (key.startsWith('_')) continue;
let label = key.replace(/_/g, ' ');
label = label.charAt(0).toUpperCase() + label.slice(1);
const val = (value !== null && value !== undefined)
? (typeof value === 'number' ? value.toFixed(2) : String(value))
: 'N/A';
text += ` ${label}: ${val}\n`;
}
} else {
text += '_Nessun dato sensore disponibile_\n';
}
// Sezione meteo (compatta)
if (forecast) {
text += '\n*Meteo:*\n';
const parts = [];
if (forecast.temperature !== null && forecast.temperature !== undefined) {
parts.push(`Temp: ${forecast.temperature}C`);
}
if (forecast.humidity !== null && forecast.humidity !== undefined) {
parts.push(`Um: ${forecast.humidity}%`);
}
if (forecast.wind?.speed !== null && forecast.wind?.speed !== undefined) {
parts.push(`Vento: ${forecast.wind.speed}km/h`);
}
text += ` ${parts.join(' | ')}\n`;
}
if (sea?.waves) {
const seaParts = [];
if (sea.waves.height !== null && sea.waves.height !== undefined) {
seaParts.push(`Onde: ${sea.waves.height}m`);
}
if (sea.waves.period !== null && sea.waves.period !== undefined) {
seaParts.push(`Per: ${sea.waves.period}s`);
}
if (seaParts.length > 0) {
text += ` ${seaParts.join(' | ')}\n`;
}
}
return text;
}
module.exports = {
command: 'live',
description: 'Dati live (meteo + sensori) con aggiornamento automatico',
pattern: /\/live/,
execute: async (bot, msg) => {
const chatId = msg.chat.id;
const text = formatLiveData();
await bot.sendMessage(chatId, text, {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: 'Avvia Live (2s)', callback_data: 'live-start' }],
[{ text: 'Aggiorna', callback_data: 'live-refresh' }]
]
}
});
},
formatLiveData
};

View File

@@ -0,0 +1,53 @@
const realtime = require('../../realtime/core.js');
const { config } = require('../../config.js');
module.exports = {
command: 'logs',
description: 'Mostra lo stato della registrazione dati in tempo reale',
pattern: /\/logs/,
execute: async (bot, msg, { app }) => {
const chatId = msg.chat.id;
try {
const stats = realtime.getStats();
const consoleUrl = config.cloudUrl || 'https://console.mebboat.it';
let statusIcon = '🔴';
if (stats.status === 'connected') statusIcon = '🟢';
else if (stats.status === 'error') statusIcon = '🟡';
let text = `📊 *Registrazione Dati Realtime*\n\n`;
text += `Stato: ${statusIcon} *${stats.status}*\n`;
text += `Sensore: \`${stats.sensorID}\`\n`;
text += `Messaggi inviati: *${stats.sent}*\n`;
text += `Frequenza: ogni *${stats.sentEveryMLS / 1000}s*\n`;
if (stats.buffered > 0) {
text += `⚠️ Messaggi in buffer: *${stats.buffered}*\n`;
}
if (stats.reconnections > 0) {
text += `Riconnessioni: ${stats.reconnections}\n`;
}
if (stats.firstSent) {
text += `\nPrimo invio: ${stats.firstSent}\n`;
}
text += `\n_I dati vengono inviati automaticamente al server ogni secondo._`;
text += `\n_Consulta i log storici sulla console:_`;
await bot.sendMessage(chatId, text, {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: '📈 Apri Console Log', url: `${consoleUrl}/logs` }],
[{ text: '🔄 Aggiorna Stato', callback_data: 'logs-refresh' }]
]
}
});
} catch (error) {
console.error("[Telegram] Errore comando /logs:", error);
bot.sendMessage(chatId, `❌ Errore: ${error.message}`);
}
}
};

View File

@@ -0,0 +1,24 @@
const realtime = require('../../realtime/core.js');
module.exports = {
command: 'realtime',
description: 'Dettagli della connessione realtime',
pattern: /\/realtime/,
execute: async (bot, msg) => {
const stats = realtime.getStats();
const statusEmoji = stats.status === 'connected' ? '🟢' : '🔴';
let message = `*Connessione Realtime* ${statusEmoji}\n\n`;
message += `*ID Sensore:* ${stats.sensorID}\n`;
message += `*Stato:* ${stats.status}\n`;
message += `*Messaggi inviati:* ${stats.sent}\n`;
message += `*Riconnessioni:* ${stats.reconnections}\n`;
message += `*Frequenza:* ${stats.sentEveryMLS}ms\n`;
if (stats.firstSent) {
message += `*Primo invio:* ${new Date(stats.firstSent).toLocaleString()}\n`;
}
await bot.sendMessage(msg.chat.id, message, { parse_mode: 'Markdown' });
}
};

View File

@@ -0,0 +1,20 @@
module.exports = {
command: 'settings',
description: 'Mostra le impostazioni del Computer di Bordo',
pattern: /\/settings/,
execute: async (bot, msg) => {
const chatId = msg.chat.id;
await bot.sendMessage(chatId, "*Configurazione Computer di Bordo*\nScegli quali parametri modificare:", {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[
{ text: "Meteo", callback_data: 'set-meteo' },
{ text: "Batterie", callback_data: 'set-batteries' }
]
]
}
});
}
};

View File

@@ -0,0 +1,40 @@
const realtime = require('../../realtime/core.js');
function createSessionMenu(app) {
const weatherActive = app.mebPlugin && app.mebPlugin.isPollingActive ? app.mebPlugin.isPollingActive() : false;
const realtimeStats = realtime.getStats();
const realtimeConnected = realtimeStats.isConnected;
return {
reply_markup: {
inline_keyboard: [
[
{ text: weatherActive ? "Meteo: 🟢 ON (Premi per fermare)" : "Meteo: 🔴 OFF (Premi per avviare)", callback_data: 'session-weather-toggle' }
],
[
{ text: realtimeConnected ? "Realtime: 🟢 Connesso" : "Realtime: 🔴 Disconnesso", callback_data: 'session-realtime-info' }
],
[
{ text: "🔄", callback_data: 'session-refresh' },
{ text: "⚙️ ⛅️ (meteo)", callback_data: 'set-meteo' }
]
]
}
};
}
module.exports = {
command: 'session',
description: 'Verifica le attività di Meteo e Realtime',
pattern: /\/session/,
execute: async (bot, msg, { app }) => {
const chatId = msg.chat.id;
const msgText = `*Servizi*\n\n`;
await bot.sendMessage(chatId, msgText, {
parse_mode: 'Markdown',
...createSessionMenu(app)
});
},
createSessionMenu
};

View File

@@ -0,0 +1,47 @@
const realtime = require('../../realtime/core.js');
module.exports = {
command: 'structure',
description: 'Mostra la struttura dati del plugin',
pattern: /\/structure/,
execute: async (bot, msg) => {
const chatId = msg.chat.id;
const rules = realtime.getSensorRules();
if (!rules) {
return bot.sendMessage(chatId, 'Nessuna configurazione sensori caricata.');
}
let text = `*Struttura Dati Plugin*\n`;
text += `Versione: \`${rules.version}\`\n`;
text += `Attivo: ${rules.isActive ? 'Si' : 'No'}\n`;
text += `Collezioni: ${rules.items?.length || 0}\n\n`;
if (rules.items) {
for (const item of rules.items) {
text += `*${item.collection}*\n`;
text += ` Path: \`${item.main_path}\`\n`;
if (item.elements && Array.isArray(item.elements)) {
for (const element of item.elements) {
const { subelements, ...fields } = element;
const [name, subPath] = Object.entries(fields)[0];
text += ` - ${name} -> \`${item.main_path}.${subPath}\`\n`;
if (subelements && Array.isArray(subelements)) {
for (const sub of subelements) {
const [sName, sPath] = Object.entries(sub)[0];
text += ` - ${sName} -> \`${item.main_path}.${subPath}.${sPath}\`\n`;
}
}
}
} else {
text += ` (valore singolo)\n`;
}
text += `\n`;
}
}
await bot.sendMessage(chatId, text, { parse_mode: 'Markdown' });
}
};

View File

@@ -0,0 +1,84 @@
const dataHub = require('../../tools/dataHub');
/**
* Formatta i dati meteo in un messaggio Telegram leggibile.
* @returns {string} Testo formattato Markdown
*/
function formatWeatherData() {
const { forecast, sea } = dataHub.getWeatherData();
if (!forecast && !sea) {
return 'Nessun dato meteo disponibile.\nIl polling potrebbe non essere ancora partito.';
}
let text = '*Meteo Attuale*\n';
text += `_${new Date().toLocaleTimeString('it-IT')}_\n\n`;
if (forecast) {
if (forecast.temperature !== null && forecast.temperature !== undefined) {
text += `Temperatura: *${forecast.temperature}*C\n`;
}
if (forecast.humidity !== null && forecast.humidity !== undefined) {
text += `Umidita: *${forecast.humidity}*%\n`;
}
if (forecast.pressure !== null && forecast.pressure !== undefined) {
text += `Pressione: *${forecast.pressure}* hPa\n`;
}
if (forecast.rain !== null && forecast.rain !== undefined) {
text += `Pioggia: *${forecast.rain}* mm\n`;
}
if (forecast.wind) {
text += `\nVento:\n`;
if (forecast.wind.speed !== null && forecast.wind.speed !== undefined) {
text += ` Velocita: *${forecast.wind.speed}* km/h\n`;
}
if (forecast.wind.direction !== null && forecast.wind.direction !== undefined) {
text += ` Direzione: *${forecast.wind.direction}*\n`;
}
if (forecast.wind.gusts !== null && forecast.wind.gusts !== undefined) {
text += ` Raffiche: *${forecast.wind.gusts}* km/h\n`;
}
}
}
if (sea) {
text += `\nMare:\n`;
if (sea.waves) {
if (sea.waves.height !== null && sea.waves.height !== undefined) {
text += ` Altezza onde: *${sea.waves.height}* m\n`;
}
if (sea.waves.period !== null && sea.waves.period !== undefined) {
text += ` Periodo: *${sea.waves.period}* s\n`;
}
if (sea.waves.direction !== null && sea.waves.direction !== undefined) {
text += ` Direzione: *${sea.waves.direction}*\n`;
}
}
if (sea.temperature !== null && sea.temperature !== undefined) {
text += ` Temp. acqua: *${sea.temperature}*C\n`;
}
}
return text;
}
module.exports = {
command: 'weather',
description: 'Mostra i dati meteo attuali',
pattern: /\/weather/,
execute: async (bot, msg) => {
const chatId = msg.chat.id;
const text = formatWeatherData();
await bot.sendMessage(chatId, text, {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: 'Aggiorna', callback_data: 'weather-refresh' }]
]
}
});
},
formatWeatherData
};