diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f5a874f..c6850cb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ 0.0.3 ===== -- Move from MANIFEST.MF to MANIFEST.JSON format for apps - wificonf: scan and connect to wifi in background thread so app stays responsive -- appstore: improve icon download handling +- appstore: add 'update' button if a new version of an app is available +- appstore: add 'restore' button to restore updated built-in apps to their original built-in version +- osupdate: show info about update and 'Start OS Update' before updating +- Introduce MANIFEST.JSON format for apps 0.0.2 ===== diff --git a/internal_filesystem/builtin/apps/com.example.osupdate/META-INF/MANIFEST.JSON b/internal_filesystem/builtin/apps/com.example.osupdate/META-INF/MANIFEST.JSON index edd988a9..2c203651 100644 --- a/internal_filesystem/builtin/apps/com.example.osupdate/META-INF/MANIFEST.JSON +++ b/internal_filesystem/builtin/apps/com.example.osupdate/META-INF/MANIFEST.JSON @@ -3,10 +3,10 @@ "publisher": "ACME Inc", "short_description": "Operating System Updater", "long_description": "", -"icon_url": "http://demo.lnpiggy.com:2121/apps/com.example.osupdate_0.0.1.mpk_icon_64x64.png", -"download_url": "http://demo.lnpiggy.com:2121/apps/com.example.osupdate_0.0.1.mpk", +"icon_url": "http://demo.lnpiggy.com:2121/apps/com.example.osupdate_0.0.2.mpk_icon_64x64.png", +"download_url": "http://demo.lnpiggy.com:2121/apps/com.example.osupdate_0.0.2.mpk", "fullname": "com.example.osupdate", -"version": "0.0.1", +"version": "0.0.2", "entrypoint": "assets/osupdate.py", "category": "osupdate" } diff --git a/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py b/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py index 08a06e0c..f906bd43 100644 --- a/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py +++ b/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py @@ -1,29 +1,38 @@ appscreen = lv.screen_active() +appscreen.clean() import lvgl as lv import ota.update from esp32 import Partition import urequests +import ujson import ota.status +import network +import time +import _thread -ota.status.status() -current = Partition(Partition.RUNNING) -current -next_partition = current.get_next_update() -next_partition +status_label=None +install_button=None -label = lv.label(appwindow) -label.set_text("OS Update: 0.00%") -label.align(lv.ALIGN.CENTER, 0, -30) - -progress_bar = lv.bar(appwindow) -progress_bar.set_size(200, 20) -progress_bar.align(lv.ALIGN.BOTTOM_MID, 0, -50) -progress_bar.set_range(0, 100) -progress_bar.set_value(0, lv.ANIM.OFF) # Custom OTA update with LVGL progress def update_with_lvgl(url): + global install_button, status_label + install_button.add_flag(lv.obj.FLAG.HIDDEN) # or change to cancel button? + status_label.set_text("Update in progress.\nNavigate away to cancel.") + ota.status.status() + current_partition = Partition(Partition.RUNNING) + print(f"Current partition: {current_partition}") + next_partition = current_partition.get_next_update() + print(f"Next partition: {next_partition}") + label = lv.label(appscreen) + label.set_text("OS Update: 0.00%") + label.align(lv.ALIGN.CENTER, 0, -30) + progress_bar = lv.bar(appscreen) + progress_bar.set_size(200, 20) + progress_bar.align(lv.ALIGN.BOTTOM_MID, 0, -50) + progress_bar.set_range(0, 100) + progress_bar.set_value(0, lv.ANIM.OFF) def progress_callback(percent): print(f"OTA Update: {percent:.1f}%") label.set_text(f"OTA Update: {percent:.2f}%") @@ -36,7 +45,8 @@ def update_with_lvgl(url): chunk_size = 4096 i = 0 print(f"Starting OTA update of size: {total_size}") - while appscreen == lv.screen_active(): + while appscreen == lv.screen_active(): # stop if the user navigates away + time.sleep_ms(100) chunk = response.raw.read(chunk_size) if not chunk: print("No chunk, breaking...") @@ -51,9 +61,68 @@ def update_with_lvgl(url): if total_size: progress_callback(bytes_written / total_size * 100) response.close() - next_partition.set_boot() - import machine - machine.reset() + if bytes_written >= total_size: # if the update was completely installed + next_partition.set_boot() + import machine + machine.reset() -# Start OTA update -update_with_lvgl("http://demo.lnpiggy.com:2121/latest.bin") +def install_button_click(download_url): + print(f"install_button_click for url {download_url}") + try: + _thread.stack_size(16384) + _thread.start_new_thread(update_with_lvgl, (download_url,)) + except Exception as e: + print("Could not start update_with_lvgl thread: ", e) + +def handle_update_info(version, download_url, changelog): + global install_button, status_label + install_button.remove_flag(lv.obj.FLAG.HIDDEN) + install_button.add_event_cb(lambda e, u=download_url: install_button_click(u), lv.EVENT.CLICKED, None) + status_label.set_text(f"Installed version: {CURRENT_OS_VERSION}\nLatest version: {version}\n\nDetails:\n\n{changelog}") + +def show_update_info(): + global status_label + status_label.set_text("Checking for OS updates...") + # URL of the JSON file + url = "http://demo.lnpiggy.com:2121/osupdate.json" # Adjust if the actual JSON URL differs + try: + # Download the JSON + response = urequests.get(url) + # Check if request was successful + if response.status_code == 200: + # Parse JSON + osupdate = ujson.loads(response.text) + # Access attributes + version = osupdate["version"] + download_url = osupdate["download_url"] + changelog = osupdate["changelog"] + # Print the values + print("Version:", version) + print("Download URL:", download_url) + print("Changelog:", changelog) + handle_update_info(version, download_url, changelog) + else: + print("Failed to download JSON. Status code:", response.status_code) + # Close response + response.close() + except Exception as e: + print("Error:", str(e)) + + +install_button = lv.button(appscreen) +install_button.align(lv.ALIGN.TOP_RIGHT, 0, NOTIFICATION_BAR_HEIGHT) +install_button.add_flag(lv.obj.FLAG.HIDDEN) # button will be shown if there is an update available +install_button.set_size(lv.SIZE_CONTENT, lv.pct(20)) +install_label = lv.label(install_button) +install_label.set_text("Update OS") +install_label.center() +status_label = lv.label(appscreen) +status_label.align(lv.ALIGN.TOP_LEFT,0,NOTIFICATION_BAR_HEIGHT) + +if not network.WLAN(network.STA_IF).isconnected(): + status_label.set_text("Error: WiFi is not connected.") + time.sleep(10) +else: + show_update_info() + +print("osupdate.py finished") diff --git a/internal_filesystem/main.py b/internal_filesystem/main.py index d474f31d..f8d6793f 100644 --- a/internal_filesystem/main.py +++ b/internal_filesystem/main.py @@ -3,7 +3,7 @@ import task_handler import machine # Constants -CURRENT_VERSION = "0.0.1" +CURRENT_OS_VERSION = "0.0.1" TFT_HOR_RES=320 TFT_VER_RES=240 NOTIFICATION_BAR_HEIGHT=24 @@ -333,6 +333,7 @@ def execute_script(script_source, is_file, is_launcher, is_graphical): 'parse_manifest': parse_manifest, # for launcher apps 'restart_launcher': restart_launcher, # for appstore apps 'show_launcher': show_launcher, # for apps that want to show the launcher + 'CURRENT_OS_VERSION': CURRENT_OS_VERSION, # for osupdate '__name__': "__main__" } print(f"Thread {thread_id}: starting script") diff --git a/ota_updates/osupdate.json b/ota_updates/osupdate.json new file mode 100644 index 00000000..fd8a2496 --- /dev/null +++ b/ota_updates/osupdate.json @@ -0,0 +1,5 @@ +{ +"version": "0.0.3", +"download_url": "http://demo.lnpiggy.com:2121/osupdate_0.0.3.bin", +"changelog": "- wificonf: scan and connect to wifi in background thread so app stays responsive\n- appstore: add 'update' button if a new version of an app is available\n- appstore: add 'restore' button to restore updated built-in apps to their original built-in version\n- osupdate: show info about update and 'Start OS Update' before updating\n- Introduce MANIFEST.JSON format for apps" +}