From 66ec2aebeb0bf7239d962585e6df02e167ded607 Mon Sep 17 00:00:00 2001 From: silent <20176737+s1len7@users.noreply.github.com> Date: Thu, 29 Oct 2026 22:23:53 +0900 Subject: [PATCH 1/4] feat: add useHostname option to show the mDNS hostname instead of the IP address The URL and QR code always use the first IPv4 address, which can change with DHCP and is harder to type than a name. With useHostname enabled, node_helper asks the local network over mDNS (reverse PTR query via multicast-dns) for the hostname of each address and falls back to the address when nobody answers within 1.5 seconds. The frontend now regenerates the QR code when the address changes and no longer generates one for the placeholder before the address is known, so a delayed hostname answer cannot leave a stale QR code. --- MMM-Remote-Control.js | 17 ++++---- README.md | 3 +- config.schema.json | 4 ++ cspell.config.json | 2 + node_helper.js | 60 ++++++++++++++++++++++++++- package.json | 1 + tests/unit/socketNotification.test.js | 13 ++++++ 7 files changed, 90 insertions(+), 10 deletions(-) diff --git a/MMM-Remote-Control.js b/MMM-Remote-Control.js index 1ef8c7e3..f62a03ea 100644 --- a/MMM-Remote-Control.js +++ b/MMM-Remote-Control.js @@ -21,6 +21,7 @@ Module.register('MMM-Remote-Control', { showQRCode: true, qrCodeSize: 150, qrCodePosition: 'above', // "below", "above", or "replace" + useHostname: false, // Show the mDNS hostname (e.g. "mirror.local") instead of the IP address }, // Define start sequence. @@ -63,7 +64,7 @@ Module.register('MMM-Remote-Control', { Log.debug(`${this.name} received a module notification: ${notification} from sender: ${sender}`) switch (notification) { case 'DOM_OBJECTS_CREATED': - this.sendSocketNotification('REQUEST_DEFAULT_SETTINGS') + this.sendSocketNotification('REQUEST_DEFAULT_SETTINGS', { useHostname: this.config.useHostname }) this.sendCurrentData() break @@ -83,6 +84,10 @@ Module.register('MMM-Remote-Control', { break case 'IP_ADDRESSES': + // A different address changes the URL, so the QR code has to be generated again + if (payload.join() !== this.addresses.join()) { + this.qrCodeDataUrl = null + } this.addresses = payload if (this.data.position) { this.updateDom() @@ -301,16 +306,14 @@ Module.register('MMM-Remote-Control', { getDom() { const wrapper = document.createElement('div') let portToShow - if (this.addresses.length === 0) { - this.addresses = ['ip-of-your-mirror'] - } + const hasAddress = this.addresses.length > 0 switch (this.port) { case '': case '8080': portToShow = ':8080'; break case '80': portToShow = ''; break default: portToShow = `:${this.port}`; break } - const url = `http://${this.addresses[0]}${portToShow}/remote.html` + const url = `http://${hasAddress ? this.addresses[0] : 'ip-of-your-mirror'}${portToShow}/remote.html` // Show QR code if enabled if (this.config.showQRCode) { @@ -327,8 +330,8 @@ Module.register('MMM-Remote-Control', { } } - // Request QR code generation if not already done - if (!this.qrCodeDataUrl) { + // Request QR code generation if not already done (not for the placeholder before the address is known) + if (!this.qrCodeDataUrl && hasAddress) { this.sendSocketNotification('GENERATE_QR_CODE', { url, size: this.config.qrCodeSize, diff --git a/README.md b/README.md index 36542993..fb734623 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ Add the module to the `modules` array in your `config.js`. Set a `position` to d // showNotificationMenu: false, // Disable the Notification menu // showQRCode: true, // Display a QR code for mobile access (default: true) // qrCodeSize: 150, // QR code size in pixels (default: 150) - // qrCodePosition: "above" // "below", "above" (default), or "replace" + // qrCodePosition: "above", // "below", "above" (default), or "replace" + // useHostname: false, // Show the mDNS hostname (e.g. "mirror.local") instead of the IP address (default: false) } }, ``` diff --git a/config.schema.json b/config.schema.json index 8af4c4e0..21257ca5 100644 --- a/config.schema.json +++ b/config.schema.json @@ -34,6 +34,10 @@ "enum": ["below", "above", "replace"], "description": "Place the QR code below or above the URL, or show it instead" }, + "useHostname": { + "type": "boolean", + "description": "Show the mDNS hostname (e.g. mirror.local) instead of the IP address in the URL and QR code" + }, "secureEndpoints": { "type": "boolean", "description": "Require an API key for protected endpoints when enabled" diff --git a/cspell.config.json b/cspell.config.json index 209b6088..2fa3e1e5 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -4,6 +4,7 @@ "words": [ "Andoramb", "apikey", + "arpa", "articlelessdetails", "articlemoredetails", "articlenext", @@ -45,6 +46,7 @@ "LOCKSTRING", "lockstrings", "magicmirror", + "mdns", "mmrc", "MODULEAPI", "MONITOROFF", diff --git a/node_helper.js b/node_helper.js index ed373a14..e06098d0 100644 --- a/node_helper.js +++ b/node_helper.js @@ -1113,6 +1113,55 @@ module.exports = NodeHelper.create({ return addresses }, + /** + * Look up the mDNS hostname of each address, keeping the address when there is no answer + * @param {string[]} addresses - IPv4 addresses + * @returns {Promise} Hostnames (e.g. "mirror.local") or the original addresses + */ + getHostnames(addresses) { + return Promise.all(addresses.map(address => this.getHostname(address))) + }, + + /** + * Ask the local network over mDNS (reverse PTR query) which hostname belongs to an IPv4 address + * @param {string} address - IPv4 address + * @returns {Promise} Hostname, or the address if nobody answers within the timeout + */ + getHostname(address) { + return new Promise((resolve) => { + let mdns + try { + mdns = require('multicast-dns')() + } + catch (error) { + Log.warn(`mDNS hostname lookup unavailable: ${error.message}`) + resolve(address) + return + } + + const name = `${address.split('.').reverse().join('.')}.in-addr.arpa` + let done = false + const finish = (hostname) => { + if (done) return + done = true + clearTimeout(timer) + mdns.destroy() + resolve(hostname) + } + const timer = setTimeout(() => finish(address), 1500) + + mdns.on('response', (response) => { + const answer = response.answers.find(record => record.type === 'PTR' && record.name === name) + if (answer) finish(answer.data) + }) + mdns.on('error', (error) => { + Log.warn(`mDNS hostname lookup for ${address} failed: ${error.message}`) + finish(address) + }) + mdns.query({ questions: [{ name, type: 'PTR' }] }) + }) + }, + /** * Handle socket notifications from the frontend module * @param {string} notification - Notification type @@ -1135,8 +1184,15 @@ module.exports = NodeHelper.create({ break case 'REQUEST_DEFAULT_SETTINGS': - // module started, answer with current ip addresses - this.sendSocketNotification('IP_ADDRESSES', this.getIpAddresses()) + // module started, answer with current ip addresses (or their hostnames if requested) + if (payload?.useHostname) { + this.getHostnames(this.getIpAddresses()).then((hostnames) => { + this.sendSocketNotification('IP_ADDRESSES', hostnames) + }) + } + else { + this.sendSocketNotification('IP_ADDRESSES', this.getIpAddresses()) + } this.sendSocketNotification('LOAD_PORT', this.configOnHd.port || '') // check if we have got saved default settings this.loadDefaultSettings() diff --git a/package.json b/package.json index 6b935f01..afdb1306 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "acorn": "^8.19.0", "dompurify": "^3.4.17", "marked": "^18.1.1", + "multicast-dns": "^7.2.5", "qrcode": "^1.5.4", "simple-git": "^4.0.2", "swagger-ui-dist": "^5.34.0" diff --git a/tests/unit/socketNotification.test.js b/tests/unit/socketNotification.test.js index 4d5cc579..94bcfec9 100644 --- a/tests/unit/socketNotification.test.js +++ b/tests/unit/socketNotification.test.js @@ -104,6 +104,19 @@ describe('socketNotificationReceived', () => { assert.ok(Array.isArray(ipNotification.payload)) }) + test('sends hostnames as IP_ADDRESSES on REQUEST_DEFAULT_SETTINGS with useHostname', async () => { + const helper = freshHelper() + helper.getIpAddresses = () => ['192.168.0.7'] + helper.getHostnames = async addresses => addresses.map(() => 'mirror.local') + helper.socketNotificationReceived = helperFactory.socketNotificationReceived.bind(helper) + + helper.socketNotificationReceived('REQUEST_DEFAULT_SETTINGS', { useHostname: true }) + await new Promise(resolve => setImmediate(resolve)) + + const ipNotification = helper.__socketNotifications.find(n => n.action === 'IP_ADDRESSES') + assert.deepEqual(ipNotification.payload, ['mirror.local']) + }) + test('sends LOAD_PORT on REQUEST_DEFAULT_SETTINGS', () => { const helper = freshHelper() helper.configOnHd.port = 9090 From 587167c89fdf4df976630a8118632894d57ee6fb Mon Sep 17 00:00:00 2001 From: silent <20176737+s1len7@users.noreply.github.com> Date: Thu, 29 Oct 2026 22:24:58 +0900 Subject: [PATCH 2/4] chore: add multicast-dns to package-lock.json --- package-lock.json | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/package-lock.json b/package-lock.json index 91923421..eb901323 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "acorn": "^8.19.0", "dompurify": "^3.4.17", "marked": "^18.1.1", + "multicast-dns": "^7.2.5", "qrcode": "^1.5.4", "simple-git": "^4.0.2", "swagger-ui-dist": "^5.34.0" @@ -1261,6 +1262,12 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT" }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.5.tgz", @@ -3251,6 +3258,18 @@ "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "license": "MIT" }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/dompurify": { "version": "3.4.17", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz", @@ -6067,6 +6086,19 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, "node_modules/napi-postinstall": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.7.tgz", @@ -7216,6 +7248,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "license": "MIT" + }, "node_modules/tinyexec": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", From 38e62b81b493d084d79dc143eeaab6f9e1253fae Mon Sep 17 00:00:00 2001 From: silent <20176737+s1len7@users.noreply.github.com> Date: Fri, 30 Oct 2026 10:34:10 +0900 Subject: [PATCH 3/4] fix: resolve lint errors in hostname lookup Co-Authored-By: Claude Opus 5.5 --- MMM-Remote-Control.js | 4 ++-- node_helper.js | 27 +++++++++++++++------------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/MMM-Remote-Control.js b/MMM-Remote-Control.js index f62a03ea..472e1a66 100644 --- a/MMM-Remote-Control.js +++ b/MMM-Remote-Control.js @@ -85,7 +85,7 @@ Module.register('MMM-Remote-Control', { case 'IP_ADDRESSES': // A different address changes the URL, so the QR code has to be generated again - if (payload.join() !== this.addresses.join()) { + if (payload.join(',') !== this.addresses.join(',')) { this.qrCodeDataUrl = null } this.addresses = payload @@ -331,7 +331,7 @@ Module.register('MMM-Remote-Control', { } // Request QR code generation if not already done (not for the placeholder before the address is known) - if (!this.qrCodeDataUrl && hasAddress) { + if (hasAddress && !this.qrCodeDataUrl) { this.sendSocketNotification('GENERATE_QR_CODE', { url, size: this.config.qrCodeSize, diff --git a/node_helper.js b/node_helper.js index e06098d0..2ea9dc5f 100644 --- a/node_helper.js +++ b/node_helper.js @@ -1113,6 +1113,16 @@ module.exports = NodeHelper.create({ return addresses }, + /** + * Send the current ip addresses (or their hostnames if requested) to the frontend + * @param {boolean} useHostname - Whether to resolve the addresses to mDNS hostnames + * @returns {Promise} + */ + async sendIpAddresses(useHostname) { + const addresses = this.getIpAddresses() + this.sendSocketNotification('IP_ADDRESSES', useHostname ? await this.getHostnames(addresses) : addresses) + }, + /** * Look up the mDNS hostname of each address, keeping the address when there is no answer * @param {string[]} addresses - IPv4 addresses @@ -1139,11 +1149,11 @@ module.exports = NodeHelper.create({ return } - const name = `${address.split('.').reverse().join('.')}.in-addr.arpa` - let done = false + const name = `${address.split('.').toReversed().join('.')}.in-addr.arpa` + let isDone = false const finish = (hostname) => { - if (done) return - done = true + if (isDone) return + isDone = true clearTimeout(timer) mdns.destroy() resolve(hostname) @@ -1185,14 +1195,7 @@ module.exports = NodeHelper.create({ break case 'REQUEST_DEFAULT_SETTINGS': // module started, answer with current ip addresses (or their hostnames if requested) - if (payload?.useHostname) { - this.getHostnames(this.getIpAddresses()).then((hostnames) => { - this.sendSocketNotification('IP_ADDRESSES', hostnames) - }) - } - else { - this.sendSocketNotification('IP_ADDRESSES', this.getIpAddresses()) - } + this.sendIpAddresses(payload?.useHostname) this.sendSocketNotification('LOAD_PORT', this.configOnHd.port || '') // check if we have got saved default settings this.loadDefaultSettings() From 149cdae1e905ad0a32c776e5aabb6b0074ddee06 Mon Sep 17 00:00:00 2001 From: silent <20176737+s1len7@users.noreply.github.com> Date: Sun, 1 Nov 2026 21:03:55 +0900 Subject: [PATCH 4/4] refactor: replace useHostname with a configurable hostname option Use the configured hostname in the URL and QR code instead of looking it up over mDNS, and drop the multicast-dns dependency. --- MMM-Remote-Control.js | 10 ++--- README.md | 2 +- config.schema.json | 6 +-- cspell.config.json | 2 - node_helper.js | 63 +-------------------------- package-lock.json | 38 ---------------- package.json | 1 - tests/dom/MMM-Remote-Control.test.js | 32 ++++++++++++++ tests/unit/socketNotification.test.js | 13 ------ 9 files changed, 43 insertions(+), 124 deletions(-) diff --git a/MMM-Remote-Control.js b/MMM-Remote-Control.js index 472e1a66..3eccbe96 100644 --- a/MMM-Remote-Control.js +++ b/MMM-Remote-Control.js @@ -21,7 +21,7 @@ Module.register('MMM-Remote-Control', { showQRCode: true, qrCodeSize: 150, qrCodePosition: 'above', // "below", "above", or "replace" - useHostname: false, // Show the mDNS hostname (e.g. "mirror.local") instead of the IP address + hostname: '', // Hostname to show instead of the IP address (e.g. "mirror.local") }, // Define start sequence. @@ -64,7 +64,7 @@ Module.register('MMM-Remote-Control', { Log.debug(`${this.name} received a module notification: ${notification} from sender: ${sender}`) switch (notification) { case 'DOM_OBJECTS_CREATED': - this.sendSocketNotification('REQUEST_DEFAULT_SETTINGS', { useHostname: this.config.useHostname }) + this.sendSocketNotification('REQUEST_DEFAULT_SETTINGS') this.sendCurrentData() break @@ -306,14 +306,14 @@ Module.register('MMM-Remote-Control', { getDom() { const wrapper = document.createElement('div') let portToShow - const hasAddress = this.addresses.length > 0 + const host = this.config.hostname || this.addresses[0] switch (this.port) { case '': case '8080': portToShow = ':8080'; break case '80': portToShow = ''; break default: portToShow = `:${this.port}`; break } - const url = `http://${hasAddress ? this.addresses[0] : 'ip-of-your-mirror'}${portToShow}/remote.html` + const url = `http://${host ?? 'ip-of-your-mirror'}${portToShow}/remote.html` // Show QR code if enabled if (this.config.showQRCode) { @@ -331,7 +331,7 @@ Module.register('MMM-Remote-Control', { } // Request QR code generation if not already done (not for the placeholder before the address is known) - if (hasAddress && !this.qrCodeDataUrl) { + if (host && !this.qrCodeDataUrl) { this.sendSocketNotification('GENERATE_QR_CODE', { url, size: this.config.qrCodeSize, diff --git a/README.md b/README.md index fb734623..0fe585a9 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Add the module to the `modules` array in your `config.js`. Set a `position` to d // showQRCode: true, // Display a QR code for mobile access (default: true) // qrCodeSize: 150, // QR code size in pixels (default: 150) // qrCodePosition: "above", // "below", "above" (default), or "replace" - // useHostname: false, // Show the mDNS hostname (e.g. "mirror.local") instead of the IP address (default: false) + // hostname: "mirror.local", // Hostname to show instead of the IP address in the URL and QR code (default: "" = IP address) } }, ``` diff --git a/config.schema.json b/config.schema.json index 21257ca5..9ae477af 100644 --- a/config.schema.json +++ b/config.schema.json @@ -34,9 +34,9 @@ "enum": ["below", "above", "replace"], "description": "Place the QR code below or above the URL, or show it instead" }, - "useHostname": { - "type": "boolean", - "description": "Show the mDNS hostname (e.g. mirror.local) instead of the IP address in the URL and QR code" + "hostname": { + "type": "string", + "description": "Hostname to show instead of the IP address in the URL and QR code (e.g. mirror.local), empty to show the IP address" }, "secureEndpoints": { "type": "boolean", diff --git a/cspell.config.json b/cspell.config.json index 2fa3e1e5..209b6088 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -4,7 +4,6 @@ "words": [ "Andoramb", "apikey", - "arpa", "articlelessdetails", "articlemoredetails", "articlenext", @@ -46,7 +45,6 @@ "LOCKSTRING", "lockstrings", "magicmirror", - "mdns", "mmrc", "MODULEAPI", "MONITOROFF", diff --git a/node_helper.js b/node_helper.js index 2ea9dc5f..ed373a14 100644 --- a/node_helper.js +++ b/node_helper.js @@ -1113,65 +1113,6 @@ module.exports = NodeHelper.create({ return addresses }, - /** - * Send the current ip addresses (or their hostnames if requested) to the frontend - * @param {boolean} useHostname - Whether to resolve the addresses to mDNS hostnames - * @returns {Promise} - */ - async sendIpAddresses(useHostname) { - const addresses = this.getIpAddresses() - this.sendSocketNotification('IP_ADDRESSES', useHostname ? await this.getHostnames(addresses) : addresses) - }, - - /** - * Look up the mDNS hostname of each address, keeping the address when there is no answer - * @param {string[]} addresses - IPv4 addresses - * @returns {Promise} Hostnames (e.g. "mirror.local") or the original addresses - */ - getHostnames(addresses) { - return Promise.all(addresses.map(address => this.getHostname(address))) - }, - - /** - * Ask the local network over mDNS (reverse PTR query) which hostname belongs to an IPv4 address - * @param {string} address - IPv4 address - * @returns {Promise} Hostname, or the address if nobody answers within the timeout - */ - getHostname(address) { - return new Promise((resolve) => { - let mdns - try { - mdns = require('multicast-dns')() - } - catch (error) { - Log.warn(`mDNS hostname lookup unavailable: ${error.message}`) - resolve(address) - return - } - - const name = `${address.split('.').toReversed().join('.')}.in-addr.arpa` - let isDone = false - const finish = (hostname) => { - if (isDone) return - isDone = true - clearTimeout(timer) - mdns.destroy() - resolve(hostname) - } - const timer = setTimeout(() => finish(address), 1500) - - mdns.on('response', (response) => { - const answer = response.answers.find(record => record.type === 'PTR' && record.name === name) - if (answer) finish(answer.data) - }) - mdns.on('error', (error) => { - Log.warn(`mDNS hostname lookup for ${address} failed: ${error.message}`) - finish(address) - }) - mdns.query({ questions: [{ name, type: 'PTR' }] }) - }) - }, - /** * Handle socket notifications from the frontend module * @param {string} notification - Notification type @@ -1194,8 +1135,8 @@ module.exports = NodeHelper.create({ break case 'REQUEST_DEFAULT_SETTINGS': - // module started, answer with current ip addresses (or their hostnames if requested) - this.sendIpAddresses(payload?.useHostname) + // module started, answer with current ip addresses + this.sendSocketNotification('IP_ADDRESSES', this.getIpAddresses()) this.sendSocketNotification('LOAD_PORT', this.configOnHd.port || '') // check if we have got saved default settings this.loadDefaultSettings() diff --git a/package-lock.json b/package-lock.json index eb901323..91923421 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "acorn": "^8.19.0", "dompurify": "^3.4.17", "marked": "^18.1.1", - "multicast-dns": "^7.2.5", "qrcode": "^1.5.4", "simple-git": "^4.0.2", "swagger-ui-dist": "^5.34.0" @@ -1262,12 +1261,6 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT" }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "license": "MIT" - }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.5.tgz", @@ -3258,18 +3251,6 @@ "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "license": "MIT" }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/dompurify": { "version": "3.4.17", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz", @@ -6086,19 +6067,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, "node_modules/napi-postinstall": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.7.tgz", @@ -7248,12 +7216,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "license": "MIT" - }, "node_modules/tinyexec": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", diff --git a/package.json b/package.json index afdb1306..6b935f01 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "acorn": "^8.19.0", "dompurify": "^3.4.17", "marked": "^18.1.1", - "multicast-dns": "^7.2.5", "qrcode": "^1.5.4", "simple-git": "^4.0.2", "swagger-ui-dist": "^5.34.0" diff --git a/tests/dom/MMM-Remote-Control.test.js b/tests/dom/MMM-Remote-Control.test.js index 2f9175f5..bc799846 100644 --- a/tests/dom/MMM-Remote-Control.test.js +++ b/tests/dom/MMM-Remote-Control.test.js @@ -106,4 +106,36 @@ describe('MMM-Remote-Control.js module', () => { }, payload) }) }) + + const getDom = (config, addresses) => { + const notifications = [] + const wrapper = Module.getDom.call({ + config: { showQRCode: true, qrCodeSize: 150, qrCodePosition: 'above', hostname: '', ...config }, + addresses, + port: '', + qrCodeDataUrl: null, + sendSocketNotification: (notification, payload) => { + notifications.push({ notification, payload }) + }, + }) + return { wrapper, notifications } + } + + test('getDom shows the IP address when no hostname is configured', () => { + const { wrapper, notifications } = getDom({}, ['192.168.0.7']) + assert.match(wrapper.textContent, /http:\/\/192\.168\.0\.7:8080\/remote\.html/) + assert.equal(notifications[0].payload.url, 'http://192.168.0.7:8080/remote.html') + }) + + test('getDom shows the configured hostname instead of the IP address', () => { + const { wrapper, notifications } = getDom({ hostname: 'mirror.local' }, ['192.168.0.7']) + assert.match(wrapper.textContent, /http:\/\/mirror\.local:8080\/remote\.html/) + assert.equal(notifications[0].payload.url, 'http://mirror.local:8080/remote.html') + }) + + test('getDom does not request a QR code for the placeholder address', () => { + const { wrapper, notifications } = getDom({}, []) + assert.match(wrapper.textContent, /ip-of-your-mirror/) + assert.equal(notifications.length, 0) + }) }) diff --git a/tests/unit/socketNotification.test.js b/tests/unit/socketNotification.test.js index 94bcfec9..4d5cc579 100644 --- a/tests/unit/socketNotification.test.js +++ b/tests/unit/socketNotification.test.js @@ -104,19 +104,6 @@ describe('socketNotificationReceived', () => { assert.ok(Array.isArray(ipNotification.payload)) }) - test('sends hostnames as IP_ADDRESSES on REQUEST_DEFAULT_SETTINGS with useHostname', async () => { - const helper = freshHelper() - helper.getIpAddresses = () => ['192.168.0.7'] - helper.getHostnames = async addresses => addresses.map(() => 'mirror.local') - helper.socketNotificationReceived = helperFactory.socketNotificationReceived.bind(helper) - - helper.socketNotificationReceived('REQUEST_DEFAULT_SETTINGS', { useHostname: true }) - await new Promise(resolve => setImmediate(resolve)) - - const ipNotification = helper.__socketNotifications.find(n => n.action === 'IP_ADDRESSES') - assert.deepEqual(ipNotification.payload, ['mirror.local']) - }) - test('sends LOAD_PORT on REQUEST_DEFAULT_SETTINGS', () => { const helper = freshHelper() helper.configOnHd.port = 9090