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:
110
plugin/telegram/commands/backuplogs.js
Normal file
110
plugin/telegram/commands/backuplogs.js
Normal file
@@ -0,0 +1,110 @@
|
||||
const fs = require('fs').promises;
|
||||
const fsSync = require('fs');
|
||||
const pth = require('path');
|
||||
const { closeButton } = require('../utility/close');
|
||||
|
||||
const dataDir = pth.join(__dirname, '../../../data/');
|
||||
const PAGE_SIZE = 5;
|
||||
|
||||
/**
|
||||
* Raccoglie ricorsivamente tutti i file nella cartella data/
|
||||
*/
|
||||
async function listDataFiles() {
|
||||
const results = [];
|
||||
|
||||
async function scan(dir, prefix) {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const fullPath = pth.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await scan(fullPath, prefix ? `${prefix}/${entry.name}` : entry.name);
|
||||
} else {
|
||||
const stat = await fs.stat(fullPath);
|
||||
results.push({
|
||||
name: prefix ? `${prefix}/${entry.name}` : entry.name,
|
||||
path: fullPath,
|
||||
size: stat.size,
|
||||
modified: stat.mtime,
|
||||
lines: null // calcolato on-demand
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await scan(dataDir, '');
|
||||
} catch (e) {
|
||||
console.error('[BACKUP] Errore scan:', e.message);
|
||||
}
|
||||
|
||||
return results.sort((a, b) => b.modified - a.modified);
|
||||
}
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
||||
}
|
||||
|
||||
function buildPage(files, page, userMessageId) {
|
||||
const totalPages = Math.ceil(files.length / PAGE_SIZE);
|
||||
const start = page * PAGE_SIZE;
|
||||
const pageFiles = files.slice(start, start + PAGE_SIZE);
|
||||
|
||||
const keyboard = pageFiles.map(f => {
|
||||
const date = new Date(f.modified).toLocaleDateString('it-IT', {
|
||||
day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit'
|
||||
});
|
||||
const label = `${f.name} (${formatSize(f.size)}, ${date})`;
|
||||
// Encode file name as base64-safe identifier (index in full list)
|
||||
const fileIdx = files.indexOf(f);
|
||||
return [{ text: label, callback_data: `bkfile:${fileIdx}:${userMessageId}` }];
|
||||
});
|
||||
|
||||
// Navigation row
|
||||
const navRow = [];
|
||||
if (page > 0) {
|
||||
navRow.push({ text: '<< Prec', callback_data: `bkpage:${page - 1}:${userMessageId}` });
|
||||
}
|
||||
navRow.push({ text: `${page + 1}/${totalPages}`, callback_data: `bknoop:0` });
|
||||
if (page < totalPages - 1) {
|
||||
navRow.push({ text: 'Succ >>', callback_data: `bkpage:${page + 1}:${userMessageId}` });
|
||||
}
|
||||
keyboard.push(navRow);
|
||||
|
||||
// Close button
|
||||
keyboard.push([{ text: '<- Chiudi', callback_data: `close:${userMessageId}` }]);
|
||||
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: 'backup',
|
||||
handler: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const files = await listDataFiles();
|
||||
|
||||
if (files.length === 0) {
|
||||
bot.sendMessage(chatId, 'Nessun file nella cartella data.', {
|
||||
reply_to_message_id: msg.message_id,
|
||||
reply_markup: closeButton(msg.message_id)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const text = `*Backup & Logs*\n\n${files.length} file disponibili nella cartella data.\n_Seleziona un file per info e download._`;
|
||||
const keyboard = buildPage(files, 0, msg.message_id);
|
||||
|
||||
bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: msg.message_id,
|
||||
reply_markup: { inline_keyboard: keyboard }
|
||||
});
|
||||
},
|
||||
|
||||
// Export utilities for callbacks
|
||||
listDataFiles,
|
||||
formatSize,
|
||||
buildPage,
|
||||
PAGE_SIZE
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,58 +1,59 @@
|
||||
const dataHub = require('../../tools/dataHub');
|
||||
const skFlow = require('../../config/skFlow');
|
||||
const { liveMarkup } = require('../utility/live');
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
const logsPaths = [
|
||||
"navigation.position",
|
||||
"navigation.headingTrue",
|
||||
"navigation.speedOverGround",
|
||||
"propulsion.p1.temperature"
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
command: 'data',
|
||||
description: 'Mostra i dati sensori attuali',
|
||||
pattern: /\/data/,
|
||||
execute: async (bot, msg) => {
|
||||
handler: (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const text = formatSensorData();
|
||||
let text = '';
|
||||
|
||||
await bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Aggiorna', callback_data: 'data-refresh' }]
|
||||
]
|
||||
// Telemetria
|
||||
const logs = skFlow.getFrom(logsPaths);
|
||||
text += '*Telemetria di Bordo*\n\n';
|
||||
if (logs && Object.keys(logs).length > 0) {
|
||||
for (const [path, value] of Object.entries(logs)) {
|
||||
const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
||||
text += `*${path}*: ${displayValue}\n`;
|
||||
}
|
||||
} else {
|
||||
text += 'Nessun dato disponibile.\n';
|
||||
}
|
||||
|
||||
// Meteo
|
||||
const weather = skFlow.getWithFilter('meb.forecast');
|
||||
text += '\n*Dati Meteo*\n\n';
|
||||
if (weather && Object.keys(weather).length > 0) {
|
||||
for (const [path, value] of Object.entries(weather)) {
|
||||
const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
||||
text += `*${path}*: ${displayValue}\n`;
|
||||
}
|
||||
} else {
|
||||
text += 'Nessun dato disponibile.\n';
|
||||
}
|
||||
|
||||
// Mare
|
||||
const marine = skFlow.getWithFilter('meb.marine');
|
||||
text += '\n*Dati Meteo del mare*\n\n';
|
||||
if (marine && Object.keys(marine).length > 0) {
|
||||
for (const [path, value] of Object.entries(marine)) {
|
||||
const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
||||
text += `*${path}*: ${displayValue}\n`;
|
||||
}
|
||||
} else {
|
||||
text += 'Nessun dato disponibile.\n';
|
||||
}
|
||||
|
||||
bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: msg.message_id,
|
||||
reply_markup: liveMarkup(msg.message_id, 'data')
|
||||
});
|
||||
},
|
||||
formatSensorData
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,53 +1,53 @@
|
||||
const realtime = require('../../realtime/core.js');
|
||||
const { config } = require('../../config.js');
|
||||
const recorder = require('../../cores/logs.local');
|
||||
const { closeButton } = require('../utility/close');
|
||||
|
||||
module.exports = {
|
||||
command: 'logs',
|
||||
description: 'Mostra lo stato della registrazione dati in tempo reale',
|
||||
pattern: /\/logs/,
|
||||
execute: async (bot, msg, { app }) => {
|
||||
handler: async (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
try {
|
||||
const stats = realtime.getStats();
|
||||
const consoleUrl = config.cloudUrl || 'https://console.mebboat.it';
|
||||
const logs = await recorder.listLogs();
|
||||
|
||||
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' }]
|
||||
]
|
||||
}
|
||||
if (!logs || logs.length === 0) {
|
||||
bot.sendMessage(chatId, 'Nessun file di log disponibile.', {
|
||||
reply_to_message_id: msg.message_id,
|
||||
reply_markup: closeButton(msg.message_id)
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[Telegram] Errore comando /logs:", error);
|
||||
bot.sendMessage(chatId, `❌ Errore: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const session = recorder.getSession();
|
||||
let text = '*Registrazioni dei Log*\n\n';
|
||||
|
||||
if (session) {
|
||||
text += `in corso: *${session.name}*\n`;
|
||||
text += `${session.elements} dati raccolti ogni ${session.delay}s\n\n`;
|
||||
}
|
||||
|
||||
text += `${logs.length} file disponibili:\n`;
|
||||
text += '_Selezionane uno per scaricarlo_';
|
||||
|
||||
// Bottoni per ogni file
|
||||
const keyboard = logs.map(log => {
|
||||
const date = new Date(log.created).toLocaleDateString('it-IT', {
|
||||
day: '2-digit', month: '2-digit', year: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit'
|
||||
});
|
||||
|
||||
const isActive = session && session.name === log.name;
|
||||
const label = isActive ? `🔴 ${log.name} *[IN CORSO, NON DISPONIBILE]*` : `${date})`;
|
||||
const callback = isActive ? `logbusy:${log.name}` : `logfile:${log.name}:${msg.message_id}`;
|
||||
|
||||
return [{ text: label, callback_data: callback }];
|
||||
});
|
||||
|
||||
// Aggiungi il bottone chiudi
|
||||
keyboard.push([{ text: '<- Chiudi', callback_data: `close:${msg.message_id}` }]);
|
||||
|
||||
bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: msg.message_id,
|
||||
reply_markup: { inline_keyboard: keyboard }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
30
plugin/telegram/commands/marine.js
Normal file
30
plugin/telegram/commands/marine.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const skFlow = require('../../config/skFlow');
|
||||
const { liveMarkup } = require('../utility/live');
|
||||
|
||||
module.exports = {
|
||||
command: 'marine',
|
||||
handler: (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const data = skFlow.getWithFilter('meb.marine');
|
||||
|
||||
let text = '';
|
||||
|
||||
if (!data || Object.keys(data).length === 0) {
|
||||
text = 'Nessun dato sul mare disponibile.';
|
||||
} else {
|
||||
text = '*Dati Meteo del mare*\n\n';
|
||||
for (const [path, value] of Object.entries(data)) {
|
||||
const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
||||
//TODO: ADD units
|
||||
//TODO: Formattare meglio i path
|
||||
text += `*${path}*: ${displayValue}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: msg.message_id,
|
||||
reply_markup: liveMarkup(msg.message_id, 'marine')
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
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' });
|
||||
}
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
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' }
|
||||
]
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
17
plugin/telegram/commands/start.js
Normal file
17
plugin/telegram/commands/start.js
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
command: 'start',
|
||||
handler: (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
bot.setMyCommands([
|
||||
{ command: 'data', description: 'Mostra tutti i dati' },
|
||||
{ command: 'logs', description: 'Registrazioni logs' },
|
||||
{ command: 'weather', description: 'Mostra i dati meteo' },
|
||||
{ command: 'marine', description: 'Mostra i dati del mare' },
|
||||
{ command: 'backup', description: 'Backup logs - lista file nella cartella data' },
|
||||
// { command: 'start', description: 'Avvia il bot e configura i comandi' }
|
||||
]);
|
||||
|
||||
bot.sendMessage(chatId, 'Benvenuto nel bot MEB!');
|
||||
}
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
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
|
||||
};
|
||||
@@ -1,47 +0,0 @@
|
||||
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' });
|
||||
}
|
||||
};
|
||||
@@ -1,84 +1,29 @@
|
||||
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;
|
||||
}
|
||||
const skFlow = require('../../config/skFlow');
|
||||
const { liveMarkup } = require('../utility/live');
|
||||
|
||||
module.exports = {
|
||||
command: 'weather',
|
||||
description: 'Mostra i dati meteo attuali',
|
||||
pattern: /\/weather/,
|
||||
execute: async (bot, msg) => {
|
||||
handler: (bot, msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const text = formatWeatherData();
|
||||
const data = skFlow.getWithFilter('meb.forecast');
|
||||
|
||||
await bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: 'Aggiorna', callback_data: 'weather-refresh' }]
|
||||
]
|
||||
let text = '';
|
||||
|
||||
if (!data || Object.keys(data).length === 0) {
|
||||
text = 'Nessun dato meteo disponibile.';
|
||||
} else {
|
||||
text = '*Dati Meteo*\n\n';
|
||||
for (const [path, value] of Object.entries(data)) {
|
||||
const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
||||
//TODO: ADD units
|
||||
text += `*${path}*: ${displayValue}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
bot.sendMessage(chatId, text, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: msg.message_id,
|
||||
reply_markup: liveMarkup(msg.message_id, 'weather')
|
||||
});
|
||||
},
|
||||
formatWeatherData
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user