From 471b559e20ff148f736c69e159200c0ce68e88bc Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 12 May 2026 11:17:43 +0200 Subject: [PATCH 1/3] feat(fota): add eraseFirmwareSlot method --- .../Flutter/GeneratedPluginRegistrant.swift | 2 ++ lib/src/fota/firmware_slot_manager_impl.dart | 18 ++++++++++++++---- .../fota_slot_info_capability.dart | 12 ++++++++++++ pubspec.yaml | 5 ++++- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 317a6682..301491e5 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,10 +7,12 @@ import Foundation import file_picker import flutter_archive +import mcumgr_flutter import universal_ble func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FlutterArchivePlugin.register(with: registry.registrar(forPlugin: "FlutterArchivePlugin")) + McumgrFlutterPlugin.register(with: registry.registrar(forPlugin: "McumgrFlutterPlugin")) UniversalBlePlugin.register(with: registry.registrar(forPlugin: "UniversalBlePlugin")) } diff --git a/lib/src/fota/firmware_slot_manager_impl.dart b/lib/src/fota/firmware_slot_manager_impl.dart index c162b253..941f1397 100644 --- a/lib/src/fota/firmware_slot_manager_impl.dart +++ b/lib/src/fota/firmware_slot_manager_impl.dart @@ -68,15 +68,25 @@ class McuMgrFotaSlotInfoManager implements FotaSlotInfoCapability { @override Future> readFirmwareSlots() async { - final updateManager = await _updateManagerFactory.getUpdateManager(_deviceId); + final updateManager = + await _updateManagerFactory.getUpdateManager(_deviceId); try { final slots = await updateManager.readImageList(); if (slots == null) { return const []; } - return slots - .map(FirmwareSlotInfo.fromImageSlot) - .toList(growable: false); + return slots.map(FirmwareSlotInfo.fromImageSlot).toList(growable: false); + } finally { + await updateManager.kill(); + } + } + + @override + Future eraseFirmwareSlot({int? channel}) async { + final updateManager = + await _updateManagerFactory.getUpdateManager(_deviceId); + try { + await updateManager.erase(channel); } finally { await updateManager.kill(); } diff --git a/lib/src/models/capabilities/fota_slot_info_capability.dart b/lib/src/models/capabilities/fota_slot_info_capability.dart index 0203c2ef..9c3a15ae 100644 --- a/lib/src/models/capabilities/fota_slot_info_capability.dart +++ b/lib/src/models/capabilities/fota_slot_info_capability.dart @@ -9,6 +9,18 @@ import 'package:mcumgr_flutter/mcumgr_flutter.dart'; abstract class FotaSlotInfoCapability { /// Reads the firmware images or slots currently reported by the wearable. Future> readFirmwareSlots(); + + /// Erases an inactive firmware image slot on the wearable. + /// + /// When [channel] is omitted, the underlying firmware backend erases its + /// default secondary image slot. When [channel] is provided, the erase request + /// targets that raw mcumgr image slot channel. + /// + /// Devices reject erase requests for slots that contain a confirmed image, an + /// image pending test on the next reboot, or an active split-image slot. Use + /// [readFirmwareSlots] first when the UI needs to decide whether erasing is + /// available. + Future eraseFirmwareSlot({int? channel}); } /// Snapshot of one firmware image slot reported by the wearable. diff --git a/pubspec.yaml b/pubspec.yaml index 99d92a30..7fb1c2fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,10 @@ dependencies: flutter_bloc: ^9.1.1 http: ^1.1.2 json_annotation: ^4.8.1 - mcumgr_flutter: ^0.6.1 + mcumgr_flutter: + git: + url: https://github.com/DennisMoschina/Flutter-nRF-Connect-Device-Manager.git + ref: master path_provider: ^2.1.1 provider: ^6.1.1 rxdart: ^0.28.0 From 6b0bc3ff03f83b7f84a6ce2f0928aa465d09d916 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 12 May 2026 11:18:22 +0200 Subject: [PATCH 2/3] doc(fota): added documentation for image erase --- doc/CAPABILITIES.md | 1 + doc/FOTA.md | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/CAPABILITIES.md b/doc/CAPABILITIES.md index e2b68300..17be381c 100644 --- a/doc/CAPABILITIES.md +++ b/doc/CAPABILITIES.md @@ -200,6 +200,7 @@ Provides firmware slot or image-table state for FOTA backends that expose it. final slotInfo = wearable.getCapability(); if (slotInfo != null) { final slots = await slotInfo.readFirmwareSlots(); + await slotInfo.eraseFirmwareSlot(); } ``` diff --git a/doc/FOTA.md b/doc/FOTA.md index f033b50e..4bc92e5f 100644 --- a/doc/FOTA.md +++ b/doc/FOTA.md @@ -259,6 +259,25 @@ Each `FirmwareSlotInfo` contains: This is useful when you want to show the current primary and secondary images before or after an update. +## Erase An Inactive Firmware Slot + +Slot-aware wearables can erase an inactive firmware image slot through +`FotaSlotInfoCapability`: + +```dart +final slotInfo = wearable.getCapability(); +if (slotInfo != null) { + await slotInfo.eraseFirmwareSlot(); + await slotInfo.eraseFirmwareSlot(channel: 1); +} +``` + +When `channel` is omitted, the firmware backend erases its default secondary +image slot. When `channel` is provided, the erase request targets that raw +mcumgr image slot channel. Devices reject erase requests for slots that contain +a confirmed image, an image pending test on the next reboot, or an active +split-image slot. + ## What Happens Internally You do not need to call the lower-level handler classes directly, but it helps to know what `UpdateBloc` is doing: @@ -367,7 +386,7 @@ Recommended UX: - `UnifiedFirmwareRepository` caches results for 15 minutes unless you request a refresh - The current upload path uses `mcumgr_flutter` under the hood - `FotaSlotInfoCapability` is optional and only available on wearables whose firmware backend exposes slot-style state -- `mcumgr_flutter 0.6.1` does not expose an API to erase an individual image slot, so this library does not currently offer slot erase either +- The current local `mcumgr_flutter` integration exposes slot erase through `FotaSlotInfoCapability.eraseFirmwareSlot` ## Related Source Files From 7be633468d1730ca58b9df97dec462829fa399f0 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 12 May 2026 11:18:48 +0200 Subject: [PATCH 3/3] chore(example): changed dependency of mcumgr_flutter to be compatible with library --- example/pubspec.lock | 33 +++++++++++++++++++++++++-------- example/pubspec.yaml | 5 ++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index a88ca564..957ea6bf 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff + url: "https://pub.dev" + source: hosted + version: "4.0.9" args: dependency: transitive description: @@ -315,11 +323,12 @@ packages: mcumgr_flutter: dependency: "direct main" description: - name: mcumgr_flutter - sha256: fbf2f621dea23dd5dc70494e700c5d4706010841b9e68739ba563cbd88c7e8ba - url: "https://pub.dev" - source: hosted - version: "0.6.1" + path: "." + ref: master + resolved-ref: "7bec874051397e0e0dcbce4a1a660e14be5a3eb3" + url: "https://github.com/DennisMoschina/Flutter-nRF-Connect-Device-Manager.git" + source: git + version: "0.8.1" meta: dependency: "direct main" description: @@ -350,7 +359,7 @@ packages: path: ".." relative: true source: path - version: "2.3.5" + version: "2.3.6" path: dependency: transitive description: @@ -487,14 +496,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + posix: + dependency: transitive + description: + name: posix + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + url: "https://pub.dev" + source: hosted + version: "6.5.0" protobuf: dependency: transitive description: name: protobuf - sha256: de9c9eb2c33f8e933a42932fe1dc504800ca45ebc3d673e6ed7f39754ee4053e + sha256: "75ec242d22e950bdcc79ee38dd520ce4ee0bc491d7fadc4ea47694604d22bf06" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "6.0.0" provider: dependency: "direct main" description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 2f208ccd..5e4c81fa 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -48,7 +48,10 @@ dependencies: flutter_svg: ^2.0.17 provider: ^6.1.5 file_picker: ^10.3.7 - mcumgr_flutter: ^0.6.1 + mcumgr_flutter: + git: + url: https://github.com/DennisMoschina/Flutter-nRF-Connect-Device-Manager.git + ref: master flutter_bloc: ^9.1.1 dev_dependencies: