diff --git a/prisma/migrations/20240331045118_add_mqtt_connection_state_columns_to_nodes_table/migration.sql b/prisma/migrations/20240331045118_add_mqtt_connection_state_columns_to_nodes_table/migration.sql
new file mode 100644
index 0000000..9ca74de
--- /dev/null
+++ b/prisma/migrations/20240331045118_add_mqtt_connection_state_columns_to_nodes_table/migration.sql
@@ -0,0 +1,3 @@
+-- AlterTable
+ALTER TABLE `nodes` ADD COLUMN `mqtt_connection_state` VARCHAR(191) NULL,
+ ADD COLUMN `mqtt_connection_state_updated_at` DATETIME(3) NULL;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 437247d..754457f 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -36,6 +36,9 @@ model Node {
neighbours Json?
neighbours_updated_at DateTime?
+ mqtt_connection_state String?
+ mqtt_connection_state_updated_at DateTime?
+
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
diff --git a/src/mqtt.js b/src/mqtt.js
index b84d60d..f330c10 100644
--- a/src/mqtt.js
+++ b/src/mqtt.js
@@ -225,6 +225,36 @@ client.on("connect", () => {
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 = BigInt('0x' + nodeIdHex.replaceAll("!", ""));
+
+ // 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){
diff --git a/src/public/index.html b/src/public/index.html
index bf72746..f40edde 100644
--- a/src/public/index.html
+++ b/src/public/index.html
@@ -2175,9 +2175,19 @@
function getTooltipContentForNode(node) {
+ // 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 = `Online ${mqttStatusLastUpdated}`;
+ } else if(node.mqtt_connection_state === "offline"){
+ mqttStatus = `Offline ${mqttStatusLastUpdated}`;
+ }
+
var tooltip = `
` +
`${node.long_name}` +
`
Short Name: ${node.short_name}` +
+ `
MQTT Status: ${mqttStatus}` +
`
Role: ${node.role_name}` +
`
Hardware: ${node.hardware_model_name}`;