Wi-Fi operations for Improv provisioning: scan for networks, apply submitted
credentials, and derive the post-provisioning redirect URL. The seam the
Improv manager calls into.
All VintageNet access is guarded (Code.ensure_loaded?) so the module loads on
host, and is injectable (:scan_trigger / :vintage_get / :configure_fn) so
the pure shaping — the configure map, the AP→network mapping, the secured? rule —
is host-tested without a radio. The Wi-Fi interface defaults to "wlan0";
pass ifname: in each call's opts to target another interface.
scan_networks/1 reads the live access_points property (kept fresh by
wpa_supplicant) rather than VintageNetWiFi.quick_scan/1, whose fresh-scan +
2 s sleep read an empty list mid-scan on hardware. It also kicks an async
VintageNet.scan/1 to refresh the property for the next call.
Improv only carries SSID+password, not a security type, so configure/3
infers key_mgmt: :sae (WPA3) vs :wpa_psk from the target SSID's scan
flags — PSK is the fallback whenever the SSID isn't in scan results (hidden
network, aged out) or the lookup itself fails.
Summary
Functions
Apply submitted credentials to the Wi-Fi interface. Looks up the target
SSID's live scan flags (union across every BSSID advertising that SSID) to
pick key_mgmt: :sae vs :wpa_psk — see configure_map/3. The lookup is
best-effort: a raise, a nil property, or the SSID simply not appearing in
scan results all fall back to [] (the PSK path) rather than failing the
call. The flags come from the same live property scan_networks/1 reads,
so the usual scan-then-submit provisioning flow keeps them fresh; a submit
with no recent scan can miss a just-appeared SAE-only network and fall
back to PSK. :vintage_get (the lookup) and :configure_fn (2-arity
(ifname, config) -> term) are injectable for tests.
VintageNet config map for a submitted SSID/password. An empty password
yields an open (key_mgmt: :none) network; otherwise key_mgmt: :sae
(WPA3, with mandatory PMF — ieee80211w: 2) if flags say the SSID is
SAE-only, else the broad-compatibility :wpa_psk fallback (also used for
hidden networks, SSIDs that aged out of scan results, and WPA2/WPA3
transition-mode networks, which still accept PSK). flags is threaded in
by the caller (configure/3, from a live scan) — this function itself
stays pure and does no lookups.
Map a VintageNetWiFi.AccessPoint-shaped value to a network/0. Pure.
The web-UI URL to hand back to the provisioner once joined, or nil if no
IPv4 address is bound yet. :vintage_get injectable for tests.
Nearby networks → {:ok, [network]} (empty SSIDs dropped, deduped by SSID
keeping the strongest signal), or {:error, reason}.
Whether an AP's :flags indicate a secured network. Pure.
Types
Functions
Apply submitted credentials to the Wi-Fi interface. Looks up the target
SSID's live scan flags (union across every BSSID advertising that SSID) to
pick key_mgmt: :sae vs :wpa_psk — see configure_map/3. The lookup is
best-effort: a raise, a nil property, or the SSID simply not appearing in
scan results all fall back to [] (the PSK path) rather than failing the
call. The flags come from the same live property scan_networks/1 reads,
so the usual scan-then-submit provisioning flow keeps them fresh; a submit
with no recent scan can miss a just-appeared SAE-only network and fall
back to PSK. :vintage_get (the lookup) and :configure_fn (2-arity
(ifname, config) -> term) are injectable for tests.
VintageNet config map for a submitted SSID/password. An empty password
yields an open (key_mgmt: :none) network; otherwise key_mgmt: :sae
(WPA3, with mandatory PMF — ieee80211w: 2) if flags say the SSID is
SAE-only, else the broad-compatibility :wpa_psk fallback (also used for
hidden networks, SSIDs that aged out of scan results, and WPA2/WPA3
transition-mode networks, which still accept PSK). flags is threaded in
by the caller (configure/3, from a live scan) — this function itself
stays pure and does no lookups.
Map a VintageNetWiFi.AccessPoint-shaped value to a network/0. Pure.
The web-UI URL to hand back to the provisioner once joined, or nil if no
IPv4 address is bound yet. :vintage_get injectable for tests.
Nearby networks → {:ok, [network]} (empty SSIDs dropped, deduped by SSID
keeping the strongest signal), or {:error, reason}.
Reads the live ["interface", ifname, "wifi", "access_points"] property that
wpa_supplicant keeps refreshed via periodic background scans — NOT
VintageNetWiFi.quick_scan/1, which triggers a fresh ioctl scan and only waits
2s, so it lands in the empty window mid-scan (HW-found: returned 0 while the
property held 31 APs). We also kick an async VintageNet.scan/1 to keep the
property fresh for the next request. :vintage_get/:scan_trigger injectable.
Whether an AP's :flags indicate a secured network. Pure.