diff --git a/CHANGELOG.md b/CHANGELOG.md
index 38e54d0b1..2c7ab8cec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- NeoPool sensor delta trigger (command ``NPTelePeriod``) (#19973)
- NeoPool store settings on unified file system (#19973)
- NeoPool command ``NPBoost`` (#19973)
+- Partition Wizard can be loaded dynamically
### Breaking Changed
diff --git a/lib/libesp32/berry/src/be_bytecode.c b/lib/libesp32/berry/src/be_bytecode.c
index 133a96618..a636567fa 100644
--- a/lib/libesp32/berry/src/be_bytecode.c
+++ b/lib/libesp32/berry/src/be_bytecode.c
@@ -596,6 +596,24 @@ void load_global_info(bvm *vm, void *fp)
be_global_release_space(vm);
}
+bclosure* be_bytecode_load_from_fs(bvm *vm, void *fp)
+{
+ int version = load_head(fp);
+ if (version == BYTECODE_VERSION) {
+ bclosure *cl = be_newclosure(vm, 0);
+ var_setclosure(vm->top, cl);
+ be_stackpush(vm);
+ load_global_info(vm, fp);
+ load_proto(vm, fp, &cl->proto, -1, version);
+ be_stackpop(vm, 2); /* pop the closure and list */
+ be_fclose(fp);
+ return cl;
+ }
+ bytecode_error(vm, be_pushfstring(vm,
+ "invalid bytecode version."));
+ return NULL;
+}
+
bclosure* be_bytecode_load(bvm *vm, const char *filename)
{
void *fp = be_fopen(filename, "rb");
@@ -603,22 +621,9 @@ bclosure* be_bytecode_load(bvm *vm, const char *filename)
bytecode_error(vm, be_pushfstring(vm,
"can not open file '%s'.", filename));
} else {
- int version = load_head(fp);
- if (version == BYTECODE_VERSION) {
- bclosure *cl = be_newclosure(vm, 0);
- var_setclosure(vm->top, cl);
- be_stackpush(vm);
- load_global_info(vm, fp);
- load_proto(vm, fp, &cl->proto, -1, version);
- be_stackpop(vm, 2); /* pop the closure and list */
- be_fclose(fp);
- return cl;
- }
- bytecode_error(vm, be_pushfstring(vm,
- "invalid bytecode version '%s'.", filename));
+ return be_bytecode_load_from_fs(vm, fp);
}
- bytecode_error(vm, be_pushfstring(vm,
- "invalid bytecode file '%s'.", filename));
return NULL;
}
+
#endif /* BE_USE_BYTECODE_LOADER */
diff --git a/lib/libesp32/berry/src/be_bytecode.h b/lib/libesp32/berry/src/be_bytecode.h
index 08affe1de..61223952c 100644
--- a/lib/libesp32/berry/src/be_bytecode.h
+++ b/lib/libesp32/berry/src/be_bytecode.h
@@ -12,6 +12,7 @@
void be_bytecode_save(bvm *vm, const char *filename, bproto *proto);
bclosure* be_bytecode_load(bvm *vm, const char *filename);
+bclosure* be_bytecode_load_from_fs(bvm *vm, void *fp);
bbool be_bytecode_check(const char *path);
#endif
diff --git a/tasmota/berry/modules/Partition_Wizard/partition_wizard.bec b/tasmota/berry/modules/Partition_Wizard/partition_wizard.bec
new file mode 100644
index 000000000..e6e829533
Binary files /dev/null and b/tasmota/berry/modules/Partition_Wizard/partition_wizard.bec differ
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index f8e3fd4a1..7e918a675 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -1141,6 +1141,8 @@
// Note that only one cipher is enabled: ECDHE_RSA_WITH_AES_128_GCM_SHA256 which is very commonly used and highly secure
#define USE_BERRY_WEBCLIENT_USERAGENT "TasmotaClient" // default user-agent used, can be changed with `wc.set_useragent()`
#define USE_BERRY_WEBCLIENT_TIMEOUT 2000 // Default timeout in milliseconds
+ #define USE_BERRY_PARTITION_WIZARD // Add a button to dynamically load the Partion Wizard from a bec file online (+1.3KB Flash)
+ #define USE_BERRY_PARTITION_WIZARD_URL "http://ota.tasmota.com/tapp/partition_wizard.bec"
#define USE_BERRY_TCPSERVER // Enable TCP socket server (+0.6k)
// #define USE_BERRY_ULP // Enable ULP (Ultra Low Power) support (+4.9k)
// Berry crypto extensions below:
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_flash_fs.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_flash_fs.ino
index 0554c8de4..e555f7d5e 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_flash_fs.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_flash_fs.ino
@@ -17,7 +17,7 @@
along with this program. If not, see .
*/
-#ifdef USE_ZIGBEE
+#if defined(USE_ZIGBEE) || defined(USE_BERRY)
#ifdef ESP32
#include
@@ -43,6 +43,12 @@ public:
_seek = 0;
}
+ FlashFileImpl(const void* buf, size_t len) {
+ _buf = (const char*)buf;
+ _len = len;
+ _seek = 0;
+ }
+
virtual ~FlashFileImpl() {}
size_t write(const uint8_t *buf, size_t size) {
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_0_berry_struct.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_0_berry_struct.ino
index ba32bd06f..04dfede34 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_52_0_berry_struct.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_52_0_berry_struct.ino
@@ -93,6 +93,9 @@ public:
int32_t timeout = 0; // Berry heartbeat timeout, preventing code to run for too long. `0` means not enabled
bool rules_busy = false; // are we already processing rules, avoid infinite loop
bool web_add_handler_done = false; // did we already sent `web_add_handler` event
+#ifdef USE_BERRY_PARTITION_WIZARD
+ bool partition_wizard_loaded = false; // did we already load Parition_Wizard
+#endif // USE_BERRY_PARTITION_WIZARD
bool autoexec_done = false; // do we still need to load 'autoexec.be'
bool repl_active = false; // is REPL running (activates log recording)
// output log is stored as a LinkedList of buffers
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_9_berry.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_9_berry.ino
index 0a356e59e..c4505b0af 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_52_9_berry.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_52_9_berry.ino
@@ -23,6 +23,10 @@
#define XDRV_52 52
#include
+extern "C" {
+ #include "be_bytecode.h"
+ #include "be_var.h"
+}
#include "berry_tasmota.h"
#ifdef USE_MATTER_DEVICE
#include "berry_matter.h"
@@ -757,6 +761,85 @@ void HandleBerryConsole(void)
WSContentStop();
}
+// Display a Button to dynamically load the Partition Wizard
+void HandleBerryPartiionWizardLoaderButton(void) {
+ bvm * vm = berry.vm;
+ static const char PARTITION_WIZARD_NAME[] = "partition_wizard";
+ if (!berry.partition_wizard_loaded) {
+ if (be_global_find(vm, be_newstr(vm, PARTITION_WIZARD_NAME)) < 0) { // the global name `partition_wizard` doesn't exist
+ WSContentSend_P("");
+ } else {
+ berry.partition_wizard_loaded = true;
+ }
+ }
+}
+
+void HandleBerryPartitionWizardLoader(void) {
+ if (BerryBECLoader(USE_BERRY_PARTITION_WIZARD_URL)) {
+ // All good, redirect
+ Webserver->sendHeader("Location", "/part_wiz", true);
+ Webserver->send(302, "text/plain", "");
+ berry.partition_wizard_loaded = true;
+ } else {
+ Webserver->sendHeader("Location", "/mn?", true);
+ Webserver->send(302, "text/plain", "");
+ }
+}
+
+// return true if successful
+bool BerryBECLoader(const char * url) {
+ bvm *vm = berry.vm;
+
+ HTTPClientLight cl;
+ cl.setUserAgent(USE_BERRY_WEBCLIENT_USERAGENT);
+ cl.setConnectTimeout(USE_BERRY_WEBCLIENT_TIMEOUT); // set default timeout
+ cl.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
+
+ if (!cl.begin(url)) {
+ AddLog(LOG_LEVEL_INFO, "BRY: unable to load URL '%s'", url);
+ // cl.end();
+ return false;
+ }
+
+ uint32_t http_connect_time = millis();
+ int32_t httpCode = cl.GET();
+ if (httpCode != 200) {
+ AddLog(LOG_LEVEL_INFO, "BRY: unable to load URL '%s' code %i", url, httpCode);
+ // cl.end();
+ return false;
+ }
+
+ int32_t sz = cl.getSize();
+ AddLog(LOG_LEVEL_DEBUG, "BRY: Response http_code %i size %i bytes in %i ms", httpCode, sz, millis() - http_connect_time);
+ // abort if we exceed 32KB size, things will not go well otherwise
+ if (sz >= 32767 || sz <= 0) {
+ AddLog(LOG_LEVEL_DEBUG, "BRY: Response size too big %i bytes", sz);
+ return false;
+ }
+
+ // create a bytes object at top of stack.
+ // the streamwriter knows how to get it.
+ uint8_t * buf = (uint8_t*) be_pushbytes(vm, nullptr, sz);
+ StreamBeBytesWriter memory_writer(vm);
+ int32_t written = cl.writeToStream(&memory_writer);
+ cl.end(); // free allocated memory ~16KB
+
+ size_t loaded_sz = 0;
+ const void * loaded_buf = be_tobytes(vm, -1, &loaded_sz);
+
+ FlashFileImplPtr fp = FlashFileImplPtr(new FlashFileImpl(loaded_buf, loaded_sz));
+ File * f_ptr = new File(fp); // we need to allocate dynamically because be_close calls `delete` on it
+ bclosure* loaded_bec = be_bytecode_load_from_fs(vm, f_ptr);
+ be_pop(vm, 1);
+ if (loaded_bec != NULL) {
+ be_pushclosure(vm, loaded_bec);
+ be_call(vm, 0);
+ be_pop(vm, 1);
+ }
+ be_gc_collect(vm); // force a GC to free the buffer now
+ return true;
+}
+
#endif // USE_WEBSERVER
/*********************************************************************************************\
@@ -839,6 +922,9 @@ bool Xdrv52(uint32_t function)
XdrvMailbox.index++;
} else {
WSContentSend_P(HTTP_BTN_BERRY_CONSOLE);
+#ifdef USE_BERRY_PARTITION_WIZARD
+ HandleBerryPartiionWizardLoaderButton();
+#endif // USE_BERRY_PARTITION_WIZARD
callBerryEventDispatcher(PSTR("web_add_button"), nullptr, 0, nullptr);
callBerryEventDispatcher(PSTR("web_add_console_button"), nullptr, 0, nullptr);
}
@@ -858,6 +944,9 @@ bool Xdrv52(uint32_t function)
berry.web_add_handler_done = true;
}
WebServer_on(PSTR("/bc"), HandleBerryConsole);
+#ifdef USE_BERRY_PARTITION_WIZARD
+ Webserver->on("/tapp", HTTP_GET, HandleBerryPartitionWizardLoader);
+#endif // USE_BERRY_PARTITION_WIZARD
break;
#endif // USE_WEBSERVER
case FUNC_SAVE_BEFORE_RESTART: