WiFiManager
This repository is a breaking fork of upstream tzapu/WiFiManager.
It is not a drop-in replacement for upstream behavior, APIs, templates, or portal customization patterns.
If you are evaluating this fork, assume that core web-portal architecture has changed and review the code before adopting it in an existing upstream-based project.
This repository: alexhopeoconnor/WiFiManager
Upstream: tzapu/WiFiManager
Breaking Changes
This fork intentionally modernizes and restructures the configuration portal. Notable differences from upstream include:
- The portal now serves a single HTML shell from
GET /. - Client navigation is handled as a SPA with hash routing.
- Interactive behavior is exposed through JSON APIs under
/api/...plus firmware upload atPOST /u. - Legacy multi-page portal routes and legacy root-template override paths have been removed.
- The portal rendering pipeline is built around DFTE instead of upstream's monolithic HTML string assembly.
- JSON endpoints are expected to be data-first, not derived from generated HTML fragments.
Current Improvements And Modernization
This fork currently includes the following architectural improvements:
- A single-shell portal architecture with embedded bootstrap JSON and embedded application JS.
- A SPA-based configuration UI for WiFi setup, parameters, info views, device actions, and OTA flow.
- A clean API surface for WiFi scanning, WiFi save, parameters, info, status, restart, erase, portal exit, and captive-portal close behavior.
- Captive portal redirect handling retained while removing duplicate legacy UI architecture.
- Data-first JSON generation for portal APIs, including info/device/about data, instead of HTML-to-JSON parsing.
- Capability-driven UI flags in bootstrap/API payloads so features like info, update, erase, and action visibility can be controlled by backend state.
- Portal bootstrap contract v2: nested
brand,context,pages,actions,layout,extraHomeCards; Wi-Fi meta params includekind(field|html) for first-class custom HTML parameters. - SPA-native feedback UX using in-DOM dialog/toast behavior rather than page-based action flows.
- Connect-on-save handoff: when portal save triggers a station connect,
/api/wifi/connect-statusnow reports a brief success state withstationIpandredirectUrlso the SPA can show the new address and redirect before the AP shuts down. - Request-scoped shell rendering: the root portal page is built for each
GET /fromWM_ROOT_SHELL_TEMPLATEusing a fresh placeholder registry. Shell inputs are%PAGE_TITLE%,%STYLES%,%BOOTSTRAP_JSON%,%PORTAL_APP_JS%, and%PORTAL_APPEND_JS%— filled inWiFiManagerHandlersfrom WiFiManager state and embedded assets (not from a server-wide template registry). - Customization via WiFiManager
portal*APIs (portalSetBrandTitle,portalSetPageInfoVisible,portalSetLayoutParamsLocation,portalAddParameter, asset hooks, etc.) and JSON under/api/..., not by exposing placeholder-registry mutation to consumers. - A clearer separation between:
- shell rendering (handlers + SPA bootstrap)
- JSON API responses
- captive portal behavior
- OTA handling
- Updated tests focused on the shell contract, bootstrap payloads, and API JSON shapes rather than removed legacy portal pages.
Portal Customization Boundary
Stable, supported portal customization is intentionally scoped:
portalSetBrand*for title, intro text, and logo SVG, plusportalSetContextIdentityText(...)for the user-facing runtime identity string on the home view.portalSetPage*,portalSetAction*,portalSetLayout*, andportalSetBehavior*for built-in portal capabilities and runtime behavior.portalAddParameter(...)for first-class custom parameters, including raw HTML blocks inside parameter-rendering surfaces (#/wifior#/setup).portalAddInfoSection(...)andportalAddHomeCard(...)for structured extra content rendered by the built-in SPA.portalAppendCss(...),portalOverrideCss(...), andportalAppendJs(...)for light theming and enhancement hooks.
Not part of the stable API:
- arbitrary HTML injection into home/info/nav/shell
- replacing built-in SPA routing or action flow
- depending on undocumented DOM IDs or route internals
- treating
include/templates/*as a supported consumer override surface
Appended JS should enhance rather than replace the built-in SPA. The documented hook contract is:
wm:readywithdetail.bootwm:view-changedwithdetail.route
If a consumer needs custom live widgets, new primary navigation concepts, or new backend-to-frontend workflows, that is considered fork territory rather than portal customization.
Portal Customization Example
WiFiManager wm;
wm.portalSetBrandTitle("Solar Battery Monitor Setup");
wm.portalSetContextIdentityText("Solar Battery Monitor");
wm.portalSetBrandHomeIntro(
"Connect your monitor to WiFi, then review battery and inverter settings."
);
wm.portalSetBrandLogoSvg(
"<svg viewBox='0 0 24 24' aria-hidden='true'>"
"<path d='M12 2L4 12h5l-1 10 8-10h-5z'></path>"
"</svg>"
);
wm.portalSetPageInfoVisible(true);
wm.portalSetPageUpdateVisible(false);
wm.portalSetActionEraseVisible(false);
wm.portalSetActionRestartVisible(true);
wm.portalSetLayoutParamsLocation(PortalParamsLocation::SetupPage);
wm.portalSetBehaviorCaptivePortalEnabled(true);
wm.portalSetBehaviorConnectOnSave(true);
wm.portalSetBehaviorExitAllowed(true);
wm.portalSetFieldPasswordPlaceholderMode(PortalPasswordPlaceholderMode::Masked);
wm.portalSetFieldStaticIpVisibility(PortalFieldVisibility::Auto);
wm.portalSetFieldStaticDnsVisibility(PortalFieldVisibility::Auto);
WiFiManagerParameter mqttHost(
"mqtt_host",
"MQTT host",
"broker.local",
64,
"placeholder='broker.local'"
);
wm.portalAddParameter(&mqttHost);
// Raw HTML remains first-class for custom parameters, but only inside the
// parameter-rendering surfaces (#/wifi or #/setup), not arbitrary portal regions.
WiFiManagerParameter gpsHelp(
"<div class='wm-callout wm-callout--info'>"
"<p>GPS options below apply only when a GPS module is connected.</p>"
"</div>"
);
wm.portalAddParameter(&gpsHelp);
PortalInfoSection battery;
battery.id = "battery";
battery.title = "Battery";
battery.items.push_back({"soc", "State of charge", "84%"});
battery.items.push_back({"voltage", "Voltage", "13.2V"});
wm.portalAddInfoSection(battery);
PortalHomeCard solar;
solar.id = "solar";
solar.title = "Solar summary";
solar.kind = PortalHomeCardKind::KeyValue;
solar.items.push_back({"pv", "PV input", "420W"});
solar.items.push_back({"load", "Load", "180W"});
wm.portalAddHomeCard(solar);
wm.portalAppendCss(
".wm-brand-logo svg{width:40px;height:40px;display:block;}"
".wm-hero-intro{max-width:42ch;}"
);
wm.portalAppendJs(
"document.addEventListener('wm:ready', function(e){"
" console.log('Portal booted', e.detail.boot);"
"});"
);
WiFi Connect Status API
When portalSetBehaviorConnectOnSave(true) is enabled, saving WiFi credentials queues a station join and the SPA polls GET /api/wifi/connect-status.
Response shape:
{
"state": "waiting | success | failed",
"message": "human readable status",
"wifiStatus": "WL_CONNECTED",
"stationIp": "192.168.1.42",
"redirectUrl": "http://192.168.1.42/"
}
Notes:
stationIpandredirectUrlare present only after a successful station join.- If the portal HTTP server is not on port
80,redirectUrlincludes the active port. - On success, WiFiManager keeps the portal alive briefly so the client can read the success payload and navigate to the new device address before the captive AP is shut down.
- This improves the handoff on typical home networks, but it is not a universal guarantee: client captive-portal helpers, browser behavior, DHCP timing, and network isolation can still affect whether the redirect completes automatically.
Dependencies
This fork depends on DFTE (Device Framework Template Engine) and ESP32Async/ESPAsyncWebServer.
For this repo's platformio.ini test setup, DFTE is expected as a sibling checkout:
lib_deps =
ESP32Async/ESPAsyncWebServer@3.9.1
symlink://../DFTE
Installation
[env:your_environment]
platform = espressif8266 ; or espressif32
board = d1_mini ; your board
framework = arduino
lib_deps =
https://github.com/alexhopeoconnor/WiFiManager.git
Or a local path:
lib_deps =
file:///path/to/WiFiManager
AI Assistance Notice
Parts of this codebase have been developed and refactored with the aid of AI coding agents under human direction and review.
License
See LICENSE.