Skip to content

Conversation

haslinghuis
Copy link
Member

@haslinghuis haslinghuis commented Oct 14, 2025

Summary by CodeRabbit

  • New Features

    • Public device-detection and port-change handlers for reliable auto-detection and external control of flashing.
    • Exposes controls to start flashing and clear pending firmware from outside the module.
  • Bug Fixes

    • Prevents interruptions during active flashing or reboot sequences.
    • Clears board selection and buffered firmware when devices are removed.
    • Correctly handles already-connected devices during startup.
  • Improvements

    • Debounced port-change handling to reduce false detections and flapping.
    • Improved init and cleanup to avoid late actions and improve stability.

@haslinghuis haslinghuis added this to the 2025.12 milestone Oct 14, 2025
@haslinghuis haslinghuis self-assigned this Oct 14, 2025
@haslinghuis haslinghuis moved this to App in 2025.12.0 Oct 14, 2025
Copy link
Contributor

coderabbitai bot commented Oct 14, 2025

Walkthrough

Adds debounce-based port-change handling, public device handlers, and new public state to src/js/tabs/firmware_flasher.js; wires those handlers to EventBus, updates initialization to optionally auto-detect existing ports, exposes start/clear functions, and extends cleanup to unsubscribe handlers and clear pending debounces.

Changes

Cohort / File(s) Summary of changes
Public API / state exposure
src/js/tabs/firmware_flasher.js
Adds public members portChangeTimer and logHead; exposes startFlashing and clearBufferedFirmware on firmware_flasher (assigned during init).
Device detection handlers
src/js/tabs/firmware_flasher.js
Introduces detectedUsbDevice(device) and detectedSerialDevice(device) as public handlers and subscribes them to EventBus topics.
Port-change handling / debouncing
src/js/tabs/firmware_flasher.js
Adds onPortChange(port) implementing PORT_CHANGE_DEBOUNCE_MS debounce, conditionally triggers AutoDetect.verifyBoard, and respects flash_on_connect, connect_lock, and reboot state.
Device removal flow
src/js/tabs/firmware_flasher.js
Adds onDeviceRemoved(devicePath) public handler to clean up board selection and buffered firmware when safe (no active ops / not rebooting).
Initialization & cleanup wiring
src/js/tabs/firmware_flasher.js
Replaces internal handlers with public ones, subscribes them to EventBus on init, optionally triggers auto-detect after init if ports exist, unsubscribes handlers and clears pending portChangeTimer on cleanup.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as UI/External
  participant FF as firmware_flasher
  participant EB as EventBus
  participant AD as AutoDetect
  participant FL as Flashing

  rect rgb(240,245,255)
  note over FF: Initialization
  UI->>FF: initialize()
  FF->>EB: subscribe detectedUsbDevice / detectedSerialDevice / onPortChange / onDeviceRemoved
  FF->>FF: expose startFlashing / clearBufferedFirmware
  EB-->>FF: publish existing ports?
  FF->>AD: (optional) verifyBoard()
  end

  rect rgb(245,255,245)
  note over EB,FF: Debounced port change
  EB-->>FF: portChanged(port)
  FF->>FF: start/reset PORT_CHANGE_DEBOUNCE_MS timer
  alt connect_lock or reboot active
    FF-->>AD: skip verifyBoard()
  else flash_on_connect enabled
    FF->>AD: verifyBoard(port)
  end
  end

  rect rgb(255,248,240)
  note over EB,FF: Device removed
  EB-->>FF: deviceRemoved(path)
  alt no active operation and not rebooting
    FF->>FF: clear board selection
    FF->>FF: clearBufferedFirmware()
  else
    FF-->>FF: defer cleanup
  end
  end

  rect rgb(255,240,245)
  note over UI,FF: Flashing guard
  UI->>FF: startFlashing()
  alt connect_lock free
    FF->>FL: begin flashing
  else
    FF-->>UI: busy/reject
  end
  end

  rect rgb(240,240,240)
  note over FF: Cleanup
  UI->>FF: cleanup()
  FF->>EB: unsubscribe handlers
  FF->>FF: clear pending portChangeTimer
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Fix flash state #4653 — Modifies src/js/tabs/firmware_flasher.js and related flashing-state methods; likely touches similar public API changes.

