Skip to content

Conversation

milhnl
Copy link

@milhnl milhnl commented Aug 31, 2025

Hi, as promised, the cleaner version. See #4751 and #4888.

One thing: there's still one cmDNS reference in wled00/wled_eeprom.cpp:83:29:. This did not break compilation for me, probably due to WLED_ADD_EEPROM_SUPPORT not being defined. I'm not sure what the correct fix would be.

Summary by CodeRabbit

  • New Features
    • Revamped Network Settings: host name field, mDNS enable toggle with live URL preview, Ethernet type selector, ESP-NOW controls, and linked remotes management.
    • Unified device hostname across services (mDNS, OTA, MQTT) with sensible defaults.
  • Bug Fixes
    • More reliable hostname/IP resolution and MQTT topic/client defaults; AP IP shown only when AP is active.
  • Style
    • Settings labels updated (“Network Setup”, “Hardware Setup”, “Time & Scheduler”).
    • “Device name” replaces “Server description”; invalid inputs are clearly highlighted.

blazoncek and others added 8 commits June 27, 2025 11:56
- fixes wled#1242
- fixes empty MQTT topic
- fixes missing hostname on change wled#4586
…hostname to "nw" key in config JSON - add new config key in "nw" for mDNS (bool) - remove `prepareHostname()` function - update mDNS calls - add hostname update for Ethernet - move WiFi configuration to cfg.json - rename LED and WiFi to Hardware and Network in settings - reorder network settings
Copy link
Contributor

coderabbitai bot commented Aug 31, 2025

Walkthrough

This PR centralizes hostname and mDNS handling: replaces cmDNS with hostName and adds mDNSenabled; updates resolution, MQTT, OTA, MDNS init/update, and captive portal. Config schema shifts hostname/mDNS to nw.*, adds defaults, and applies WiFi/Ethernet settings. UI pages and XML reflect new fields; utility APIs updated and hardened.

Changes

Cohort / File(s) Summary
Hostname/mDNS core refactor
wled00/wled.h, wled00/wled.cpp, wled00/network.cpp, wled00/mqtt.cpp, wled00/wled_server.cpp, wled00/bus_manager.cpp
Replace cmDNS with hostName; introduce mDNSenabled; gate MDNS.begin/update/queries on mDNSenabled; set OTA/Ethernet hostnames from hostName; add resolveHostname() helper; adjust captive portal host check.
Config load/save and runtime application
wled00/cfg.cpp, wled00/set.cpp
Move hostname/mDNS to nw.name/nw.mdns with legacy compatibility; generate defaults; apply WiFi/Ethernet hostname, sleep, PHY, TX power; widen copies to sizeof; add mDNSenabled handling; remove ST handling.
UI: Settings pages
wled00/data/settings.htm, wled00/data/settings_ui.htm, wled00/data/settings_wifi.htm, wled00/xml.cpp
Update labels (e.g., Network/Hardware/Scheduler); remove ST control; add hostname (CM) and mDNS (MD) controls with live URL; reorganize WiFi/ETH/ESP‑NOW/AP sections; safer IP formatting; reflect hostName/mDNSenabled in XML.
Public API declarations
wled00/fcn_declare.h
Add IPAddress resolveHostname(const String&, bool useMDNS=true); change prepareHostname signature to include size_t maxLen=32.
Utilities
wled00/util.cpp
Rework prepareHostname(char*, size_t): MAC-based base, optional serverDescription augmentation, maxLen-bound, strip trailing hyphens, explicit termination.
Improv protocol adjustments
wled00/improv.cpp
Use hostName when serverDescription is default; safer version formatting with snprintf_P.
Styling
wled00/data/style.css
Apply .err styling to input:invalid to highlight invalid fields.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • blazoncek
  • willmmiles
  • DedeHai

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@netmindz netmindz self-assigned this Aug 31, 2025
@netmindz netmindz added connectivity Issue regarding protocols, WiFi connection or availability of interfaces Awaiting testing labels Aug 31, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
wled00/mqtt.cpp (1)

