Skip to content

Conversation

@IDisposable
Copy link
Contributor

@IDisposable IDisposable commented Sep 27, 2025

Improves the OTA (Over-The-Air) update process by adding better reporting and handling of reboot requirements.

  • More detailed logging, state management for reboots

@IDisposable
Copy link
Contributor Author

@adamshiervani Not actually sure how to go about testing this...

@ym ym self-requested a review September 29, 2025 10:59
@IDisposable IDisposable force-pushed the feat/better-auto-update branch from 2389544 to d71c887 Compare September 29, 2025 18:30
@adamshiervani
Copy link
Contributor

In the Makefile, you just set a low number and run ./dev_deploy.sh -i. If you check for updates with that, you should be able to get through the OTA process.

I'll try that out now.

Copy link
Contributor

@adamshiervani adamshiervani left a comment

Choose a reason for hiding this comment

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

The OTA mechanism technically works. I was able to upgrade a device by lowering the version number in the Makefile. The code cleanup and the more granular state pushes to the client are great, but I have a few usability and robustness concerns.

The updated UI is confusing (I suspect flex-col is missing 😄). Despite the copy, it’s not immediately clear whether the reboot is automatic or requires the user to click “Reboot,” and it’s not obvious what happens if a reboot is already pending. All valid UX questions from the user’s perspective.

OTA Component SCR-20250930-mgco

With the current implementation, if the reboot + IP assignment takes longer than the WebSocket reconnect policy allows, the “Reconnect” and “Reboot” buttons can lead the client to stale, lazy-loaded settings routes that break the app.

Stale settings pages image

This PR aside, the only actual problem I noticed with OTA is the client-side state becomes desynchronized after a reboot: the client doesn’t perform a full refresh when the update completes. This is likely not caused by insufficient OTA-status events, but rather, the WebSocket reconnect threshold is being exceeded during the reboot/IP-assignment cycle (for example, DHCP can take a while). You can actually reproduce this by pulling the Ethernet cable during reboot -- the client hits the reconnect timeout and stops trying.

I would simply suggest extending the WebSocket reconnect threshold during updates to infinity. This way, the only thing that I can think of that could break the client-side part of the OTA mechanism is if DHCP assigned a different IP to the JetKVM, but that's fine for now.

@IDisposable IDisposable force-pushed the feat/better-auto-update branch from b97c52c to 70cd19d Compare October 1, 2025 16:52
@IDisposable
Copy link
Contributor Author

@adamshiervani clean-up complete and added exponential backoff and 2000 retries (so effectively 5.5 hours)

text="Reboot the KVM"
LeadingIcon={MdRestartAlt}
textAlign="center"
reloadDocument={true}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This forces a page reload as soon as the navigation is registered

text="Reconnect to KVM"
LeadingIcon={MdConnectWithoutContact}
textAlign="center"
reloadDocument={true}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This forces a page reload as soon as the navigation is registered

@IDisposable
Copy link
Contributor Author

DHCP assigned a different IP to the JetKVM, but that's fine for now.

In that case, we wouldn't be able to reconnect if they were using an IP, but if they were using a hostname or accessing through app.jetkvm.com it would eventually reconnect ;)

@IDisposable
Copy link
Contributor Author

