Skip to content

feat(web): install devices by mDNS name, warn to reserve raw IPs - #714

Draft
HuggeK wants to merge 1 commit into
srcfl:masterfrom
HuggeK:mdns-first-device-install
Draft

feat(web): install devices by mDNS name, warn to reserve raw IPs#714
HuggeK wants to merge 1 commit into
srcfl:masterfrom
HuggeK:mdns-first-device-install

Conversation

@HuggeK

@HuggeK HuggeK commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What & why

When you install a discovered device, the setup wizard now binds to the device's
self-broadcast mDNS (.local) name — the same name already shown in the scan
results — instead of its raw IPv4 address, whenever the device advertises one.

A raw IP is only stable if it's reserved in the router's DHCP pool. If it isn't,
a new DHCP lease can move the device and silently break the connection. An mDNS
.local name follows the device across lease changes, so binding to it is the
safer default.

When only a raw IP is available (no .local broadcast), the wizard and the
device settings now tell the operator to reserve that IP for the device in
their router's DHCP settings
so a future lease can't move it.

Changes

  • web/setup.js — the scan already resolved and displayed the mDNS
    hostname, but the install flow dropped it and bound the raw IP. Now:
    • useScanDevice / useManualDevice carry a hostname through to the
      selected device.
    • prefillDriverConfig prefills the host field with the .local name when
      the device broadcasts one (isMDNSName), otherwise the IP.
    • a new updateHostHint() shows a green note for an mDNS host (survives DHCP
      changes, with the discovered IP for reference) or a yellow DHCP-reservation
      warning for a raw-IP host; it also tracks manual edits of the field.
    • host validation and capability binding (modbus / mqtt / http) accept
      an IP or a hostname.
  • web/setup.html — host labels read "IP address or hostname"; adds the
    .host-hint / .host-hint.mdns / .host-hint.dhcp styles and the hint
    element under the host field.
  • web/settings/tabs/devices.js — the Settings device host fields get the
    same guidance: prefer a .local name, and reserve a raw IP in the router.
  • web/setup.test.mjs — structural tests lock in the hostname
    carry-through, the .local preference, and the DHCP-reservation warning.

The Go backend already accepts hostnames for Modbus/MQTT/HTTP hosts and for
fingerprint scanning, so this is a front-end-only change.

Why only .local names are preferred, not every DNS name

Only RFC 6762 .local names are self-broadcast by the device itself over mDNS,
so they resolve without depending on the router's DNS. A generic reverse-DNS
name depends on router configuration that may not be present, so the wizard
keeps the IP as the bound host in that case (and warns to reserve it).

Testing

  • node --test over the web test files — 48 pass, 0 fail.

🤖 Generated with Claude Code

The setup wizard showed a discovered device's mDNS hostname in the scan
table but always bound the raw IP into the driver config. Prefer the
self-broadcast .local name when the device advertises one, so the
connection survives DHCP lease changes; other reverse-DNS names stay
display-only since resolving them depends on the router.

When the configured host is an IP literal, a live hint under the host
field now tells the operator to reserve that IP for the device in the
router's DHCP settings. The Settings device help texts carry the same
advice.

Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
@HuggeK
HuggeK marked this pull request as ready for review July 29, 2026 13:45
@HuggeK
HuggeK requested a review from erikarenhill as a code owner July 29, 2026 13:45

@frahlg frahlg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Read the whole diff. The reasoning is right and the implementation is careful — restricting the preference to .local via isMDNSName() and leaving every other reverse-DNS name display-only is exactly the correct line, since anything else depends on the router's DNS. The live hint on input is a nice touch, and routing saveDriver through one host variable keeps Modbus, MQTT and HTTP consistent.

One concern before this becomes the default, and I could not settle it from here.

Can the official container resolve a .local name at all?

  • The runtime image is alpine:3.22 with ca-certificates and tzdata (Dockerfile:39,43). No mDNS resolver, and musl has no NSS plugin mechanism, so the usual nss-mdns fix is not available the way it is on glibc.
  • Go's resolver does unicast DNS against the nameservers in resolv.conf. mDNS is multicast to 224.0.0.251:5353. The repo's own mDNS code (go/internal/scanner/mdns.go) is a hand-rolled multicast query used for discovery; it does not feed the resolver that modbus.Dial and the HTTP client use.
  • network_mode: host means the container can reach a host-local stub resolver, so this may work where the host runs systemd-resolved with MulticastDNS enabled — and fail where it runs dhcpcd + avahi, which is the common Raspberry Pi OS setup.

So the outcome likely depends on the host, and the PR removes the one sentence that warned about it: "mDNS names work when your OS resolver supports them; otherwise use the LAN IP". config.example.yaml:94 still carries the same caveat — allowed_hosts: ["zap.local"] # use the LAN IP if mDNS is unavailable.

The failure mode is bad: the wizard silently writes inverter.local, the driver never connects, and the operator has no way to tell that name resolution is the reason.

What would settle it: install a discovered device by its .local name on the official image on a Pi and confirm the driver comes online. If it works, this is ready. If it depends on the host resolver, the safer shape is to probe the name before saving and fall back to the IP when it does not resolve — the wizard already knows both.

Not blocking on my side; @erikarenhill owns web/ and this is his call.

@HuggeK
HuggeK marked this pull request as draft July 30, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants