esp_provisioning_wifi 0.3.1 copy "esp_provisioning_wifi: ^0.3.1" to clipboard
esp_provisioning_wifi: ^0.3.1 copied to clipboard

Provision WiFi on Espressif ESP32 devices over Bluetooth LE from Flutter, with a Bloc API and native Android and iOS provisioning implementations.

logo

pub package License: MIT

esp_provisioning_wifi #

Library to provision WiFi on ESP32 devices over Bluetooth, using Bloc.

API Notes #

  • Import the package via the public barrel:
    • import 'package:esp_provisioning_wifi/esp_provisioning_wifi.dart';
  • Most apps drive the flow through EspProvisioningBloc (see Usage). For direct, non-Bloc use, instantiate EspProvisioningService() from the same import; it exposes all of the methods below plus getPlatformVersion().
  • scanBleDevices(prefix) returns Future<List<String>> of matching device names and must run before scanWifiNetworks/provisionWifi.
  • scanWifiNetworks(...) returns Future<List<EspWifiNetwork>>.
    • Each network exposes ssid (String), rssi (dBm, int?) and security (a typed EspWifiSecurity enum mirroring Espressif's WifiAuthMode), populated on both platforms.
  • scanWifiNetworks(...), provisionWifi(...) and fetchCustomData(...) accept security: EspSecurityScheme.security1 (default) or .security2.
    • Security 2 (SRP6a) additionally requires the username configured in the firmware; omitting it fails fast with E0.
    • The bloc events EspProvisioningEventBleSelected and EspProvisioningEventWifiSelected take the same optional security and username parameters.
  • provisionWifi(...) returns Future<bool>.
    • It resolves true on success and throws a PlatformException with a typed error code on failure: E_PROV_* for provisioning-phase failures, or a connect-phase code such as E_CONNECT, E_CONNECT_TIMEOUT or E_CANCELLED (see the Error Code Contract below).
  • cancelOperations() returns Future<bool> and cancels active native work.
    • In-flight scan/provision calls fail with E_CANCELLED (EspProvisioningFailure.cancelled) on both platforms.
  • EspProvisioningState.failure exposes typed failures using EspProvisioningFailure.
    • none, permissionDenied, timeout, cancelled, deviceNotFound, invalidResponse, sessionFailed, authenticationFailed, networkNotFound, provisioningFailed, platform, unknown.
  • EspProvisioningState.errorCode and errorDetails expose raw platform diagnostics.
  • scanWifiNetworks(...) and provisionWifi(...) accept optional connectTimeout.
    • This timeout is propagated through Dart and native layers for BLE connection timing.
  • EspProvisioningBloc accepts connectTimeout (BLE connect phase, default 15s) and requestTimeout (overall operation budget, default connectTimeout + 20s).
  • Dart-side request timeouts cancel the in-flight native operation and emit status: EspProvisioningStatus.error with failure == EspProvisioningFailure.timeout.
  • fetchCustomData(deviceName, proofOfPossession, {endpoint = 'custom-data', payload = '', security, username, connectTimeout}) returns Future<String?> and reads provisioning custom endpoint payloads.
    • Service-level only (there is no bloc event for it); failures throw E_CUSTOM_DATA.
    • Useful for firmware-driven provisioning metadata such as lock state or SoftAP password hints.

Error Code Contract #

The plugin reports stable error codes that the bloc maps into EspProvisioningFailure. Most come from the native layers; E_INVALID_RESPONSE, E_TIMEOUT and E_UNKNOWN are raised by the Dart layer:

  • E0 (EspProvisioningErrorCodes.missingArgument)
  • E1 (EspProvisioningErrorCodes.wifiScanFailed)
  • E_PERMISSION
  • E_BLE_SCAN_START
  • E_BLE_SCAN
  • E_DEVICE_NOT_FOUND
  • E_INVALID_RESPONSE
  • E_CONNECT_TIMEOUT
  • E_CONNECT
  • E_CUSTOM_DATA
  • E_DEVICE
  • E_PROV_SESSION
  • E_PROV_CONFIG
  • E_PROV_AUTH
  • E_PROV_NETWORK_NOT_FOUND
  • E_PROV_FAILED
  • DEVICE_DISCONNECTED (legacy, no longer emitted since 0.3.1)
  • E_CANCELLED
  • E_TIMEOUT
  • E_UNKNOWN

Import: package:esp_provisioning_wifi/esp_provisioning_error_codes.dart.

Platform note: both platforms emit the granular provisioning codes (E_PROV_SESSION, E_PROV_CONFIG, E_PROV_AUTH, E_PROV_NETWORK_NOT_FOUND), with E_PROV_FAILED as the fallback. On iOS an incorrect proof of possession is typically rejected during the connect phase (E_CONNECT/E_DEVICE, mapped to EspProvisioningFailure.platform) rather than as E_PROV_SESSION. Both platforms emit E_DEVICE_NOT_FOUND when the named device cannot be found (Android from its BLE scan cache, iOS from the device search) and E_CONNECT when the device disconnects during the connect phase (DEVICE_DISCONNECTED is a legacy code, no longer emitted since 0.3.1).

Migration (0.2.x -> 0.3.0) #

  1. Security 2 (SRP6a) support: scanWifiNetworks, provisionWifi, fetchCustomData, and the bloc selection events accept optional security (EspSecurityScheme) and username parameters. Defaults are unchanged (Security 1), so existing call sites keep working.
  2. iOS now populates EspWifiNetwork.rssi/security and emits the granular E_PROV_* codes; code that special-cased their absence on iOS can be simplified.
  3. Any class that overrides scanWifiNetworks, provisionWifi, or fetchCustomData — custom FlutterEspBleProvPlatform implementations, or test fakes extending FlutterEspBleProv (e.g. injected into EspProvisioningBloc) — must add the new security/username named parameters to its overrides. Classes that don't override those methods are unaffected.

Migration (0.1.x -> 0.2.0) #

  1. The method channel and native plugin package/classes were renamed, so this plugin no longer conflicts with apps that also depend on flutter_esp_ble_prov. No Dart-side changes are needed for this.
  2. scanWifiNetworks(...) and EspProvisioningState.wifiNetworks now use EspWifiNetwork instead of String. Use network.ssid where you previously used the string; rssi and security are available on Android.
  3. Provisioning failures now throw typed PlatformExceptions (E_PROV_SESSION, E_PROV_CONFIG, E_PROV_AUTH, E_PROV_NETWORK_NOT_FOUND, E_PROV_FAILED) instead of resolving false. The bloc maps them to new EspProvisioningFailure values (sessionFailed, authenticationFailed, networkNotFound, provisioningFailed); exhaustive switches over EspProvisioningFailure must handle them.
  4. Timeouts now emit status: EspProvisioningStatus.error (previously the step status was kept with failure: timeout).
  5. The TIMEOUT constant was replaced by kEspDefaultConnectTimeout and kEspDefaultOperationBudget; EspProvisioningBloc now takes connectTimeout and requestTimeout parameters.
  6. Minimums raised: Dart ^3.5.0, Flutter 3.24+, flutter_bloc 9, permission_handler 12 (13.x is deferred until its AGP 9 / compileSdk 37 toolchain requirements are mainstream; pinning permission_handler: ^13.0.0 in your app will conflict with this plugin's ^12.0.3 constraint).

Migration (0.0.x -> 0.1.0) #

  1. Replace state.timedOut checks with state.failure == EspProvisioningFailure.timeout.
  2. For error UX and telemetry, use both:
    • state.failure for typed handling
    • state.errorCode and state.errorDetails for diagnostics
  3. If you call service methods directly, invoke cancelOperations() before starting a new scan/provision flow to cancel stale native operations.
  4. Replace direct src imports with:
    • import 'package:esp_provisioning_wifi/esp_provisioning_wifi.dart';

Usage #

BlocProvider(
  create: (_) => EspProvisioningBloc(),
  child: BlocConsumer<EspProvisioningBloc, EspProvisioningState>(
    listener: (_, state) {
      if (state.failure != EspProvisioningFailure.none) {
        // Use typed failure for user-facing behavior.
        debugPrint('Failure: ${state.failure} | ${state.errorMsg}');
      }
    },
    builder: (_, state) {
      return Text('Status: ${state.status}');
    },
  ),
)

Drive the flow by adding events (see example/lib/main.dart for a complete UI):

final bloc = context.read<EspProvisioningBloc>();

// 1. Scan for BLE devices advertising the given name prefix
//    ('PROV_' in the Espressif demos).
bloc.add(const EspProvisioningEventStart('PROV_'));
// -> status == bleScanned; pick a name from state.bluetoothDevices.

// 2. Connect to the chosen device and scan its visible WiFi networks. The
//    proof of possession must match the firmware ('abcd1234' in the demos).
bloc.add(const EspProvisioningEventBleSelected('PROV_XXXXXX', 'abcd1234'));
// -> status == wifiScanned; pick a network from state.wifiNetworks.

// 3. Provision the chosen network.
bloc.add(const EspProvisioningEventWifiSelected(
    'PROV_XXXXXX', 'abcd1234', 'my-ssid', 'my-wifi-password'));
// -> status == wifiProvisioned with state.wifiProvisioned == true on success.

Device firmware requirements #

The ESP32 must run Espressif's BLE provisioning scheme (e.g. wifi_prov_mgr from ESP-IDF, or the Arduino WiFiProv demo) using Security 1 (the default) or Security 2:

  • Security 1 requires a proof-of-possession (PoP) string. Pass the same PoP your firmware was configured with (the Espressif demos default to abcd1234, with device names prefixed PROV_).
  • Security 2 (SRP6a) requires the firmware's sec2 username and PoP; pass security: EspSecurityScheme.security2 and username alongside the PoP.

A wrong PoP surfaces as E_PROV_SESSION (EspProvisioningFailure.sessionFailed) on Android; on iOS it fails during connect. Security 0 (unauthenticated) firmware is not supported.

Requirements #

  • Dart ^3.5.0, Flutter 3.24+.
  • If your app also depends on permission_handler directly, use ^12.x — a ^13.0.0 pin conflicts with this plugin's ^12.0.3 constraint.

Android 6 (API level 23)+ #

Make sure your android/app/build.gradle has 23+ here:

defaultConfig {
    minSdkVersion Math.max(23, flutter.minSdkVersion)
}

This plugin resolves the Espressif provisioning library from JitPack, so every consuming app's Android build must be able to reach jitpack.io. Apps using the classic top-level allprojects { repositories { ... } } block add maven { url 'https://jitpack.io' } there; apps enforcing repositories via settings.gradle (dependencyResolutionManagement) add it like this:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

Bluetooth permissions are automatically requested by the library.

The plugin declares the android.hardware.bluetooth_le feature as NOT required, so it never hides your app from the Play Store on non-BLE devices. If your whole app requires BLE, declare the feature with android:required="true" in your own manifest.

iOS 13.0+ #

Add this in your ios/Runner/Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>Our app uses bluetooth to find, connect and transfer data between different devices</string>

This package requests Bluetooth permission through permission_handler, whose iOS Bluetooth support is compiled out by default. Enable it in your ios/Podfile post_install hook, otherwise the permission request always fails and the provisioning flow never starts:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',
        'PERMISSION_BLUETOOTH=1',
      ]
    end
  end
end

Notes #

Origins #

This library started as a Bloc wrapper over flutter_esp_ble_prov. The native Android and iOS provisioning implementations are now maintained inside this package.

Espressif provisioning libraries #

7
likes
140
points
368
downloads

Documentation

API reference

Publisher

verified publishersleepasloth.com

Weekly Downloads

Provision WiFi on Espressif ESP32 devices over Bluetooth LE from Flutter, with a Bloc API and native Android and iOS provisioning implementations.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

bloc_concurrency, equatable, flutter, flutter_bloc, permission_handler, plugin_platform_interface

More

Packages that depend on esp_provisioning_wifi

Packages that implement esp_provisioning_wifi