diff --git a/src/mqtt.js b/src/mqtt.js index 6b8d86f..3052b19 100644 --- a/src/mqtt.js +++ b/src/mqtt.js @@ -604,37 +604,6 @@ client.on("connect", () => { // handle message received client.on("message", async (topic, message) => { try { - - // handle node status - if(topic.includes("/stat/!")){ - try { - - // get node id and status - const nodeIdHex = topic.split("/").pop(); - const mqttConnectionState = message.toString(); - - // convert node id hex to int value - const nodeId = convertHexIdToNumericId(nodeIdHex); - - // update mqtt connection state for node - await prisma.node.updateMany({ - where: { - node_id: nodeId, - }, - data: { - mqtt_connection_state: mqttConnectionState, - mqtt_connection_state_updated_at: new Date(), - }, - }); - - // no need to continue with this mqtt message - return; - - } catch(e) { - console.error(e); - } - } - // decode service envelope const envelope = ServiceEnvelope.decode(message); if(!envelope.packet){ @@ -661,6 +630,16 @@ client.on("message", async (topic, message) => { } } + // Update Node MQTT status based on Last Packet Received time + const ret = await prisma.node.updateMany({ + where: { + node_id: convertHexIdToNumericId(envelope.gatewayId), + }, + data: { + mqtt_connection_state_updated_at: new Date(), + }, + }); + // attempt to decrypt encrypted packets const isEncrypted = envelope.packet.encrypted?.length > 0; if(isEncrypted){ diff --git a/src/public/index.html b/src/public/index.html index b6f2340..aff629e 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -1485,6 +1485,30 @@ + +
+ +
Nodes that have not uploaded to MQTT in this time will show as blue icons. Reload to update map.
+ +
+
@@ -1777,6 +1801,19 @@ } } + function getConfigNodesDisconnectedAgeInSeconds() { + const value = localStorage.getItem("config_nodes_disconnected_age_in_seconds"); + return value != null ? parseInt(value) : null; + } + + function setConfigNodesDisconnectedAgeInSeconds(value) { + if(value != null){ + return localStorage.setItem("config_nodes_disconnected_age_in_seconds", value); + } else { + return localStorage.removeItem("config_nodes_disconnected_age_in_seconds"); + } + } + function getConfigNodesOfflineAgeInSeconds() { const value = localStorage.getItem("config_nodes_offline_age_in_seconds"); return value != null ? parseInt(value) : null; @@ -1836,6 +1873,7 @@ isShowingAnnouncement: this.shouldShowAnnouncement(), configNodesMaxAgeInSeconds: window.getConfigNodesMaxAgeInSeconds(), + configNodesDisconnectedAgeInSeconds: window.getConfigNodesDisconnectedAgeInSeconds(), configNodesOfflineAgeInSeconds: window.getConfigNodesOfflineAgeInSeconds(), configWaypointsMaxAgeInSeconds: window.getConfigWaypointsMaxAgeInSeconds(), configNeighboursMaxDistanceInMeters: window.getConfigNeighboursMaxDistanceInMeters(), @@ -2791,6 +2829,9 @@ configNodesMaxAgeInSeconds() { window.setConfigNodesMaxAgeInSeconds(this.configNodesMaxAgeInSeconds); }, + configNodesDisconnectedAgeInSeconds() { + window.setConfigNodesDisconnectedAgeInSeconds(this.configNodesDisconnectedAgeInSeconds); + }, configNodesOfflineAgeInSeconds() { window.setConfigNodesOfflineAgeInSeconds(this.configNodesOfflineAgeInSeconds); }, @@ -3500,12 +3541,15 @@ // icon based on mqtt connection state var icon = iconMqttDisconnected; - if(node.mqtt_connection_state === "online"){ - icon = iconMqttConnected; + const now = moment(); + const configNodesDisconnectedAgeInSeconds = getConfigNodesDisconnectedAgeInSeconds(); + if(configNodesDisconnectedAgeInSeconds){ + if(now.diff(moment(node.mqtt_connection_state_updated_at)) > configNodesDisconnectedAgeInSeconds * 1000){ + icon = iconMqttConnected; + } } // use offline icon for nodes older than configured node offline age - const now = moment(); const configNodesOfflineAgeInSeconds = getConfigNodesOfflineAgeInSeconds(); if(configNodesOfflineAgeInSeconds){ const lastUpdatedAgeInMillis = now.diff(moment(node.updated_at)); @@ -3519,7 +3563,7 @@ icon: icon, tagName: node.node_id, // we want to show online nodes above offline, but without needing to use separate layer groups - zIndexOffset: node.mqtt_connection_state === "online" ? 1000 : -1000, + zIndexOffset: now.diff(moment(node.mqtt_connection_state_updated_at)) > configNodesDisconnectedAgeInSeconds * 1000 ? 1000 : -1000, }).on('click', function(event) { // close tooltip on click to prevent tooltip and popup showing at same time event.target.closeTooltip(); @@ -3937,13 +3981,13 @@ // human friendly connection state var mqttStatus = ""; - var mqttStatusLastUpdated = node.mqtt_connection_state_updated_at ? `(${moment(new Date(node.mqtt_connection_state_updated_at)).fromNow()})` : ""; - if(node.mqtt_connection_state === "online"){ - mqttStatus = `Connected ${mqttStatusLastUpdated}`; - } else if(node.mqtt_connection_state === "offline"){ - mqttStatus = `Disconnected ${mqttStatusLastUpdated}`; + var mqttStatusLastUpdated = node.mqtt_connection_state_updated_at ? `${moment(new Date(node.mqtt_connection_state_updated_at)).fromNow()}` : ""; + if(moment().diff(moment(node.mqtt_connection_state_updated_at)) > getConfigNodesDisconnectedAgeInSeconds() * 1000){ + mqttStatus = `Heard ${mqttStatusLastUpdated}`; + } else if(moment().diff(moment(node.mqtt_connection_state_updated_at)) < getConfigNodesDisconnectedAgeInSeconds() * 1000){ + mqttStatus = `Last Heard ${mqttStatusLastUpdated}`; } else { - mqttStatus = `Disconnected`; + mqttStatus = `Not heard`; } var loraFrequencyRange = getRegionFrequencyRange(node.region_name);