Suggested reviewers

  • blckmn
  • VitroidFPV
  • KarateBrot

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is missing entirely and does not include any of the required sections from the repository template, leaving out a summary of changes, motivation, testing instructions, and other important considerations. Please add a descriptive pull request description following the repository template, including a clear summary of changes, motivation, test instructions, and any relevant issue references, and remove the placeholder instructions.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title succinctly and accurately describes the primary change by indicating a fix to the cleanup of firmware flasher event handlers and timers, matching the core modifications in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/js/tabs/firmware_flasher.js (1)

54-58: Fix logging placeholder usage

Template string includes “%s” which won’t be interpolated. Simplify to a single interpolated message.

-        console.log(
-            `${firmware_flasher.logHead} Reboot mode: %s, flash on connect`,
-            STM32.rebootMode,
-            isFlashOnConnect,
-        );
+        console.log(
+            `${firmware_flasher.logHead} Reboot mode: ${STM32.rebootMode}, flash on connect: ${isFlashOnConnect}`,
+        );
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ecb566 and 17162d3.

📒 Files selected for processing (1)
  • src/js/tabs/firmware_flasher.js (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: blckmn
PR: betaflight/betaflight-configurator#4583
File: src/js/tabs/firmware_flasher.js:949-961
Timestamp: 2025-09-02T07:45:48.606Z
Learning: In src/js/tabs/firmware_flasher.js, the config file loading code path after firmware loading (in the load_file click handler) cannot be reached when UF2 firmware is loaded, according to the maintainer blckmn. This code is maintained for backward compatibility with unified target settings and will be refactored in a future PR.
🧬 Code graph analysis (1)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (1)
  • PortHandler (16-50)
🔇 Additional comments (7)
src/js/tabs/firmware_flasher.js (7)

46-77: USB detection flow: good guards and reboot resume

Honors connect_lock, only clears it when resuming from reboot, and invokes startFlashing safely. Looks solid.


78-93: Serial detection: safe early-exit + guarded auto-detect

Early return under connect_lock and try/catch around AutoDetect is appropriate.


94-133: Debounced port-change handling is correct

Clears existing timer, re-checks connect_lock at timeout, and avoids auto-detect during reboot/flash-on-connect. Good defensive logic.


134-144: Removal handler correctly avoids interfering with active flows

Skips during connect_lock/reboot and clears board + buffered firmware otherwise. Good.


845-850: Exposing startFlashing/clearBufferedFirmware is appropriate

Makes public handlers callable by module-scoped listeners and tests. Good placement before event registration.


851-854: EventBus registrations are clean and symmetric with cleanup

Good move to centralize through public handlers for easy unregistration.


1594-1611: EventBus cleanup + timer clear: correct and complete

Handler references are correctly used for $off; debounce timer is cleared to prevent late execution. This addresses the event cleanup bug.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/js/tabs/firmware_flasher.js (1)

1571-1577: Address inconsistent auto-detect conditions and fix indentation.

This issue was previously flagged: The init-time auto-detect (line 1573) only checks !isFlashOnConnect, but the port-change path (line 109) also checks !STM32.rebootMode. This inconsistency can cause unexpected auto-detection when resuming from a reboot.

Additionally, ESLint reports indentation errors on lines 1572-1576 (expected 8 spaces but found 12-16).

Apply this diff to align the behavior with the port-change path and fix indentation:

-        
-            const isFlashOnConnect = $("input.flash_on_connect").is(":checked");
-            if (PortHandler.portAvailable && !isFlashOnConnect) {
-                console.log(`${self.logHead} 💥 Auto-detecting board for already connected device`);
-                AutoDetect.verifyBoard();
-            }
-        
+        const isFlashOnConnect = $("input.flash_on_connect").is(":checked");
+        if (PortHandler.portAvailable && !isFlashOnConnect && !STM32.rebootMode) {
+            console.log(`${self.logHead} Auto-detecting board for already connected device`);
+            AutoDetect.verifyBoard();
+        }
+
🧹 Nitpick comments (1)
src/js/tabs/firmware_flasher.js (1)

50-144: Consider timing guarantees for handler initialization.

The handlers use optional chaining (?.()) when calling startFlashing (line 75) and clearBufferedFirmware (line 143), suggesting these methods might not be available when the handlers execute. Since these methods are assigned during initialize() (lines 848-849) but the handlers are wired to EventBus at the same time, there's a potential race condition if events fire before initialization completes.

Consider either:

  1. Moving the EventBus subscriptions to after the method assignments (lines 851-854 should come after 848-849), or
  2. Adding explicit checks with meaningful error messages if the methods are unavailable

Otherwise, the debouncing logic and state management look solid.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17162d3 and 47386c4.

📒 Files selected for processing (1)
  • src/js/tabs/firmware_flasher.js (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (1)
  • PortHandler (16-50)
🪛 ESLint
src/js/tabs/firmware_flasher.js

[error] 1572-1572: Expected indentation of 8 spaces but found 12.

(indent)


[error] 1573-1573: Expected indentation of 8 spaces but found 12.

(indent)


[error] 1574-1574: Expected indentation of 12 spaces but found 16.

(indent)


[error] 1575-1575: Expected indentation of 12 spaces but found 16.

(indent)


[error] 1576-1576: Expected indentation of 8 spaces but found 12.

(indent)

🔇 Additional comments (3)
src/js/tabs/firmware_flasher.js (3)

845-854: LGTM! Method exposure and event wiring looks correct.

The code properly exposes internal functions to the module scope and wires up EventBus subscriptions. The ordering is correct: method assignments (lines 848-849) happen before EventBus subscriptions (lines 851-854), which prevents the race condition mentioned in the previous comment.


1596-1612: Excellent cleanup implementation!

The cleanup logic properly:

  • Unsubscribes all EventBus handlers that were registered during initialization
  • Clears any pending portChangeTimer to prevent late callbacks after cleanup
  • Uses a helper function to avoid code duplication

This thoroughly addresses the event cleanup concerns mentioned in the PR title.


50-77: LGTM! Subtle reboot and connect-lock handling is correct.

The logic carefully distinguishes between resuming from a reboot and flash-on-connect scenarios:

  • Captures the reboot state before clearing it (line 68)
  • Only clears GUI.connect_lock when resuming from a reboot (lines 72-74), preventing interference with other active operations
  • Properly handles both the reboot resumption path and the flash-on-connect path

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47386c4 and ba3593d.

📒 Files selected for processing (1)
  • src/js/tabs/firmware_flasher.js (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (2)
  • devicePath (110-110)
  • PortHandler (16-50)
🪛 ESLint
src/js/tabs/firmware_flasher.js

[error] 1571-1571: Expected indentation of 8 spaces but found 12.

(indent)


[error] 1572-1572: Expected indentation of 8 spaces but found 12.

(indent)


[error] 1573-1573: Expected indentation of 12 spaces but found 16.

(indent)


[error] 1574-1574: Expected indentation of 12 spaces but found 16.

(indent)


[error] 1575-1575: Expected indentation of 8 spaces but found 12.

(indent)

🔇 Additional comments (4)
src/js/tabs/firmware_flasher.js (4)

24-25: LGTM! Well-chosen debounce duration.

The 500ms debounce constant is appropriate for port-change events, balancing responsiveness with noise reduction from rapid port state transitions.


45-143: Excellent implementation of debounced event handlers.

The new public handlers provide robust port and device management:

  • Debouncing (lines 95-98, 112-123): Properly clears pending timers and re-validates connect_lock state on timeout to prevent stale operations.
  • State management: Correctly handles GUI.connect_lock and STM32.rebootMode to avoid interfering with active operations.
  • Defensive programming (lines 74, 142): Appropriate use of optional chaining (?.) since startFlashing and clearBufferedFirmware are exposed after handlers register.
  • Logging: Consistent use of logHead prefix aids debugging.

844-853: LGTM! Proper function exposure and event wiring.

Exposing startFlashing and clearBufferedFirmware on the firmware_flasher object before subscribing handlers ensures external callers and event handlers can safely reference these functions, even if called before initialize() completes.


1590-1614: LGTM! Thorough cleanup implementation.

The cleanup function properly manages lifecycle:

  • Event unsubscription (lines 1595-1605): DRY helper function unsubscribes all registered handlers.
  • Timer cleanup (lines 1607-1611): Clears pending debounce timer to prevent late execution after tab switch.
  • Defensive checks: Validates handler existence before unsubscribing.

This prevents memory leaks and ensures clean tab transitions.

Copy link

Copy link
Contributor

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/js/tabs/firmware_flasher.js (1)

118-118: Consider extracting complex condition for readability.

The condition on line 118 checks four separate criteria. Extracting it to a named variable would improve clarity:

+        const shouldAutoDetect = port && port !== "0" && 
+                                 !$("input.flash_on_connect").is(":checked") && 
+                                 !STM32.rebootMode;
-        if (port && port !== "0" && $("input.flash_on_connect").is(":checked") === false && !STM32.rebootMode) {
+        if (shouldAutoDetect) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f934bc and a75eb96.

📒 Files selected for processing (1)
  • src/js/tabs/firmware_flasher.js (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/js/tabs/firmware_flasher.js (3)
src/js/protocols/webstm32.js (1)
  • STM32 (1007-1007)
src/js/gui.js (1)
  • GUI (473-473)
src/js/port_handler.js (1)
  • PortHandler (16-50)
🔇 Additional comments (3)
src/js/tabs/firmware_flasher.js (3)

49-153: Well-implemented event handlers with proper guards and debouncing.

The new event handlers demonstrate solid defensive programming:

  • Concurrency control: All handlers check GUI.connect_lock to prevent interference with active operations
  • Reboot state management: detectedUsbDevice correctly clears connect_lock only when resuming from a reboot (lines 66-73), avoiding accidental interruption of flash-on-connect flows
  • Debouncing: onPortChange implements proper debouncing by clearing stale timers and re-checking connect_lock inside the timeout callback (lines 105-108, 122-127)
  • Error resilience: Try-catch blocks and optional chaining (?.()) prevent crashes when functions aren't yet initialized

The logic correctly handles the interaction between reboot mode, flash-on-connect, and port changes.


854-863: Good initialization pattern for handler-function coupling.

The code correctly exposes locally-scoped functions before subscribing event handlers. This ordering ensures handlers can immediately call firmware_flasher.startFlashing?.() and firmware_flasher.clearBufferedFirmware?.() even if events fire synchronously during subscription.

The comment clearly explains the rationale, and the optional chaining provides safety if handlers somehow run before initialization completes.


1604-1620: Comprehensive cleanup prevents memory leaks and late callbacks.

The cleanup implementation is thorough:

  • Event unsubscription: All four EventBus handlers are properly unsubscribed via the helper function
  • Timer cancellation: Clearing portChangeTimer (lines 1617-1620) ensures the debounced callback cannot fire after the tab closes, preventing potential errors or stale state updates
  • Clean pattern: The cleanupHandler helper makes the unsubscription logic readable and maintainable

This properly handles the lifecycle of tab-scoped event handlers.

Copy link
Member

@nerdCopter nerdCopter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • approving based on #4661 testing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: App

Development

Successfully merging this pull request may close these issues.

3 participants