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: