From 677c37a50f1caeeee58ceebab2595542492ec12c Mon Sep 17 00:00:00 2001 From: Alex R Date: Mon, 12 Apr 2021 20:04:18 -0500 Subject: [PATCH] Quickdeploy (#18) * improve configuration handling * change config variable names to snake case * populate tag ui with github api * tag box and asset box update each other * only call api once * fix some combo box formatting * clean up combobox callbacks * working cancel button with quick mode * get download url by tag and name * working somewhat * all modes working --- src/main.py | 32 ++++++--- src/meson.build | 1 + src/quick_deploy.py | 56 ++++++++++++++++ src/window.py | 148 ++++++++++++++++++++++++++++++++++-------- src/window.ui | 155 ++++++++++++++++++++++++++++++++++++-------- 5 files changed, 331 insertions(+), 61 deletions(-) create mode 100644 src/quick_deploy.py diff --git a/src/main.py b/src/main.py index 66ff70e..0a30bcf 100644 --- a/src/main.py +++ b/src/main.py @@ -16,6 +16,7 @@ def __init__(self): self.manager = None config = self.configuration_setup() self.mode = config["settings"]["mode"] + self.deploy_type = config["settings"]["deploy_type"] super().__init__( application_id="org.gnome.siglo", flags=Gio.ApplicationFlags.FLAGS_NONE ) @@ -23,21 +24,34 @@ def __init__(self): def configuration_setup(self): config = configparser.ConfigParser() home = str(Path.home()) - configDir = home + "/.config/siglo" - if not Path(configDir).is_dir(): - Path.mkdir(Path(configDir)) - configFile = configDir + "/siglo.ini" - if not Path(configFile).is_file(): - config["settings"] = {"mode": "singleton"} - with open(configFile, "w") as f: + config_dir = home + "/.config/siglo" + if not Path(config_dir).is_dir(): + Path.mkdir(Path(config_dir)) + config_file = config_dir + "/siglo.ini" + if not self.config_file_is_valid(config, config_file): + config["settings"] = {"mode": "singleton", "deploy_type": "quick"} + with open(config_file, "w") as f: config.write(f) - config.read(configFile) + config.read(config_file) return config + def config_file_is_valid(self, config, config_file): + keys = ("mode", "deploy_type") + if not Path(config_file).is_file(): + return False + else: + config.read(config_file) + for key in keys: + if not key in config["settings"]: + return False + return True + def do_activate(self): win = self.props.active_window if not win: - win = SigloWindow(application=self, mode=self.mode) + win = SigloWindow( + application=self, mode=self.mode, deploy_type=self.deploy_type + ) win.present() self.manager = InfiniTimeManager(self.mode) info_prefix = "[INFO ] Done Scanning" diff --git a/src/meson.build b/src/meson.build index e6c4d02..2fdab6f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -27,6 +27,7 @@ configure_file( siglo_sources = [ '__init__.py', + 'quick_deploy.py', 'main.py', 'window.py', 'bluetooth.py', diff --git a/src/quick_deploy.py b/src/quick_deploy.py new file mode 100644 index 0000000..a74acab --- /dev/null +++ b/src/quick_deploy.py @@ -0,0 +1,56 @@ +import requests +import json + +url = "https://api.github.com/repos/JF002/InfiniTime/releases" +version_blacklist = ( + "0.6.0", + "0.6.1", + "0.6.2", + "0.7.0", + "0.7.1", + "0.8.0-develop", + "0.8.1-develop", + "0.8.2-develop", + "0.9.0-develop", + "0.9.0", + "0.8.3", + "0.8.2" +) + + +def get_quick_deploy_list(): + r = requests.get(url) + d = json.loads(r.content) + quick_deploy_list = [] + for item in d: + for asset in item["assets"]: + if ( + asset["content_type"] == "application/zip" + and item["tag_name"] not in version_blacklist + ): + helper_dict = { + "tag_name": item["tag_name"], + "name": asset["name"], + "browser_download_url": asset["browser_download_url"], + } + quick_deploy_list.append(helper_dict) + return quick_deploy_list + + +def get_tags(full_list): + tags = set() + for element in full_list: + tags.add(element["tag_name"]) + return sorted(tags, reverse=True) + +def get_assets_by_tag(tag, full_list): + asset_list = [] + for element in full_list: + if tag == element["tag_name"]: + asset_list.append(element["name"]) + return asset_list + +def get_download_url(name, tag, full_list): + for element in full_list: + if tag == element["tag_name"] and name == element["name"]: + return element["browser_download_url"] \ No newline at end of file diff --git a/src/window.py b/src/window.py index f34d9e8..fa992c0 100644 --- a/src/window.py +++ b/src/window.py @@ -1,11 +1,12 @@ -import threading import gatt import configparser +import urllib.request from pathlib import Path from gi.repository import Gtk, GObject from .bluetooth import InfiniTimeDevice from .ble_dfu import InfiniTimeDFU from .unpacker import Unpacker +from .quick_deploy import * @Gtk.Template(resource_path="/org/gnome/siglo/window.ui") @@ -25,17 +26,32 @@ class SigloWindow(Gtk.ApplicationWindow): multi_device_listbox = Gtk.Template.Child() rescan_button = Gtk.Template.Child() multi_device_switch = Gtk.Template.Child() + auto_bbox_scan_pass = Gtk.Template.Child() + bbox_scan_pass = Gtk.Template.Child() + ota_pick_tag_combobox = Gtk.Template.Child() + ota_pick_asset_combobox = Gtk.Template.Child() + ota_pick_asset_combobox = Gtk.Template.Child() + deploy_type_switch = Gtk.Template.Child() - def __init__(self, mode, **kwargs): + def __init__(self, mode, deploy_type, **kwargs): self.ble_dfu = None self.ota_file = None self.manager = None + self.asset = None + self.asset_download_url = None + self.tag = None self.mode = mode + self.deploy_type = deploy_type super().__init__(**kwargs) GObject.threads_init() if mode == "multi": - self.auto_switch = True + self.auto_switch_mode = True self.multi_device_switch.set_active(True) + if deploy_type == "quick": + self.full_list = get_quick_deploy_list() + if deploy_type == "manual": + self.auto_switch_deploy_type = True + self.deploy_type_switch.set_active(True) def depopulate_listbox(self): children = self.multi_device_listbox.get_children() @@ -59,6 +75,15 @@ def populate_listbox(self): self.multi_device_listbox.set_visible(True) self.multi_device_listbox.show_all() + def populate_tagbox(self): + for tag in get_tags(self.full_list): + self.ota_pick_tag_combobox.append_text(tag) + + def populate_assetbox(self): + self.ota_pick_asset_combobox.remove_all() + for asset in get_assets_by_tag(self.tag, self.full_list): + self.ota_pick_asset_combobox.append_text(asset) + def done_scanning_multi(self, manager, info_prefix): self.manager = manager scan_result = manager.get_scan_result() @@ -88,12 +113,57 @@ def done_scanning_singleton(self, manager, info_prefix): + manager.get_mac_address() ) self.scan_pass_box.set_visible(True) + self.ota_picked_box.set_visible(True) + if self.deploy_type == "quick": + self.auto_bbox_scan_pass.set_visible(True) + self.populate_tagbox() + if self.deploy_type == "manual": + self.bbox_scan_pass.set_visible(True) else: info_suffix += "\n[INFO ] Scan Failed" self.rescan_button.set_visible(True) self.scan_fail_box.set_visible(True) self.main_info.set_text(info_prefix + info_suffix) + @Gtk.Template.Callback() + def multi_listbox_row_selected(self, list_box, row): + if row is not None: + mac_add = row.get_child().get_label() + self.manager.set_mac_address(mac_add) + self.info_scan_pass.set_text( + self.manager.alias + + " Found!\n\nAdapter Name: " + + self.manager.adapter_name + + "\nMac Address: " + + self.manager.get_mac_address() + ) + print("deploy type!", self.deploy_type) + self.scan_pass_box.set_visible(True) + self.ota_picked_box.set_visible(True) + if self.deploy_type == "manual": + self.bbox_scan_pass.set_visible(True) + if self.deploy_type == "quick": + self.auto_bbox_scan_pass.set_visible(True) + self.populate_tagbox() + self.multi_device_listbox.set_visible(False) + + @Gtk.Template.Callback() + def ota_pick_tag_combobox_changed_cb(self, widget): + self.tag = self.ota_pick_tag_combobox.get_active_text() + self.populate_assetbox() + + @Gtk.Template.Callback() + def ota_pick_asset_combobox_changed_cb(self, widget): + self.asset = self.ota_pick_asset_combobox.get_active_text() + if self.asset is not None: + self.ota_picked_box.set_sensitive(True) + self.asset_download_url = get_download_url( + self.asset, self.tag, self.full_list + ) + else: + self.ota_picked_box.set_sensitive(False) + self.asset_download_url = None + @Gtk.Template.Callback() def rescan_button_clicked(self, widget): if self.manager is not None: @@ -133,19 +203,34 @@ def ota_file_selected(self, widget): self.main_info.set_text("File: " + filename.split("/")[-1]) self.ota_picked_box.set_visible(True) self.ota_selection_box.set_visible(False) + self.ota_picked_box.set_sensitive(True) @Gtk.Template.Callback() def ota_cancel_button_clicked(self, widget): - self.main_info.set_text("Choose another OTA File") - self.ota_picked_box.set_visible(False) - self.ota_selection_box.set_visible(True) + if self.deploy_type == "quick": + self.ota_pick_asset_combobox.remove_all() + self.ota_pick_tag_combobox.remove_all() + self.populate_tagbox() + self.ota_picked_box.set_sensitive(False) + if self.deploy_type == "manual": + self.main_info.set_text("Choose another OTA File") + self.ota_picked_box.set_visible(False) + self.ota_selection_box.set_visible(True) @Gtk.Template.Callback() def flash_it_button_clicked(self, widget): + if self.deploy_type == "quick": + file_name = "/tmp/" + self.asset + local_filename, headers = urllib.request.urlretrieve( + self.asset_download_url, file_name + ) + self.ota_file = local_filename + self.main_info.set_text("Updating Firmware...") self.ota_picked_box.set_visible(False) self.dfu_progress_box.set_visible(True) self.sync_time_button.set_visible(False) + self.auto_bbox_scan_pass.set_visible(False) unpacker = Unpacker() try: binfile, datfile = unpacker.unpack_zipfile(self.ota_file) @@ -165,10 +250,35 @@ def flash_it_button_clicked(self, widget): self.dfu_progress_text.set_text(self.get_prog_text()) self.ble_dfu.connect() + @Gtk.Template.Callback() + def deploy_type_toggled(self, widget): + if self.deploy_type == "manual" and self.auto_switch_deploy_type: + self.auto_switch_deploy_type = False + else: + current_deploy_type = self.deploy_type + config = configparser.ConfigParser() + home = str(Path.home()) + configDir = home + "/.config/siglo" + configFile = configDir + "/siglo.ini" + if current_deploy_type == "quick": + config["settings"] = {"mode": self.mode, "deploy_type": "manual"} + self.deploy_type = "manual" + if current_deploy_type == "manual": + config["settings"] = {"mode": self.mode, "deploy_type": "quick"} + self.deploy_type = "quick" + with open(configFile, "w") as f: + config.write(f) + self.main_info.set_text("[WARN ] Settings changed, please restart Siglo") + self.rescan_button.set_visible(False) + self.scan_pass_box.set_visible(False) + self.depopulate_listbox() + self.scan_fail_box.set_visible(False) + self.auto_bbox_scan_pass.set_visible(False) + @Gtk.Template.Callback() def mode_toggled(self, widget): - if self.mode == "multi" and self.auto_switch == True: - self.auto_switch = False + if self.mode == "multi" and self.auto_switch_mode == True: + self.auto_switch_mode = False else: current_mode = self.mode config = configparser.ConfigParser() @@ -176,10 +286,10 @@ def mode_toggled(self, widget): configDir = home + "/.config/siglo" configFile = configDir + "/siglo.ini" if current_mode == "singleton": - config["settings"] = {"mode": "multi"} + config["settings"] = {"mode": "multi", "deploy_type": self.deploy_type} self.mode = "multi" if current_mode == "multi": - config["settings"] = {"mode": "singleton"} + config["settings"] = {"mode": "singleton", "deploy_type": self.deploy_type} self.mode = "singleton" with open(configFile, "w") as f: config.write(f) @@ -189,22 +299,6 @@ def mode_toggled(self, widget): self.depopulate_listbox() self.scan_fail_box.set_visible(False) - @Gtk.Template.Callback() - def multi_listbox_row_selected(self, list_box, row): - if row is not None: - mac_add = row.get_child().get_label() - self.manager.set_mac_address(mac_add) - self.info_scan_pass.set_text( - self.manager.alias - + " Found!\n\nAdapter Name: " - + self.manager.adapter_name - + "\nMac Address: " - + self.manager.get_mac_address() - ) - self.scan_pass_box.set_visible(True) - self.multi_device_listbox.set_visible(False) - - def update_progress_bar(self): self.dfu_progress_bar.set_fraction( self.ble_dfu.total_receipt_size / self.ble_dfu.image_size @@ -224,3 +318,5 @@ def show_complete(self): self.bt_spinner.set_visible(False) self.sync_time_button.set_visible(True) self.dfu_progress_box.set_visible(False) + if (self.deploy_type == "quick"): + self.auto_bbox_scan_pass.set_visible(True) diff --git a/src/window.ui b/src/window.ui index 7774ca1..87f4f97 100644 --- a/src/window.ui +++ b/src/window.ui @@ -14,6 +14,15 @@ + + + True + False + Manual OTA File + True + + +