From f2006566d7da1972d14f61252c7df2a79244558a Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Sat, 27 Sep 2025 17:20:34 +0200 Subject: [PATCH] Extension Manager, replacing loading of Partition Wizard (#23955) --- CHANGELOG.md | 1 + .../src/embedded/extension_manager.be | 236 +- .../src/embedded/tasmota_class.be | 13 +- .../solidify/solidified_extension_manager.h | 2912 +++++++++-------- .../src/solidify/solidified_tasmota_class.h | 78 +- platformio_override_sample.ini | 2 +- tasmota/my_user_config.h | 6 +- 7 files changed, 1727 insertions(+), 1521 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b694bbf27..05f7c70f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ All notable changes to this project will be documented in this file. - Extend state JSON message with functional hostname and ipaddress which could be WiFi or Ethernet - Berry multiplication between string and int (#23850) - Support for RX8030 RTC (#23855) +- Extension Manager, replacing loading of Partition Wizard ### Breaking Changed - Berry `animate` framework is DEPRECATED, will be replace by `animation` framework (#23854) diff --git a/lib/libesp32/berry_tasmota/src/embedded/extension_manager.be b/lib/libesp32/berry_tasmota/src/embedded/extension_manager.be index caf94053d..607ade267 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/extension_manager.be +++ b/lib/libesp32/berry_tasmota/src/embedded/extension_manager.be @@ -2,7 +2,6 @@ # # - var extension_manager = module("extension_manager") #@ solidify:extension_manager @@ -40,10 +39,10 @@ class Extension_manager # tapp_name(wd) # # Takes a working directory 'wd' and extract the name of the tapp file. - # Ex: '/.extensions/Leds_Panel.tapp#' becomes 'Leds_Panel.tapp' + # Ex: '/.extensions/Leds_Panel.tapp#' becomes 'Leds_Panel' # # @param wv: string - the Working Dir of the tapp file like '/.extensions/Leds_Panel.tapp#' - # @return string - the raw name of the tapp file, like 'Leds_Panel.tapp' + # @return string - the raw name of the tapp file, like 'Leds_Panel' ##################################################################################################### static def tapp_name(wd) import string @@ -113,6 +112,10 @@ class Extension_manager # Return a map of installed tap files, by tapp file name # tapp_name -> path {'Leds_Panel.tapp': '/.extensions/Leds_Panel.tapp'} # + # Example: + # > extension_manager.list_installed_ext() + # {'Leds_Panel': '/.extensions/Leds_Panel.tapp', 'Partition_Wizard': '/.extensions/Partition_Wizard.tapp_'} + # # @return map: tapp_namt -> full path (or wd) static def list_installed_ext() # Read extensions in file system @@ -130,6 +133,10 @@ class Extension_manager # # List all extensions in file-system, whether they are running or not # + # Example: + # > extension_manager.list_extensions_in_fs() + # {'Leds Panel': '/.extensions/Leds_Panel.tapp', 'Partition Wizard': '/.extensions/Partition_Wizard.tapp_'} + # # @return sortedmap: with Name of App as key, and following map: # name, description, version (int), autorun (bool) static def list_extensions_in_fs() @@ -195,19 +202,97 @@ class Extension_manager # all good, created successfully end + ##################################################################################################### + # run_stop_ext(tapp_fname, run_stop) + # + # @param tapp_fname : string - name of tapp file to install from repository (ex: "Leds_Panel") + # @param run_stop : bool - `true` to run , `false` to stop + # @return bool - `true` if success + static def run_stop_ext(tapp_fname, run_stop) + # sanitize + tapp_fname = _class.tapp_name(tapp_fname) + + # get the path for actual file + var tapp_path = _class.list_installed_ext().find(tapp_fname) + if tapp_path != nil + if run_stop + return tasmota.load(tapp_path) + else + return tasmota.unload_extension(tapp_path) + end + else + return false + end + end + + ##################################################################################################### + # enable_disable_ext(tapp_fname, run_stop) + # + # @param tapp_fname : string - name of tapp file to enable or disable (ex: "Leds_Panel") + # @param enable : bool - `true` to enable , `false` to disable + # @return bool - `true` if success + static def enable_disable_ext(tapp_fname, enable) + import string + # sanitize + tapp_fname = _class.tapp_name(tapp_fname) + + # get the path for actual file + var tapp_path = _class.list_installed_ext().find(tapp_fname) + if tapp_path != nil + + var new_name + if enable && string.endswith(tapp_path, ".tapp_") + new_name = tapp_path[0..-2] # remove trailing '_' + elif !enable && string.endswith(tapp_path, ".tapp") + new_name = tapp_path + '_' # add trailing '_' + end + + if new_name + import path + var success = path.rename(tapp_path, new_name) + if (success) # update any running extension with its new name + if (tasmota._ext != nil) && tasmota._ext.contains(tapp_path) + tasmota._ext[new_name] = tasmota._ext[tapp_path] + tasmota._ext.remove(tapp_path) + end + end + return success + end + end + return false + end + + ##################################################################################################### + # delete_ext(tapp_fname) + # + # @param tapp_fname : string - name of tapp file to delete from file system (ex: "Leds_Panel") + # @return bool - `true` if success + static def delete_ext(tapp_fname) + # sanitize + tapp_fname = _class.tapp_name(tapp_fname) + + # get the path for actual file + var tapp_path = _class.list_installed_ext().find(tapp_fname) + if tapp_path != nil + import path + _class.run_stop_ext(tapp_fname, false) # stop the extension if it's running + var success = path.remove(tapp_path) + return success + else + return false + end + end + ##################################################################################################### # install_from_store(tapp_fname) # # @param tapp_fname : string - name of tapp file to install from repository + # @return bool : 'true' if success def install_from_store(tapp_fname) import string import path # sanitize - tapp_fname = self.tapp_name(tapp_fname) - # add '.tapp' extension if it is not present - if !string.endswith(tapp_fname, ".tapp") - tapp_fname += '.tapp' - end + tapp_fname = self.tapp_name(tapp_fname) + ".tapp" # full url var ext_url = f"{self.EXT_REPO}{self.EXT_REPO_FOLDER}{tapp_fname}" log(f"EXT: installing from '{ext_url}'", 3) @@ -222,19 +307,20 @@ class Extension_manager var r = cl.GET() if r != 200 log(f"EXT: return_code={r}", 2) - return + return false end var ret = cl.write_file(local_file) cl.close() # test if file exists and tell its size if ret > 0 && path.exists(local_file) log(f"EXT: successfully installed '{local_file}' {ret} bytes", 3) + return true else raise "io_error", f"could not download into '{local_file}' ret={ret}" end except .. as e, m - tasmota.log(format("EXT: exception '%s' - '%s'", e, m), 2) - return nil + log(format("EXT: exception '%s' - '%s'", e, m), 2) + return false end end @@ -278,7 +364,6 @@ class Extension_manager webserver.content_send("" ) - webserver.content_send("
" - "