1-243: Remove leftover cmDNS reference and convert MDNS.queryHost calls to resolveHostname

  • wled00/wled_eeprom.cpp:83: replace the cmDNS argument in
    readStringFromEEPROM(96, cmDNS, 32);
    with the updated hostname storage key or remove cmDNS entirely.
  • wled00/network.cpp:277: change
    clnt = MDNS.queryHost(mDNSname.c_str());
    to
    clnt = resolveHostname(mDNSname);
  • wled00/mqtt.cpp:222: change
    mqttIP = MDNS.queryHost(mqttMDNS.c_str());
    to
    mqttIP = resolveHostname(mqttMDNS);
  • wled00/bus_manager.cpp:733: change
    clnt = MDNS.queryHost(_hostname);
    to
    clnt = resolveHostname(_hostname);
  • No prepareHostname(...) calls with a single argument remain.
wled00/bus_manager.cpp (2)

728-737: Use resolveHostname() to normalize names and avoid duplicated mDNS logic.

This path doesn’t lowercase or strip “.local” like other code. Call the shared resolver for consistent behavior and implicit fallback to WiFi.hostByName.

Apply:

 #ifdef ARDUINO_ARCH_ESP32
 void BusNetwork::resolveHostname() {
   static unsigned long nextResolve = 0;
   if (Network.isConnected() && millis() > nextResolve && _hostname.length() > 0) {
     nextResolve = millis() + 600000; // resolve only every 10 minutes
-    IPAddress clnt;
-    if (mDNSenabled) clnt = MDNS.queryHost(_hostname);
-    else WiFi.hostByName(_hostname.c_str(), clnt);
+    IPAddress clnt = resolveHostname(_hostname, mDNSenabled);
     if (clnt != IPAddress()) _client = clnt;
   }
 }
 #endif

If resolveHostname() isn’t visible here, include its declaration:

 #include "bus_wrapper.h"
+#include "fcn_declare.h" // resolveHostname()

877-906: Syntax error: missing semicolon after u++.

This breaks compilation.

Apply:

-    u++
+    u++;
wled00/set.cpp (1)

35-41: Potentially inverted client SSID reconnect check.

Unchanged code but likely wrong: it reconnects when SSID is equal. Consider flipping to reconnect only when changed or previously empty.

Proposed fix (context only):

