Skip to content

Commit bc3f87f

Browse files
committed
[docs] Freeze connected board identity display
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent 0e059aa commit bc3f87f

3 files changed

Lines changed: 18 additions & 4 deletions

File tree

‎docs/specifications/App/TDD.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ the host `file` classifier reports as `script text executable` (BLD-11).
311311

312312
### 4.8 `lib/connect/` — scan/connect flow UI
313313

314-
**Responsibility:** present a scan filtered to the PyBLE service UUID, listing each board by its **advertised name + RSSI** — the user-set device **label** when set, otherwise the default `PyBLE-XXXX` ([§7.1](#71-scan--connect)) — never a raw list; drive connect (GATT → TX subscribe → MTU 247 → INFO/HELLO → show `DeviceInfo` incl. `deviceId`/`label`) before enabling editor/console/file actions; surface permission/adapter state with localized rationale; list and reconnect saved boards; refuse an unsupported `proto_version` with an update prompt; scan→connect→use with no pairing-code/account/lease. It also hosts the post-connect **screenless-identity** UX: a **rename-board** action ([§11.5](#115-screenless-identity--rename--identify)) and a cap-gated **Identify** action.
314+
**Responsibility:** present a scan filtered to the PyBLE service UUID, listing each board by its **advertised name + RSSI** — the user-set device **label** when set, otherwise the default `PyBLE-XXXX` ([§7.1](#71-scan--connect)) — never a raw list; drive connect (GATT → TX subscribe → MTU 247 → INFO/HELLO → show `DeviceInfo` incl. `deviceId`/`agentVersion`/`label`) before enabling editor/console/file actions; surface permission/adapter state with localized rationale; list and reconnect saved boards; refuse an unsupported `proto_version` with an update prompt; scan→connect→use with no pairing-code/account/lease. It also hosts the post-connect **screenless-identity** UX: a **rename-board** action ([§11.5](#115-screenless-identity--rename--identify)) and a cap-gated **Identify** action.
315315

316316
**Screenless-identity UX (FR-CONN-10/11/12, SEC-8/9):**
317317

@@ -324,7 +324,7 @@ the host `file` classifier reports as `script text executable` (BLD-11).
324324
- **Neutral scan seam (`lib/pble`).** `class ScanHit { final String id; final String name; final int rssi; }` — the advertised board, retyped 1:1 off `BleScanResult` (`name` is the advertised name **verbatim**: the user-set label, else `PyBLE-XXXX`; value equality by `id`). `abstract interface class Scanner { Stream<List<ScanHit>> scan({Duration? timeout}); Future<void> stopScan(); }` — emits an **accumulating, deduped-by-`id`, latest-RSSI snapshot list** (accumulation lives in `lib/pble`, not the UI). A shipped `FakeScanner implements Scanner` scripts snapshots for tests. **This supersedes the illustrative `DiscoveredBoard`/`Scanner`-in-`lib/connect` sketch** — the seam is a neutral `lib/pble` type, exported through the `pble` barrel.
325325
- **Readiness seam.** `BleReadinessSource` (`BleReadiness current` + `Stream<BleReadiness> readiness`, [§7.4](#74-permissions--platform-quirks)) is **re-exported through the `pble` barrel** (the enum already is), so the connect UI observes adapter/permission transitions without importing `lib/ble`.
326326
- **`ConnectionManager` (neutral, `lib/pble`).** Holds the session; composes a `Scanner`, a `BleReadinessSource`, and a `ConnectionFactory` (`typedef ConnectionFactory = Future<Connection> Function(String deviceId)`). Surface: `Connection get connection` (the **stable facade**, [§6.2](#62-binding-pattern)); `Stream<List<ScanHit>> get scanResults`; `BleReadinessSource get readiness`; `ValueListenable<ConnectPhase> get phase`; `ScanHit? get selected`; `Object? get lastError`; and `startScan({Duration? timeout})` / `stopScan()` / `connect(String id)` / `disconnect()` / `dispose()`. `enum ConnectPhase { idle, scanning, connecting, connected, failed }` is the **connect-flow session phase — distinct from `ConnState`** (`scanning`/`connected` are deliberately not `ConnState` members, [§5.2](#52-shared-types)). `connect(id)` calls the `ConnectionFactory`, publishes the resulting `Connection`, and lets `PbleConnection.fromLink`'s link observation drive `connecting → ready`. The production `PbleConnectionManager.production({required String appName, required String appVersion})` wires the real `flutter_blue_plus` stack (`FbpBleAdapter` as scan source **and** connector, `FbpBleReadinessSource`) **internally**, so `main.dart` names no `lib/ble` type ([§6.2](#62-binding-pattern)).
327-
- **Connect controller + view (`lib/connect`).** A `ConnectController` (Riverpod `Notifier`, ADR-0007) projects the manager into an immutable `ConnectState { ConnectPhase phase, BleReadiness readiness, List<ScanHit> hits, ScanHit? selected, DeviceInfo? deviceInfo, Object? error }` (with `int get hitCount`), exposing `startScan`/`stopScan`/`connect(id)`/`disconnect`. On reaching `connected` it reads `connection.deviceInfo()` and stores it — the **DeviceInfo proof** the UI shows. `ConnectScreen` (the surface, hosted by `lib/app/pages/connect_page.dart` as a thin delegator) renders: the readiness banner (adapter-off / unauthorized / unsupported with localized rationale, FR-CONNECT-3); the scan CTA → live `ScanHit` list (name + RSSI, FR-CONNECT-1) → connecting indicator → connected `DeviceInfo` card (chip / mpy / free-mem, FR-CONNECT-2) → disconnect; the unsupported-`proto_version` update prompt (FR-CONNECT-6); and a **live diagnostics panel** (adapter state, scanning yes/no, hit count, last-error detail) for on-hardware debugging (FR-CONN-6).
327+
- **Connect controller + view (`lib/connect`).** A `ConnectController` (Riverpod `Notifier`, ADR-0007) projects the manager into an immutable `ConnectState { ConnectPhase phase, BleReadiness readiness, List<ScanHit> hits, ScanHit? selected, DeviceInfo? deviceInfo, Object? error }` (with `int get hitCount`), exposing `startScan`/`stopScan`/`connect(id)`/`disconnect`. On reaching `connected` it reads `connection.deviceInfo()` and stores it — the **DeviceInfo proof** the UI shows. `ConnectScreen` (the surface, hosted by `lib/app/pages/connect_page.dart` as a thin delegator) renders: the readiness banner (adapter-off / unauthorized / unsupported with localized rationale, FR-CONNECT-3); the scan CTA → live `ScanHit` list (name + RSSI, FR-CONNECT-1) → connecting indicator → connected `DeviceInfo` card (board ID / PyBLE firmware / chip / mpy / free-mem, FR-CONNECT-2) → disconnect; the unsupported-`proto_version` update prompt (FR-CONNECT-6); and a **live diagnostics panel** (adapter state, scanning yes/no, hit count, last-error detail) for on-hardware debugging (FR-CONN-6). The board-ID row reads only `DeviceInfo.deviceId` (never `ScanHit.id`); board ID and agent version are verbatim technical values. Empty compatibility defaults render localized **Not reported** rows and do not gate the connection.
328328
- **Scope of this freeze.** Delivers FR-CONNECT-1/2/3/5/6 + FR-CONN-6. **Saved-boards persistence** (FR-CONNECT-4 list/reconnect-by-remembered-id) depends on Drift `board_ref` (`lib/data`, A-24) which does not exist yet — auto-reattempt on an *unexpected* drop works today (A-03 `ReconnectingBleLink` + re-HELLO), but the persisted saved-board **list** is A-24/S5. The **screenless-identity UX** (rename / Identify, `setLabel`/`setIdentifyLed`/`identify`) is A-36/A-37/S7 and is **not** in this increment.
329329

330330
**Public API (sketch):** `ConnectController` (Riverpod `Notifier`) over the neutral `ConnectionManager` above; plus (A-36/A-37) `renameBoard(String label)`, `configureIdentifyLed(IdentifyLed)`, and `identify()` delegating to `Connection`.

‎docs/specifications/App/specs.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,12 +809,19 @@ implementation:
809809
### 4.14 Scan/Connect Flow UI (`lib/connect/`) — FR-CONNECT
810810

811811
- **FR-CONNECT-1** — The connect screen MUST present a scan **filtered to the PyBLE service UUID**, listing each board by its **advertised name with RSSI** — the user-set device **label** when one is set, otherwise the default `PyBLE-XXXX` ([protocol.md §2](../protocol.md#2-ble-transport-gatt)) — and MUST NOT present a raw, unfiltered BLE device list. MUST (*source: PRD §9.6, §19.3, §8.1, protocol.md §2; verify: widget; story: A-22*)
812-
- **FR-CONNECT-2** — On Connect, the app MUST open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, and display `DeviceInfo` (chip, MicroPython version, free memory) before enabling editor/console/file actions. MUST (*source: PRD §9.6, §19.3, §8.1; verify: integration; story: A-22*)
812+
- **FR-CONNECT-2** — On Connect, the app MUST open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, and display `DeviceInfo` (display-only board ID, PyBLE agent firmware version, chip, MicroPython version, and free memory) before enabling editor/console/file actions. The board ID MUST be `DeviceInfo.deviceId`, never the platform scan identifier; board ID and agent version MUST be rendered verbatim, MUST NOT be derived or used as an authorization/profile gate, and an empty value MUST render a localized **Not reported** fallback without blocking use. MUST (*source: PRD §9.6, §19.3, §8.1, protocol.md §7; verify: widget/integration; story: A-22*)
813813
- **FR-CONNECT-3** — The screen MUST surface BLE permission and adapter state with localized rationale (iOS `NSBluetoothAlwaysUsageDescription`; Android `BLUETOOTH_SCAN`/`BLUETOOTH_CONNECT` + older-Android location). MUST (*source: PRD §9.6, §19.3, app.md §5; verify: widget/integration; story: A-04/A-22*)
814814
- **FR-CONNECT-4** — The screen MUST list **saved boards** and reconnect them by remembered identifier; on link loss the app MUST auto-reattempt and in-flight transfers MUST resume per [protocol.md §5](../protocol.md#5-file-transfer-the-reliability-core). MUST (*source: PRD §8.1, §19.3, §13.1; verify: integration; story: A-03/A-22*)
815815
- **FR-CONNECT-5** — The flow MUST be scan → connect → use only: no pairing-code step, no account, no session-lock/heartbeat, and no board-identity gating (the non-goals of [PRD §4.3](../prd.md)). MUST (*source: PRD §8.1, §9.6, §19.3, §4.3; verify: widget; story: A-22*)
816816
- **FR-CONNECT-6** — The connect flow MUST refuse a board with an unsupported `proto_version` and prompt to update the firmware/app rather than failing silently. MUST (*source: PRD §18.3; verify: widget/integration; story: A-10/A-22*)
817817

818+
**Connected Board-info identity slice — FROZEN (`[docs]` 2026-08-22, additive to FR-CONNECT-2).** The testable acceptance criteria are:
819+
820+
- **AC-1 (board ID source)** — The Board-info card MUST label and show `DeviceInfo.deviceId` verbatim. It MUST NOT expose `ScanHit.id`, which is a platform connection identifier (an iOS UUID or Android MAC-form string), or substitute the advertised name/label. MUST
821+
- **AC-2 (firmware distinction)** — The card MUST label and show `DeviceInfo.agentVersion` verbatim as **PyBLE firmware**, distinct from the existing **MicroPython** version row. It MUST NOT derive the value from app metadata or `mpyVersion`. MUST
822+
- **AC-3 (additive compatibility)** — When either additive field is empty, its row MUST remain visible with a localized **Not reported** value; absence MUST NOT fail the connection, hide the existing DeviceInfo proof, or gate any action. MUST
823+
- **AC-4 (existing proof retained)** — The chip, MicroPython version, free-memory value, and Disconnect action MUST remain present and retain their current semantics. MUST
824+
818825
### 4.15 About & Open-Source Information (`lib/app/`) — FR-ABOUT
819826

820827
**FROZEN (X-11 About-page subset · `[docs]` 2026-07-28).** This subset makes

‎docs/specifications/app.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Widgets receive a `Connection` (or narrow callbacks derived from it). Tests inje
7474
## 4. Connect flow
7575

7676
1. **Scan** — `flutter_blue_plus` scan filtered to the PyBLE service UUID; show advertised `PyBLE-XXXX` names + RSSI.
77-
2. **Connect** — open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, show `DeviceInfo`.
77+
2. **Connect** — open GATT, subscribe to TX notify, request MTU 247, read INFO / send HELLO, show `DeviceInfo`, including its display-only board ID and PyBLE agent firmware version.
7878
3. **Use** — editor/console/files bind to the `Connection`.
7979
4. **Reconnect** — on link loss, auto-reattempt; in-flight file transfers resume via PBLE/1 §5. Saved boards reconnect by remembered identifier.
8080

@@ -83,6 +83,13 @@ renders unknown future values verbatim and continues from advertised PBLE/1
8383
capabilities; a missing in-app pin reference is informational and MUST NOT
8484
block the connection.
8585

86+
The connected Board-info card renders `DeviceInfo.deviceId` as **Board ID** and
87+
`DeviceInfo.agentVersion` as **PyBLE firmware**, verbatim. It MUST NOT substitute
88+
the platform scan identifier, derive either value, or use either value as an
89+
authorization/profile gate. When a legacy or nonconforming agent reports an
90+
empty value, the card shows a localized **Not reported** fallback and keeps the
91+
connection usable.
92+
8693
`DeviceInfo.chip == "esp32-s3"` does not identify which provisioning image is
8794
installed and cannot distinguish the lean `esp32-s3-n16r8` image from the
8895
separate `waveshare-esp32-s3-lcd-147b` image. The app therefore MUST NOT infer

0 commit comments

Comments
 (0)