Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion server/monitor-types/mqtt.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const { log, UP } = require("../../src/util");
const mqtt = require("mqtt");
const jsonata = require("jsonata");
const { regex } = require("nostr-tools/nip30");

Check warning on line 5 in server/monitor-types/mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

'regex' is assigned a value but never used
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MQtt and nostr don't seem really connected.
Can you add a comment explaining this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong import, auto import from vs-code ...


class MqttMonitorType extends MonitorType {
name = "mqtt";
Expand Down Expand Up @@ -48,6 +49,19 @@
}
}

/**
* This function checks if the actual MQTT topic matches the subscribed topic.
* To handle MQTT wildcards, it converts the subscribed topic into a regex pattern.
* @param {string} subcribedTopic MQTT subscribed topic
* @returns {RegExp} RegExp if the actual topic matches the subscribed topic
*/
static mqttTopicRegex(subcribedTopic) {
subcribedTopic = subcribedTopic.replace(/([$.|?*{}()\[\]\\])/g, '\\$1'); // Escape special regex chars except + and #

Check failure on line 59 in server/monitor-types/mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Strings must use doublequote

Check failure on line 59 in server/monitor-types/mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Unnecessary escape character: \[
subcribedTopic = subcribedTopic.replace(/\+/g, '[^/]+'); // Replace + with regex for one or more characters except slash

Check failure on line 60 in server/monitor-types/mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Strings must use doublequote
Copy link
Collaborator

@CommanderStorm CommanderStorm Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this quite works when the start of the string is "+", or am I misunderstanding the syntax here wrong?

This would meann that \+ => \[^/]+ which likely won't work as you want it to.

subcribedTopic = subcribedTopic.replace(/#/g, '.*'); // Replace # with regex for zero or more characters until next slash

Check failure on line 61 in server/monitor-types/mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Strings must use doublequote
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think \# would also be affected under the current implementation.
Is this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, i don't know to much of the js regex engine but # is not considered as special char

I can add a test to check it

return new RegExp(`^${subcribedTopic}$`);;

Check failure on line 62 in server/monitor-types/mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Unnecessary semicolon
}

/**
* Connect to MQTT Broker, subscribe to topic and receive message as String
* @param {string} hostname Hostname / address of machine to test
Expand Down Expand Up @@ -89,12 +103,15 @@
clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8)
});

var regexTopic;

Check failure on line 106 in server/monitor-types/mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Unexpected var, use let or const instead
Copy link
Preview

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use 'let' instead of 'var' for block-scoped variable declaration following modern JavaScript best practices.

Suggested change
var regexTopic;
let regexTopic;

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOPE here explicit usage of var for global scope of the instance


client.on("connect", () => {
log.debug("mqtt", "MQTT connected");

try {
client.subscribe(topic, () => {
log.debug("mqtt", "MQTT subscribed to topic");
regexTopic = MqttMonitorType.mqttTopicRegex(topic);
});
} catch (e) {
client.end();
Expand All @@ -110,7 +127,7 @@
});

client.on("message", (messageTopic, message) => {
if (messageTopic === topic) {
if (regexTopic.test(messageTopic)) {
client.end();
clearTimeout(timeoutID);
resolve(message.toString("utf8"));
Expand Down
42 changes: 42 additions & 0 deletions test/backend-test/test-mqtt.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,46 @@
new Error("Message received but value is not equal to expected value, value was: [present]")
);
});

test('should match exact topic without wildcards', () => {

Check failure on line 104 in test/backend-test/test-mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Strings must use doublequote
const regex = MqttMonitorType.mqttTopicRegex('sensor/temperature');

Check failure on line 105 in test/backend-test/test-mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Strings must use doublequote
assert.ok(regex.test('sensor/temperature') === true);

Check failure on line 106 in test/backend-test/test-mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Strings must use doublequote
assert.ok(regex.test('sensor/humidity') === false);

Check failure on line 107 in test/backend-test/test-mqtt.js

View workflow job for this annotation

GitHub Actions / check-linters

Strings must use doublequote
});

test('should match exact topic without wildcards but with special characters', () => {
const regex = MqttMonitorType.mqttTopicRegex('sensor.pomme/temperature');
assert.ok(regex.test('sensor.pomme/temperature') === true);
assert.ok(regex.test('sensor.pomme/humidity') === false);
});

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a testcase specifically for the escaping of #, + and the other regex characters.

test('should match + wildcard for single level', () => {
const regex = MqttMonitorType.mqttTopicRegex('sensor/+/temperature');
assert.ok(regex.test('sensor/room1/temperature') === true)
assert.ok(regex.test('sensor/room2/temperature') === true)
assert.ok(regex.test('sensor/room1/humidity') === false)
assert.ok(regex.test('sensor/temperature') === false)
});

test('should match # wildcard for multi-level', () => {
const regex = MqttMonitorType.mqttTopicRegex('sensor/#');
assert.ok(regex.test('sensor/room1') === true);
assert.ok(regex.test('sensor/room1/temperature') === true);
assert.ok(regex.test('sensor/') === true);
assert.ok(regex.test('actuator/room1') === false);
});

test('should combine + and # wildcards', () => {
const regex = MqttMonitorType.mqttTopicRegex('sensor/+/status/#');
assert.ok(regex.test('sensor/room1/status/online') === true);
assert.ok(regex.test('sensor/room2/status/offline/extra') === true);
assert.ok(regex.test('sensor/status') === false);
assert.ok(regex.test('sensor/room1') === false);
});

test('should escape special regex characters in topic', () => {
const regex = MqttMonitorType.mqttTopicRegex('some.topic/+/value$');
assert.ok(regex.test('some.topic/abc/value$') === true);
assert.ok(regex.test('some.topic/abc/value') === false);
});
});
Loading