feat(web): install devices by mDNS name, warn to reserve raw IPs - #714
feat(web): install devices by mDNS name, warn to reserve raw IPs#714HuggeK wants to merge 1 commit into
Conversation
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>
frahlg
left a comment
There was a problem hiding this comment.
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.22withca-certificatesandtzdata(Dockerfile:39,43). No mDNS resolver, and musl has no NSS plugin mechanism, so the usualnss-mdnsfix 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 thatmodbus.Dialand the HTTP client use. network_mode: hostmeans the container can reach a host-local stub resolver, so this may work where the host runssystemd-resolvedwith MulticastDNS enabled — and fail where it runsdhcpcd+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.
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 scanresults — 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
.localname follows the device across lease changes, so binding to it is thesafer default.
When only a raw IP is available (no
.localbroadcast), the wizard and thedevice 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 mDNShostname, but the install flow dropped it and bound the raw IP. Now:
useScanDevice/useManualDevicecarry ahostnamethrough to theselected device.
prefillDriverConfigprefills the host field with the.localname whenthe device broadcasts one (
isMDNSName), otherwise the IP.updateHostHint()shows a green note for an mDNS host (survives DHCPchanges, 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.
modbus/mqtt/http) acceptan IP or a hostname.
web/setup.html— host labels read "IP address or hostname"; adds the.host-hint/.host-hint.mdns/.host-hint.dhcpstyles and the hintelement under the host field.
web/settings/tabs/devices.js— the Settings device host fields get thesame guidance: prefer a
.localname, and reserve a raw IP in the router.web/setup.test.mjs— structural tests lock in the hostnamecarry-through, the
.localpreference, 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
.localnames are preferred, not every DNS nameOnly RFC 6762
.localnames 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 --testover the web test files — 48 pass, 0 fail.🤖 Generated with Claude Code