4 Commits
r1.0 ... r1.1

Author SHA1 Message Date
c1328
00c118ab4a Update meshcli_iobroker.js
bugfixes
2026-01-29 00:08:46 +01:00
c1328
aa562b8ab6 Update meshcli_iobroker.js
history html object added
2026-01-28 23:48:16 +01:00
c1328
df58a950bd Update meshcli_iobroker.js
add history function
2026-01-28 23:34:45 +01:00
c1328
0b3c39b964 Update README.md 2026-01-28 20:51:33 +01:00
2 changed files with 131 additions and 6 deletions

View File

@@ -18,7 +18,7 @@ Ziel ist es, Meshtastic-Netzwerke in ioBroker sichtbar und steuerbar zu machen:
send TraceRoute, Ping, Message, u.v.m.
- **Echtzeit-Chat** via MQTT
Nachrichtenempfang (LoRa & MQTT) direkt in ioBroker-Datenpunkten
Nachrichtenempfang und Sendung (LoRa & MQTT) direkt in ioBroker-Datenpunkten
- **Multi-Kanal Support**
Primary/Secondary Channels werden unterstützt

View File

@@ -9,8 +9,8 @@ var mqttPath = 'mqtt.3.msh.*.json.*';
var chats = [
{ name: 'Default', id: 0 },
{ name: 'Puig', id: 1 },
{ name: 'Baleares', id: 2 }
{ name: '<private channel>', id: 1 },
{ name: '<public channel>', id: 2 }
];
@@ -57,9 +57,12 @@ on({id: /^mqtt\.3\.msh\..*\.json\..*$/, change: "any"}, function (obj) {
}
// Save in Chat Channel
const chatMsgPath = '0_userdata.0.Meshtastic.Chats.' + channelIdx + '.lastMessage';
if (getObject('0_userdata.0.Meshtastic.Chats.' + channelIdx)) {
setState(chatMsgPath, `${displayName}: ${text}`, true);
const chatPath = '0_userdata.0.Meshtastic.Chats.' + channelIdx;
if (getObject(chatPath)) {
setState(chatPath + '.lastMessage', `${displayName}: ${text}`, true);
// --- add to history ---
addToHistory(channelIdx, displayName, text);
}
log(`Meshtastic Chat [${channelIdx}]: ${displayName} sagt "${text}"`);
@@ -69,6 +72,49 @@ on({id: /^mqtt\.3\.msh\..*\.json\..*$/, change: "any"}, function (obj) {
}
});
// Trigger für Nachrichten an Kanäle (Chats)
on({id: /^0_userdata\.0\.Meshtastic\.Chats\.\d+\.sendMessage$/, change: "any", ack: false}, function (obj) {
const msg = obj.state.val;
if (!msg || msg === "") return;
const parts = obj.id.split('.');
// parseInt stellt sicher, dass channelId eine Nummer ist
const channelId = parseInt(parts[parts.length - 2]);
log(`Meshtastic: Sende Nachricht an Kanal ${channelId}: ${msg}`);
// In der CLI-Exec wird channelId wieder zum String für den Befehl,
// aber für addToHistory ist es nun die korrekte Nummer.
exec(`/home/iobroker/.local/bin/meshtastic --host ${deviceIp} --ch-index ${channelId} --sendtext "${msg}"`, function (error, result, stderr) {
if (error) {
log(`Fehler beim Senden: ${stderr}`, 'error');
} else {
setState(obj.id, "", true);
// Jetzt passt der Typ für den ersten Parameter (number)
addToHistory(channelId, "ICH (ioBroker)", msg);
}
});
});
// Trigger für Direktnachrichten an einzelne Nodes
on({id: /^0_userdata\.0\.Meshtastic\.Nodes\..*\.command\.sendMessage$/, change: "any", ack: false}, function (obj) {
const msg = obj.state.val;
if (!msg || msg === "") return;
const parts = obj.id.split('.');
const nodeId = parts[parts.length - 3]; // Pfad ist Nodes.ID.command.sendMessage
log(`Meshtastic: Sende Direktnachricht an !${nodeId}: ${msg}`);
exec(`/home/iobroker/.local/bin/meshtastic --host ${deviceIp} --dest "!${nodeId}" --sendtext "${msg}"`, function (error, result, stderr) {
if (error) {
log(`Fehler beim Senden an Node: ${stderr}`, 'error');
} else {
setState(obj.id, "", true);
}
});
});
// ======================================================
// 2. CLI Node Polling + Parsing
// ======================================================
@@ -283,9 +329,88 @@ function createChats() {
},
native: {}
});
// message history as JSON (read-only)
setObject('0_userdata.0.Meshtastic.Chats.' + chatObj.id + '.history', {
type: 'state',
common: {
name: 'Chat Historie JSON',
type: 'string',
role: 'json',
read: true,
write: false
},
native: {}
});
});
}
/**
* Fügt eine Nachricht zum JSON-Verlauf und HTML-String eines Kanals hinzu
* @param {number} channelIdx - Index des Kanals (0, 1, 2...)
* @param {string} senderName - Aufgelöster Name oder ID des Absenders
* @param {string} messageText - Inhalt der Nachricht
*/
function addToHistory(channelIdx, senderName, messageText) {
const basePath = '0_userdata.0.Meshtastic.Chats.' + channelIdx;
const historyPath = basePath + '.history';
const htmlPath = basePath + '.history_html';
const maxEntries = 20; // Anzahl der gespeicherten Nachrichten
// --- 1. JSON HISTORY (für Jarvis JsonTable) ---
let history = [];
if (existsState(historyPath)) {
let currentState = getState(historyPath).val;
try {
history = JSON.parse(currentState) || [];
} catch (e) { history = []; }
}
// Neues Objekt erstellen
const newEntry = {
ts: Date.now(),
time: formatDate(new Date(), "HH:mm"),
from: senderName,
text: messageText
};
// Oben einfügen & Kürzen
history.unshift(newEntry);
if (history.length > maxEntries) history = history.slice(0, maxEntries);
// JSON Speichern
setState(historyPath, JSON.stringify(history), true);
// --- 2. HTML HISTORY (für Jarvis StateHTML / Messenger-Look) ---
let html = '<div style="display:flex; flex-direction:column; gap:10px; font-family:sans-serif;">';
history.forEach(m => {
html += `
<div style="background:rgba(128,128,128,0.1); padding:8px 12px; border-radius:12px; border-left:4px solid #009688; max-width:95%;">
<div style="font-size:0.75em; opacity:0.6; margin-bottom:4px;">
<span style="font-weight:bold; color:#009688;">${m.from}</span> • ${m.time}
</div>
<div style="font-size:0.95em; line-height:1.3; word-wrap:break-word;">
${m.text}
</div>
</div>`;
});
html += '</div>';
// HTML Datenpunkt prüfen und schreiben
if (!existsState(htmlPath)) {
setObject(htmlPath, {
type: 'state',
common: { name: 'Chat Historie HTML', type: 'string', role: 'html', read: true, write: false },
native: {}
});
setTimeout(() => { setState(htmlPath, html, true); }, 200);
} else {
setState(htmlPath, html, true);
}
}
// ======================================================
// 6. Command Listener + CLI Actions