@adamshiervani This should be ready for retest/review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR improves the OTA (Over-The-Air) update process by adding better reporting and handling of reboot requirements, including enhanced logging, state management for reboots, and UI changes to indicate reboot status with reconnect functionality.

  • Adds rebootNeeded and rebooting state management to track update progress through completion
  • Implements UI changes to show reboot status and provide manual reconnect/reboot buttons
  • Improves error handling and logging throughout the update process

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ui/src/routes/devices.$id.tsx Refactors authentication logic and improves WebSocket reconnection handling
ui/src/routes/devices.$id.settings.general.update.tsx Adds reboot state UI with reconnect and manual reboot buttons
ui/src/routes/devices.$id.settings.general.reboot.tsx Adds window reload on dialog close for proper state refresh
ui/src/routes/devices.$id.settings.access._index.tsx Fixes grammar in comment
ui/src/main.tsx Simplifies authentication check logic
ui/src/hooks/stores.ts Adds rebootNeeded and rebooting state fields to OTA store
ui/src/components/Button.tsx Adds reloadDocument prop support to LinkButton component
ota.go Improves error messages, logging, and adds reboot state management
main.go Enhances auto-update logic with better logging and timing

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@IDisposable IDisposable force-pushed the feat/better-auto-update branch 3 times, most recently from cfd9d3f to fe91742 Compare October 7, 2025 17:07
@IDisposable IDisposable force-pushed the feat/better-auto-update branch 4 times, most recently from b3385ee to 48f5cb3 Compare October 28, 2025 00:10
@IDisposable
Copy link
Contributor Author

Rebased and ready for review.

@IDisposable IDisposable requested a review from ym October 28, 2025 00:11
Add ability to request a reload to LinkButton and Link
Added exponential backoff to reconnection
Made the number of reconnect attempts settable
Doesn't attempt a reconnection if we intentionally disconnect
Make sure the fire-and-forget for TURN activity doesn't result in unhandled promise rejection.
Fix comment
Added force page reload to the onClose events of update/reboot
Ensure we get the correct UI version.
Fixed comment about the system update progress
Removed duplicate code between main and devices.$id
The isOnDevice and checkAuth can be leveraged from devices.$id.tsx
Extend retries to 2000 (which is 20000 seconds after backing off)
Also cleaned up the loader function error-state returns
@IDisposable IDisposable force-pushed the feat/better-auto-update branch from ffb5bb5 to 98f7233 Compare October 29, 2025 19:46
@adamshiervani adamshiervani merged commit 7955ee9 into jetkvm:dev Oct 29, 2025
4 checks passed
@IDisposable IDisposable deleted the feat/better-auto-update branch October 30, 2025 04:23
@tadic-luka
Copy link
Contributor

@IDisposable I'm not familiar with react/tsx that much but your changes introduced compilation errors:

src/routes/devices.$id.settings.advanced.tsx:317:19 - error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.

317                   await sleep(2000);
                      ~~~~~

  src/routes/devices.$id.settings.advanced.tsx:314:26
    314                 onClick={() => {
                                 ~~~~~~~
    Did you mean to mark this function as 'async'?

src/routes/devices.$id.settings.advanced.tsx:317:25 - error TS2304: Cannot find name 'sleep'.

317                   await sleep(2000);
                            ~~~~~

src/routes/devices.$id.settings.general.reboot.tsx:15:5 - error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.

15     await sleep(1000);
       ~~~~~

  src/routes/devices.$id.settings.general.reboot.tsx:12:31
    12   const onClose = useCallback(() => {
                                     ~~~~~~~
    Did you mean to mark this function as 'async'?

src/routes/devices.$id.settings.general.reboot.tsx:15:11 - error TS2304: Cannot find name 'sleep'.

15     await sleep(1000);
             ~~~~~

src/routes/devices.$id.settings.general.update.tsx:27:5 - error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.

27     await sleep(1000);
       ~~~~~

  src/routes/devices.$id.settings.general.update.tsx:24:31
    24   const onClose = useCallback(() => {
                                     ~~~~~~~
    Did you mean to mark this function as 'async'?


Found 5 errors in 3 files.

Errors  Files
     2  src/routes/devices.$id.settings.advanced.tsx:317
     2  src/routes/devices.$id.settings.general.reboot.tsx:15
     1  src/routes/devices.$id.settings.general.update.tsx:27
make: *** [Makefile:95: frontend] Error 2

I would say this is a regression and not an issue with the version of the npm or other build tool used to compile this code.

@IDisposable
Copy link
Contributor Author

Yikes, sorry about that, the rebase broke that code (it was an old PR). Thanks for the reminder to recompile after doing rebases 😄

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants