Fall back to BLE Wi-Fi provisioning on sustained post-boot link loss

Once StaClient::connect() had already succeeded once, a later link
loss (router rebooted, password changed, device moved) just retried
esp_wifi_connect() forever with no bound and no fallback -- the only
recovery path was a power cycle, since BLE provisioning only ever
started from startSetupMode() (no stored credentials, or the initial
boot-time connect attempt exhausting its own bounded retry count).

connect() now optionally takes the secure store and device identity;
when set, a link lost for longer than ~1 minute starts BLE
provisioning (net::ble_provisioning::start(), same GATT flow as first-
time setup) alongside the still-ongoing STA reconnect attempts -- it
doesn't stop trying Wi-Fi on its own, it just also gives the app a way
in over Bluetooth if Wi-Fi doesn't come back.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 01:07:36 +02:00
co-authored by Claude Sonnet 5
parent 36cd85f4d0
commit 5f46486d46
4 changed files with 111 additions and 8 deletions
@@ -17,6 +17,8 @@
*/
#pragma once
#include "core/DeviceIdentity.hpp"
#include "core/ISecureStore.hpp"
#include "core/WifiCredentials.hpp"
#include "net/NetError.hpp"
@@ -94,8 +96,20 @@ public:
* @brief connect — join the network described by creds.
*
* @dname connect
* @param creds Validated domain credentials from ISecureStore.
* @param hostname STA hostname / mDNS label (no .local suffix).
* @param creds Validated domain credentials from
* ISecureStore.
* @param hostname STA hostname / mDNS label (no .local
* suffix).
* @param bleFallbackStore When set alongside bleFallbackIdentity,
* a link lost for over a minute *after*
* this call already returned successfully
* starts BLE provisioning (additive,
* STA reconnect attempts keep running) so
* the app can reconfigure Wi-Fi without a
* power cycle. Null skips this behaviour.
* @param bleFallbackIdentity Device identity for the BLE fallback's
* advertising name; must outlive this
* StaClient when bleFallbackStore is set.
* @return Ok on success, or NetError::StaConnectTimeout /
* NetError::StaConnectFailed.
* @pubstate writes connected_ on success; uses creds via Secret.
@@ -105,7 +119,9 @@ public:
*/
[[nodiscard]] std::expected<void, NetError>
connect(const core::WifiCredentials& creds,
std::string_view hostname = {});
std::string_view hostname = {},
core::ISecureStore* bleFallbackStore = nullptr,
const core::DeviceIdentity* bleFallbackIdentity = nullptr);
private:
bool connected_;