-        if (strlen(oldSSID) == 0 || !strncmp(multiWiFi[n].clientSSID, oldSSID, 32)) {
+        if (strlen(oldSSID) == 0 || strncmp(multiWiFi[n].clientSSID, oldSSID, 32) != 0) {
           forceReconnect = true;
         }
🧹 Nitpick comments (6)
wled00/data/style.css (2)

67-69: Consolidate invalid-field styling and fix indentation to tabs

You now style input:invalid here and again on Line 82. Keep a single rule and use tabs per repo guideline for data web files.

Apply within this hunk:

-.err, input:invalid {
-  color: #f00;
-}
+.err, input:invalid {
+	color: #f00;
+}

82-84: Remove duplicate input:invalid rule

This duplicates the styling added on Line 67. Drop the later block.

-input:invalid {
-  color: #f00;
-}
wled00/mqtt.cpp (1)

216-230: Unify MQTT host resolution via resolveHostname() (honors mDNSenabled and normalizes .local).

Avoid duplicating mDNS logic and string munging here. Use the new resolveHostname() helper so behavior matches the rest of the stack and you still fall back cleanly.

Apply:

 #ifdef ARDUINO_ARCH_ESP32
-    String mqttMDNS = mqttServer;
-    mqttMDNS.toLowerCase(); // make sure we have a lowercase hostname
-    int pos = mqttMDNS.indexOf(F(".local"));
-    if (pos > 0) mqttMDNS.remove(pos); // remove .local domain if present (and anything following it)
-    if (mDNSenabled && mqttMDNS.length() > 0 && mqttMDNS.indexOf('.') < 0) { // if mDNS is enabled and server does not have domain
-      mqttIP = MDNS.queryHost(mqttMDNS.c_str());
-      if (mqttIP != IPAddress()) // if MDNS resolved the hostname
-        mqtt->setServer(mqttIP, mqttPort);
-      else
-        mqtt->setServer(mqttServer, mqttPort);
-    } else
+    IPAddress ip = resolveHostname(String(mqttServer), mDNSenabled);
+    if (ip != IPAddress()) {
+      mqtt->setServer(ip, mqttPort);
+    } else
 #endif
       mqtt->setServer(mqttServer, mqttPort);
wled00/improv.cpp (1)

209-211: snprintf size: pass full buffer size for clarity.

Using sizeof(vString) instead of sizeof(vString)-1 is simpler; snprintf already reserves space for the NUL.

Apply:

-  char vString[33];
-  snprintf_P(vString, sizeof(vString)-1, PSTR("%s/%i"), versionString, VERSION);
+  char vString[33];
+  snprintf_P(vString, sizeof(vString), PSTR("%s/%i"), versionString, VERSION);
wled00/data/settings_ui.htm (1)

221-225: Minor UX polish for “Device name”.

Consider disabling autocorrect and spellcheck to avoid browser meddling.

Apply:

-		Device name: <input type="text" name="DS" minlength="2" maxlength="32"><br>
+		Device name: <input type="text" name="DS" minlength="2" maxlength="32" autocomplete="off" spellcheck="false"><br>
wled00/xml.cpp (1)

277-277: Consider making the comment more actionable.

The comment mentions using WiFi.softAPmacAddress() but doesn't explain when or why. Consider expanding it.

-      // WiFi.softAPmacAddress() for AP MAC address
+      // TODO: Consider displaying WiFi.softAPmacAddress() when AP is inactive to show AP MAC for reference
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc87aa and e38575f.

📒 Files selected for processing (16)
  • wled00/bus_manager.cpp (2 hunks)
  • wled00/cfg.cpp (4 hunks)
  • wled00/data/settings.htm (1 hunks)
  • wled00/data/settings_ui.htm (2 hunks)
  • wled00/data/settings_wifi.htm (5 hunks)
  • wled00/data/style.css (1 hunks)
  • wled00/fcn_declare.h (2 hunks)
  • wled00/improv.cpp (1 hunks)
  • wled00/mqtt.cpp (1 hunks)
  • wled00/network.cpp (3 hunks)
  • wled00/set.cpp (3 hunks)
  • wled00/util.cpp (1 hunks)
  • wled00/wled.cpp (4 hunks)
  • wled00/wled.h (1 hunks)
  • wled00/wled_server.cpp (1 hunks)
  • wled00/xml.cpp (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
wled00/{*.cpp,!(html_*)*.h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ source and header files

Files:

  • wled00/wled_server.cpp
  • wled00/fcn_declare.h
  • wled00/improv.cpp
  • wled00/mqtt.cpp
  • wled00/wled.cpp
  • wled00/set.cpp
  • wled00/bus_manager.cpp
  • wled00/wled.h
  • wled00/cfg.cpp
  • wled00/network.cpp
  • wled00/xml.cpp
  • wled00/util.cpp
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use tabs for indentation in web files (.htm/.html/.css/.js) under wled00/data

Files:

  • wled00/data/settings.htm
  • wled00/data/style.css
  • wled00/data/settings_ui.htm
  • wled00/data/settings_wifi.htm
🧠 Learnings (3)
📚 Learning: 2025-08-28T05:32:32.719Z
Learnt from: CR
PR: wled/WLED#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-28T05:32:32.719Z
Learning: Applies to wled00/html_*.h : Do not edit generated embedded web files (wled00/html_*.h)

Applied to files:

  • wled00/wled_server.cpp
  • wled00/set.cpp
  • wled00/wled.h
📚 Learning: 2025-04-26T12:51:04.993Z
Learnt from: KrX3D
PR: wled/WLED#4664
File: wled00/syslog.cpp:50-51
Timestamp: 2025-04-26T12:51:04.993Z
Learning: In the WLED codebase, syslog configuration variables (syslogHost, syslogPort, syslogProtocol, syslogFacility, syslogSeverity) are defined as global variables in wled.h and are accessed directly in the SyslogPrinter implementation, rather than being class members.

Applied to files:

  • wled00/wled.h
📚 Learning: 2025-04-18T22:27:58.634Z
Learnt from: KrX3D
PR: wled/WLED#4237
File: usermods/INA219_v2/INA219_v2.cpp:265-276
Timestamp: 2025-04-18T22:27:58.634Z
Learning: When implementing MQTT message handling in WLED usermods, use `strstr(topic, "/specific/path")` instead of `strcmp_P(topic, PSTR("/specific/path"))` to properly match topics that include the device prefix. The full MQTT topic typically follows the pattern `<mqttDeviceTopic>/specific/path`.

Applied to files:

  • wled00/cfg.cpp
🧬 Code graph analysis (5)
wled00/fcn_declare.h (3)
wled00/bus_manager.cpp (2)
  • resolveHostname (728-737)
  • resolveHostname (728-728)
wled00/network.cpp (2)
  • resolveHostname (268-283)
  • resolveHostname (268-268)
wled00/util.cpp (2)
  • prepareHostname (131-149)
  • prepareHostname (131-131)
wled00/wled.cpp (1)
wled00/util.cpp (2)
  • prepareHostname (131-149)
  • prepareHostname (131-131)
wled00/set.cpp (1)
wled00/util.cpp (4)
  • prepareHostname (131-149)
  • prepareHostname (131-131)
  • isAsterisksOnly (151-159)
  • isAsterisksOnly (151-151)
wled00/cfg.cpp (1)
wled00/util.cpp (2)
  • prepareHostname (131-149)
  • prepareHostname (131-131)
wled00/xml.cpp (1)
wled00/util.cpp (8)
  • printSetFormValue (111-113)
  • printSetFormValue (111-111)
  • printSetFormValue (118-120)
  • printSetFormValue (118-118)
  • printSetFormCheckbox (108-110)
  • printSetFormCheckbox (108-108)
  • printSetClassElementHTML (122-124)
  • printSetClassElementHTML (122-122)
🔇 Additional comments (35)
wled00/data/settings.htm (1)

41-42: LGTM: label updates only; routes unchanged

The button text changes read better and keep existing endpoints.

Also applies to: 47-47

wled00/fcn_declare.h (2)

257-257: LGTM: resolveHostname() declaration matches intended behavior

Default parameter keeps MDNS optional at call sites.


392-392: prepareHostname callers correctly pass maxLen
All three usages in wled.cpp, set.cpp, and cfg.cpp now explicitly provide the maxLen argument; no further updates needed.

wled00/mqtt.cpp (2)

216-230: Heads-up: .local without mDNS will likely fail to resolve.

If users set an MQTT server like foo.local while mDNS is disabled, AsyncMqttClient will try standard DNS (and fail). Consider warning in UI or documenting this, or automatically strip “.local” only when mDNSenabled is true.


221-221: LGTM: mDNS gating.

Switching the gate from cmDNS length to mDNSenabled is correct and matches the PR’s intent.

wled00/bus_manager.cpp (1)

31-31: LGTM: new global gate.

Extern mDNSenabled aligns this module with the new global switch.

wled00/improv.cpp (1)

212-213: LGTM: prefer hostName when serverDescription is default.

This matches the new hostname model and removes cmDNS coupling.

wled00/data/settings_ui.htm (1)

156-159: LGTM: Save gating simplified.

Dropping ST from the submit condition aligns with the UI changes that removed ST.

wled00/set.cpp (2)

85-86: LGTM: bounds-safe AP password handling.

Using isAsterisksOnly() and sizeof-based strlcpy is correct.


296-297: LGTM: unsigned loop/index.

Matches intent and avoids UB with negative comparisons.

wled00/xml.cpp (4)

193-193: Good defensive programming with buffer size declaration.

Adding an explicit buffer size for the local variable improves safety and clarity.


216-217: LGTM! Hostname and mDNS settings properly migrated.

The change from cmDNS to hostName and addition of mDNSenabled checkbox aligns well with the PR's hostname unification objective.


270-273: Good: Added null check for AP IP display.

The addition of apActive check prevents displaying invalid AP information when the AP is not active, improving data accuracy.


273-273: Buffer-safe formatting with snprintf.

Using snprintf with explicit buffer size is the correct approach for safe string formatting.

wled00/wled.cpp (4)

144-144: Appropriate mDNS update gating on ESP8266.

Good practice to only update mDNS when it's enabled, avoiding unnecessary processing.


426-427: Hostname initialization logic looks correct.

The code properly checks if hostname equals the default and generates a unique one if needed. The size calculation sizeof(hostName)-1 ensures null termination space.


500-500: ArduinoOTA hostname correctly updated.

The OTA hostname now uses the unified hostName variable, maintaining consistency across the system.


713-717: mDNS initialization properly gated.

The mDNS service is correctly initialized only when mDNSenabled is true, with proper cleanup (MDNS.end()) before reinitializing.

wled00/util.cpp (2)

126-131: Well-documented function signature change.

The addition of comprehensive documentation explaining the function's behavior, constraints, and parameters is excellent. The maxLen parameter addition improves safety by preventing buffer overflows.


132-148: Robust hostname generation with proper bounds checking.

The implementation correctly:

  • Respects the maxLen boundary
  • Generates a MAC-based default hostname
  • Sanitizes serverDescription characters appropriately
  • Strips trailing hyphens
  • Ensures null termination
wled00/network.cpp (4)

268-283: New hostname resolution function is well-implemented.

The function properly handles both mDNS and DNS resolution with appropriate fallbacks. Good defensive programming with network connectivity check.


285-291: Verify the intent of the empty string logic.

The function sets the string to empty if all MAC bytes are zero. This seems intentional but could be surprising behavior.

Is this intended to handle uninitialized or invalid MAC addresses? If so, consider adding a comment to clarify this behavior:

void fillMAC2Str(char *str, const uint8_t *mac) {
  sprintf_P(str, PSTR("%02x%02x%02x%02x%02x%02x"), MAC2STR(mac));
  byte nul = 0;
  for (int i = 0; i < 6; i++) nul |= *mac++;  // do we have 0
- if (!nul) str[0] = '\0';                    // empty string
+ if (!nul) str[0] = '\0';                    // empty string for all-zero MAC (uninitialized/invalid)
}

407-407: Preprocessor directive style consistency.

Changed from #if defined(...) to #ifdef ... for consistency.


417-417: Ethernet hostname correctly set.

The Ethernet hostname is now consistently set to the unified hostName variable.

wled00/data/settings_wifi.htm (6)

6-6: UI title updated to reflect broader scope.

The change from "WiFi Settings" to "Network Settings" accurately reflects that this page now handles WiFi, Ethernet, and ESP-NOW configuration.


137-137: Good use of callback for dynamic UI updates.

Loading the settings script with a callback to genUrl ensures the UI updates after settings are loaded.


147-147: Verify ESP-NOW and Ethernet incompatibility handling.

The code disables Ethernet when ESP-NOW is enabled, but should we also disable ESP-NOW when Ethernet is selected?

Consider bidirectional enforcement:

function tE() {
  // keep the hidden input with MAC addresses, only toggle visibility of the list UI
  gId('rlc').style.display = d.Sf.RE.checked ? 'block' : 'none';
  if (d.Sf.RE.checked) d.Sf.ETH.selectedIndex = 0; // disable Ethernet if ESPNOW is enabled
}

Also update the ETH select onchange:

-<select name="ETH" onchange="if(this.selectedIndex!=0)d.Sf.RE.checked=false;">
+<select name="ETH" onchange="if(this.selectedIndex!=0){d.Sf.RE.checked=false;tE();}">

This ensures the ESP-NOW UI is properly hidden when Ethernet is selected.


180-183: Live URL preview implementation is helpful.

The genUrl() function provides immediate feedback to users about their hostname configuration and mDNS availability.


195-195: Good hostname validation pattern.

The pattern [a-zA-Z0-9_\-]* correctly restricts hostnames to valid characters. However, consider if empty hostnames should be allowed.

Should we enforce a minimum length to prevent empty hostnames?

-<input type="text" name="CM" minlength="2" maxlength="32" pattern="[a-zA-Z0-9_\-]*" oninput="genUrl()"><br>
+<input type="text" name="CM" minlength="2" maxlength="32" pattern="[a-zA-Z0-9_\-]+" required oninput="genUrl()"><br>

211-211: Improved AP SSID validation.

Good addition of the pattern attribute to restrict AP SSID to valid characters. The empty option for disabling AP is clearly indicated.

wled00/cfg.cpp (5)

160-169: Good implementation of WiFi hostname configuration

The WiFi hostname is properly applied using the unified hostName variable for both ESP32 and ESP8266 platforms. This ensures consistency across different hardware platforms.


658-660: MQTT default topic generation uses MAC-based pattern

The automatic generation of MQTT client ID and device topic correctly uses the device's MAC address, which aligns well with the hostname unification approach where MAC-based identifiers are used as fallbacks.


871-879: Serialization correctly implements new schema

The serialization properly writes serverDescription to id.name and hostName to nw.name, with mDNSenabled written to nw.mdns. This correctly implements the new unified hostname schema.


69-69: Size usage is correct: alexaInvocationName[33]
sizeof(alexaInvocationName) yields 33, matching its definition in wled00/wled.h.


163-164: txPower is properly constrained and cast to wifi_power_t; no changes required

The value is clamped between WIFI_POWER_2dBm and WIFI_POWER_19_5dBm at line 157 before being passed to WiFi.setTxPower at line 164.

Comment on lines +60 to +67
getStringFromJson(serverDescription, id["name"], sizeof(serverDescription));
// legacy behaviour
getStringFromJson(hostName, id[F("mdns")], sizeof(hostName));
if (strlen(hostName) == 0) {
mDNSenabled = false; // if no host name is set, disable mDNS
prepareHostname(hostName, sizeof(hostName)-1);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential issue with legacy hostname migration logic

The current implementation reads from id["mdns"] into hostName for legacy compatibility, but if that field is empty, it disables mDNS and generates a new hostname. This could unexpectedly disable mDNS for users who had it enabled but didn't have an explicit hostname set in their legacy configuration.

Consider preserving the existing mDNS enabled state when migrating legacy configurations:

-  // legacy behaviour
-  getStringFromJson(hostName, id[F("mdns")], sizeof(hostName));
-  if (strlen(hostName) == 0) {
-    mDNSenabled = false; // if no host name is set, disable mDNS
-    prepareHostname(hostName, sizeof(hostName)-1);
-  }
+  // legacy behaviour - preserve existing mDNS state
+  const char* legacyHostname = id[F("mdns")];
+  if (legacyHostname != nullptr) {
+    getStringFromJson(hostName, legacyHostname, sizeof(hostName));
+  }
+  // Generate hostname if empty (from either legacy or new location)
+  if (strlen(hostName) == 0) {
+    prepareHostname(hostName, sizeof(hostName)-1);
+  }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In wled00/cfg.cpp around lines 60 to 67, the legacy migration currently zeroes
mDNSenabled when id["mdns"] yields an empty hostName; instead, preserve the
existing mDNSenabled flag and only change it if the legacy config explicitly
indicates mDNS should be disabled. Update the logic to: read hostName from
id["mdns"], if hostName is empty call prepareHostname(...) to generate a
hostname but do not set mDNSenabled = false; only set mDNSenabled = false when
the legacy field explicitly signals mDNS should be disabled (or when another
explicit legacy flag indicates disablement).

Comment on lines +74 to +75
CJSON(mDNSenabled, nw[F("mdns")]);
getStringFromJson(hostName, nw["name"], sizeof(hostName));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Order of operations issue: mDNS flag might be overwritten

The mDNS enabled flag is set on line 74 from the new location nw["mdns"], but the legacy code on lines 62-66 might have already set it to false. This could cause the new value to override the legacy migration logic unexpectedly.

Consider reordering to handle legacy migration after reading the new values:

   JsonObject nw = doc["nw"];
-  CJSON(mDNSenabled, nw[F("mdns")]);
-  getStringFromJson(hostName, nw["name"], sizeof(hostName));
+  // Read new format first
+  bool hasNewHostname = nw["name"] != nullptr;
+  if (hasNewHostname) {
+    getStringFromJson(hostName, nw["name"], sizeof(hostName));
+    CJSON(mDNSenabled, nw[F("mdns")]);
+  }
+  // Only apply legacy if new format not present
+  else if (id[F("mdns")] != nullptr) {
+    getStringFromJson(hostName, id[F("mdns")], sizeof(hostName));
+    // Don't force mDNS disabled for legacy configs
+  }
+  // Generate hostname if still empty
+  if (strlen(hostName) == 0) {
+    prepareHostname(hostName, sizeof(hostName)-1);
+  }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In wled00/cfg.cpp around lines 62-75, the new mDNS flag is read from nw["mdns"]
on line 74 which can be unintentionally overridden by the earlier legacy
migration block (lines 62-66); move the legacy migration logic to run after you
read the new values (i.e., after the CJSON/getStringFromJson lines) or,
alternatively, keep reading nw["mdns"] first and then apply the legacy check to
explicitly override mDNSenabled when legacy storage indicates it should be
disabled; update the surrounding comment to clarify the order and ensure the
final mDNSenabled value reflects legacy migration when applicable.

Comment on lines +66 to 77
strlcpy(hostName, request->arg(F("CM")).c_str(), sizeof(hostName));
if (strlen(hostName) == 0) prepareHostname(hostName, sizeof(hostName)-1);
#ifdef ARDUINO_ARCH_ESP32
#ifdef WLED_USE_ETHERNET
ETH.setHostname(hostName);
#endif
WiFi.setHostname(hostName);
#else
WiFi.hostname(hostName);
#endif
mDNSenabled = request->hasArg(F("MD"));

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Always sanitize hostName and trigger reconnect when it changes.

Currently prepareHostname() runs only if CM is empty, so user-supplied values can slip through with illegal chars/length. Also, hostname changes should set forceReconnect so stacks pick them up.

Apply:

-    strlcpy(hostName, request->arg(F("CM")).c_str(), sizeof(hostName));
-    if (strlen(hostName) == 0) prepareHostname(hostName, sizeof(hostName)-1);
+    char oldHost[sizeof(hostName)]; strlcpy(oldHost, hostName, sizeof(oldHost));
+    strlcpy(hostName, request->arg(F("CM")).c_str(), sizeof(hostName));
+    // Sanitize and bound length regardless of user input
+    prepareHostname(hostName, sizeof(hostName)-1);
+    if (strcmp(oldHost, hostName) != 0) forceReconnect = true;
     #ifdef ARDUINO_ARCH_ESP32
       #ifdef WLED_USE_ETHERNET
     ETH.setHostname(hostName);
       #endif
     WiFi.setHostname(hostName);
     #else
     WiFi.hostname(hostName);
     #endif
     mDNSenabled = request->hasArg(F("MD"));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
strlcpy(hostName, request->arg(F("CM")).c_str(), sizeof(hostName));
if (strlen(hostName) == 0) prepareHostname(hostName, sizeof(hostName)-1);
#ifdef ARDUINO_ARCH_ESP32
#ifdef WLED_USE_ETHERNET
ETH.setHostname(hostName);
#endif
WiFi.setHostname(hostName);
#else
WiFi.hostname(hostName);
#endif
mDNSenabled = request->hasArg(F("MD"));
// Always sanitize hostName and trigger reconnect on change
char oldHost[sizeof(hostName)]; strlcpy(oldHost, hostName, sizeof(oldHost));
strlcpy(hostName, request->arg(F("CM")).c_str(), sizeof(hostName));
// Sanitize and bound length regardless of user input
prepareHostname(hostName, sizeof(hostName)-1);
if (strcmp(oldHost, hostName) != 0) forceReconnect = true;
#ifdef ARDUINO_ARCH_ESP32
#ifdef WLED_USE_ETHERNET
ETH.setHostname(hostName);
#endif
WiFi.setHostname(hostName);
#else
WiFi.hostname(hostName);
#endif
mDNSenabled = request->hasArg(F("MD"));
🤖 Prompt for AI Agents
In wled00/set.cpp around lines 66 to 77, always sanitize the user-supplied
hostName and trigger a reconnect when it changes: call prepareHostname(hostName,
sizeof(hostName)) (or an appropriate sanitizer/truncator) for both empty and
non-empty CM values instead of only when CM is empty, compare the sanitized
hostName against the current/previous hostname and if different set
forceReconnect = true so network stacks pick up the change, and then call the
appropriate platform hostname APIs (ETH/WiFi) with the sanitized value.

Comment on lines 79 to 82
char oldSSID[33]; strcpy(oldSSID, apSSID);
strlcpy(apSSID, request->arg(F("AS")).c_str(), 33);
strlcpy(apSSID, request->arg(F("AS")).c_str(), sizeof(apSSID));
if (!strcmp(oldSSID, apSSID) && apActive) forceReconnect = true;
apHide = request->hasArg(F("AH"));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix AP SSID change detection (current logic flips condition).

The code forces reconnect when SSID is unchanged.

Apply:

-    if (!strcmp(oldSSID, apSSID) && apActive) forceReconnect = true;
+    if (strcmp(oldSSID, apSSID) != 0 && apActive) forceReconnect = true;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
char oldSSID[33]; strcpy(oldSSID, apSSID);
strlcpy(apSSID, request->arg(F("AS")).c_str(), 33);
strlcpy(apSSID, request->arg(F("AS")).c_str(), sizeof(apSSID));
if (!strcmp(oldSSID, apSSID) && apActive) forceReconnect = true;
apHide = request->hasArg(F("AH"));
char oldSSID[33]; strcpy(oldSSID, apSSID);
strlcpy(apSSID, request->arg(F("AS")).c_str(), sizeof(apSSID));
if (strcmp(oldSSID, apSSID) != 0 && apActive) forceReconnect = true;
apHide = request->hasArg(F("AH"));
🤖 Prompt for AI Agents
In wled00/set.cpp around lines 79 to 82, the logic that sets forceReconnect is
inverted so it triggers when the SSID is unchanged; change the comparison to
detect a difference and only force reconnect when the SSID actually changed
while AP is active (i.e., replace the current equality check with a non-zero
strcmp/strncmp result combined with apActive) so forceReconnect is set only when
oldSSID and apSSID differ and apActive is true.

@@ -234,7 +234,7 @@ static bool captivePortal(AsyncWebServerRequest *request)
if (!request->hasHeader(F("Host"))) return false;

String hostH = request->getHeader(F("Host"))->value();
if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && hostH.indexOf(cmDNS) < 0 && hostH.indexOf(':') < 0) {
if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && hostH.indexOf(hostName) < 0 && hostH.indexOf(':') < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Captive-portal false negative when hostName is empty; also consider case-insensitive match

If hostName == "", hostH.indexOf(hostName) returns 0 and bypasses the captive portal erroneously. Add an emptiness guard; optionally normalize case.

Minimal fix:

-  if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && hostH.indexOf(hostName) < 0 && hostH.indexOf(':') < 0) {
+  if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && (hostName[0] == 0 || hostH.indexOf(hostName) < 0) && hostH.indexOf(':') < 0) {

(Optional) case-insensitive match:

-  if (!isIp(hostH) && hostH.indexOf(F("wled.me")) < 0 && (hostName[0] == 0 || hostH.indexOf(hostName) < 0) && hostH.indexOf(':') < 0) {
+  String hostHL = hostH; hostHL.toLowerCase();
+  String ownHL = String(hostName); ownHL.toLowerCase();
+  if (!isIp(hostH) && hostHL.indexOf(F("wled.me")) < 0 && (ownHL.length() == 0 || hostHL.indexOf(ownHL) < 0) && hostH.indexOf(':') < 0) {
🤖 Prompt for AI Agents
In wled00/wled_server.cpp around line 237, the host check treats an empty
hostName as matching (hostH.indexOf(hostName) == 0), causing false negatives for
captive-portal; also matches are case-sensitive. Fix by first checking hostName
is non-empty before calling indexOf, and optionally normalize both hostH and
hostName to the same case (e.g., toLowerCase) before testing. Update the
conditional to require hostName.length() > 0 (or !hostName.isEmpty()) before
evaluating hostH.indexOf(hostName) and/or compare with case-insensitive
variants.

Comment on lines +342 to 344
WLED_GLOBAL char hostName[33] _INIT(MDNS_NAME); // mDNS address (*.local, replaced by wled-XXXXXX if default is used)
WLED_GLOBAL bool mDNSenabled _INIT(true); // use mDNS (default is true, can be changed in web UI)
WLED_GLOBAL char apSSID[33] _INIT(""); // AP off by default (unless setup)
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Good: unified hostName + mDNSenabled

Defaults and sizes look consistent with hostname usage elsewhere.

To ensure no leftover symbols cause build issues (per PR note about cmDNS in wled_eeprom.cpp), run:


🏁 Script executed:

#!/bin/bash
# Find any remaining cmDNS references
rg -nP '\bcmDNS\b' -C2

Length of output: 392


Update EEPROM handlers to use hostName instead of residual cmDNS
In wled00/wled_eeprom.cpp (around line 83), the EEPROM read/write calls still reference cmDNS. Replace these with hostName (adjusting the size parameter to 33) to match the new declaration and avoid undefined-symbol build errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting testing connectivity Issue regarding protocols, WiFi connection or availability of interfaces
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants