Skip to content

Feature/better ota upgrade - #154

Open
Aeyohan wants to merge 6 commits into
mainfrom
feature/better_ota_upgrade
Open

Feature/better ota upgrade#154
Aeyohan wants to merge 6 commits into
mainfrom
feature/better_ota_upgrade

Conversation

@Aeyohan

@Aeyohan Aeyohan commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

A few different OTA upgrade improvements:

  • Properly handle cross-app upgrades (for both --single and normal releases). Disabled by default, and opt in with --cross-app
  • Added an error message when patch upload fails
  • Updated the tool to handle multiple gateways:
    • Multiple gateways run main loop in different threads
    • No patch transfer slowdown observed (no bottlenecked by GIL)
    • Device connection mutex (other gateways will not try to connect to a tag if a connection/activity is currently ongoing)
    • Usage: add multiple ports with --server-port (port1) (port2) (...)
    • Updated state & UI to handle multiple devices

Tested with --single and -r options (incidentally using --cross-app since the only tags I could easily use weren't downgrading (presumably rollback protection) so cross app was easier to go between.

sample:
image

Aeyohan added 6 commits July 14, 2026 12:58
Tool had partial support to update devices using diffs of a different
application id.
Formally extended support for cross-application upgrades by:
* Disabling using cross-app diff by default, and enabling with cli arg.
* When enabled, allowing devices with a different app-id to be upgraded
using a diff from a different app-id

Usage: add `--cross-app` to `ota_upgrade.py --cross-app -r ...` to allow
devices running a different app to be upgraded.

Notes:
* `--cross-app` is not required when the diff selected by `--single` is
already for a different application id.
* As a result, it has no effect when used with `--single`.
* Requires a diff to be already generated for the release. This does not
create one.
Improved patch upload failure error handing.
When uploading a patch fails, prints out an error message.
e.g. when using `--single`, if the gateway and upgrade target are on
different networks, The RPC fails with `-EIO`.
The generic error is now printed to the console.
Decoupled update logic from main run loop.
This will allow the update loop to be run on multiple threads.

Signed-off-by: Aeyohan Furtado <aeyohan@embeint.com>
Update relevant functions to manually specify which `LocalClient` is
being used.
This will allow relevant functions to be called to a specific gateway
client.

Signed-off-by: Aeyohan Furtado <aeyohan@embeint.com>
Updated multiple state variables to accommodate handling multi-device
state.
Moved connection, copying and uploading to their own state variables.
Updated table UI to use new state variables.
Updated UI redraws to not depend on state change.

Signed-off-by: Aeyohan Furtado <aeyohan@embeint.com>
Enable mutliple gateway server ports.
Each gateway is handled by a separate thread.
UI can handle displaying status on multiple gateways.

Signed-off-by: Aeyohan Furtado <aeyohan@embeint.com>
@Aeyohan
Aeyohan force-pushed the feature/better_ota_upgrade branch from 786bc3a to 5928476 Compare July 14, 2026 03:30
@Aeyohan

Aeyohan commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

fix multi line fstring for CI

@Aeyohan
Aeyohan requested a review from JordanYates July 14, 2026 03:36

@JordanYates JordanYates left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you remove the first commit from this PR as well, it could come in later with extra checks, but its pretty dangerous in a development context, which is where this script runs.

If you have cross-app diffs, you probably have a few devices running said apps for testing purposes. The option will automatically force them all to the same app, which is not necessarily what you want.

except RuntimeError as e:
sys.exit(str(e))
for client in unavailable:
self._clients.remove(client)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is being done in a few places, it should be done once after the print output at the start of the function

meta.add_column()
meta.add_row(table)
meta.add_row(Status(self.state))
if self._state_connecting:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of this, I think it makes more sense to roll the state for each GW into a line:

        status = Table.grid(expand=True)
        status.add_column()
        status.add_column()
        status.add_column(ratio=1)
        for gw in gateways:
            status.add_row("GW ADDR ", Status(self.state), self.progress)

        meta = Table.grid()
        meta.add_row(table)
        meta.add_row(status)

        return meta

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Does GW_ADDR, refer to the socket port?
Otherwise if this is for the gateway's Infuse ID, I initially did think of something like this, but I don't know how to easily get it though?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The socket address/port, maybe in the form 224.1.1.1:5791? If it looks confusing maybe just the port

try:
self._state_uploading.add(source.infuse_id)
live.update(self.progress_table())
# self.state_update(live, f"Uploading patch file to {source.infuse_id:016X}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

commented out line?

@Aeyohan

Aeyohan commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Can you remove the first commit from this PR as well, it could come in later with extra checks, but its pretty dangerous in a development context, which is where this script runs.

If you have cross-app diffs, you probably have a few devices running said apps for testing purposes. The option will automatically force them all to the same app, which is not necessarily what you want.

Even though it's locked behind needing to specify --cross-app?
Regardless if it needs more checks or fine tuning around which app IDs are acceptable, I'll remove anything --cross-app related, but does it make sense to at least keep the --single functionality? Currently when selecting a diff from a given app to a different app, unintuitively it won't ever end up sending the patch (script criteria filters on same-to-same app id). Here it'd only ever happen when a specific app to other specific app is specified.

@JordanYates

JordanYates commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Even though it's locked behind needing to specify --cross-app?

Yes, I am thinking an explicit warning through the user_confirm function that lists all the applications that will be updated if seen. But another PR, not this one.

but does it make sense to at least keep the --single functionality?

It makes sense to keep (probably with a similar warning as above), but its related to cross-app, so belongs in the same PR as that option PR.

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.

2 participants