mirror of https://github.com/arendst/Tasmota.git
Add Esp32 Partition Manager as a Berry component
This commit is contained in:
parent
e102d4e66c
commit
ee2b6d7f74
|
@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Force ESP32 defines USE_UFILESYS, GUI_TRASH_FILE and #define GUI_EDIT_FILE
|
- Force ESP32 defines USE_UFILESYS, GUI_TRASH_FILE and #define GUI_EDIT_FILE
|
||||||
- Speed up initial GUI console refresh
|
- Speed up initial GUI console refresh
|
||||||
- Simplified configuration for ir-full and removal of tasmota-ircustom
|
- Simplified configuration for ir-full and removal of tasmota-ircustom
|
||||||
|
- Add Esp32 Partition Manager as a Berry component
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- ESP32 Webcam add boundary marker before sending mjpeg image (#12376)
|
- ESP32 Webcam add boundary marker before sending mjpeg image (#12376)
|
||||||
|
|
|
@ -14,6 +14,7 @@ extern int w_webserver_on(bvm *vm);
|
||||||
extern int w_webserver_state(bvm *vm);
|
extern int w_webserver_state(bvm *vm);
|
||||||
|
|
||||||
extern int w_webserver_check_privileged_access(bvm *vm);
|
extern int w_webserver_check_privileged_access(bvm *vm);
|
||||||
|
extern int w_webserver_redirect(bvm *vm);
|
||||||
extern int w_webserver_content_start(bvm *vm);
|
extern int w_webserver_content_start(bvm *vm);
|
||||||
extern int w_webserver_content_send(bvm *vm);
|
extern int w_webserver_content_send(bvm *vm);
|
||||||
extern int w_webserver_content_send_style(bvm *vm);
|
extern int w_webserver_content_send_style(bvm *vm);
|
||||||
|
@ -23,6 +24,7 @@ extern int w_webserver_content_button(bvm *vm);
|
||||||
|
|
||||||
extern int w_webserver_argsize(bvm *vm);
|
extern int w_webserver_argsize(bvm *vm);
|
||||||
extern int w_webserver_arg(bvm *vm);
|
extern int w_webserver_arg(bvm *vm);
|
||||||
|
extern int w_webserver_arg_name(bvm *vm);
|
||||||
extern int w_webserver_has_arg(bvm *vm);
|
extern int w_webserver_has_arg(bvm *vm);
|
||||||
|
|
||||||
#if !BE_USE_PRECOMPILED_OBJECT
|
#if !BE_USE_PRECOMPILED_OBJECT
|
||||||
|
@ -33,6 +35,7 @@ be_native_module_attr_table(webserver) {
|
||||||
be_native_module_function("state", w_webserver_state),
|
be_native_module_function("state", w_webserver_state),
|
||||||
|
|
||||||
be_native_module_function("check_privileged_access", w_webserver_check_privileged_access),
|
be_native_module_function("check_privileged_access", w_webserver_check_privileged_access),
|
||||||
|
be_native_module_function("redirect", w_webserver_redirect),
|
||||||
be_native_module_function("content_start", w_webserver_content_start),
|
be_native_module_function("content_start", w_webserver_content_start),
|
||||||
be_native_module_function("content_send", w_webserver_content_send),
|
be_native_module_function("content_send", w_webserver_content_send),
|
||||||
be_native_module_function("content_send_style", w_webserver_content_send_style),
|
be_native_module_function("content_send_style", w_webserver_content_send_style),
|
||||||
|
@ -43,6 +46,7 @@ be_native_module_attr_table(webserver) {
|
||||||
|
|
||||||
be_native_module_function("arg_size", w_webserver_argsize),
|
be_native_module_function("arg_size", w_webserver_argsize),
|
||||||
be_native_module_function("arg", w_webserver_arg),
|
be_native_module_function("arg", w_webserver_arg),
|
||||||
|
be_native_module_function("arg_name", w_webserver_arg_name),
|
||||||
be_native_module_function("has_arg", w_webserver_has_arg),
|
be_native_module_function("has_arg", w_webserver_has_arg),
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -57,6 +61,7 @@ module webserver (scope: global) {
|
||||||
state, func(w_webserver_state)
|
state, func(w_webserver_state)
|
||||||
|
|
||||||
check_privileged_access, func(w_webserver_check_privileged_access)
|
check_privileged_access, func(w_webserver_check_privileged_access)
|
||||||
|
redirect, func(w_webserver_redirect)
|
||||||
content_start, func(w_webserver_content_start)
|
content_start, func(w_webserver_content_start)
|
||||||
content_send, func(w_webserver_content_send)
|
content_send, func(w_webserver_content_send)
|
||||||
content_send_style, func(w_webserver_content_send_style)
|
content_send_style, func(w_webserver_content_send_style)
|
||||||
|
@ -67,6 +72,7 @@ module webserver (scope: global) {
|
||||||
|
|
||||||
arg_size, func(w_webserver_argsize)
|
arg_size, func(w_webserver_argsize)
|
||||||
arg, func(w_webserver_arg)
|
arg, func(w_webserver_arg)
|
||||||
|
arg_name, func(w_webserver_arg_name)
|
||||||
has_arg, func(w_webserver_has_arg)
|
has_arg, func(w_webserver_has_arg)
|
||||||
}
|
}
|
||||||
@const_object_info_end */
|
@const_object_info_end */
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,24 +1,26 @@
|
||||||
#include "be_constobj.h"
|
#include "be_constobj.h"
|
||||||
|
|
||||||
static be_define_const_map_slots(m_libwebserver_map) {
|
static be_define_const_map_slots(m_libwebserver_map) {
|
||||||
{ be_const_key(on, 5), be_const_func(w_webserver_on) },
|
{ be_const_key(state, 5), be_const_func(w_webserver_state) },
|
||||||
{ be_const_key(state, 6), be_const_func(w_webserver_state) },
|
{ be_const_key(member, 9), be_const_func(w_webserver_member) },
|
||||||
{ be_const_key(content_flush, -1), be_const_func(w_webserver_content_flush) },
|
|
||||||
{ be_const_key(content_send, -1), be_const_func(w_webserver_content_send) },
|
|
||||||
{ be_const_key(arg, -1), be_const_func(w_webserver_arg) },
|
|
||||||
{ be_const_key(has_arg, -1), be_const_func(w_webserver_has_arg) },
|
|
||||||
{ be_const_key(content_start, 2), be_const_func(w_webserver_content_start) },
|
|
||||||
{ be_const_key(content_button, -1), be_const_func(w_webserver_content_button) },
|
{ be_const_key(content_button, -1), be_const_func(w_webserver_content_button) },
|
||||||
|
{ be_const_key(content_start, -1), be_const_func(w_webserver_content_start) },
|
||||||
|
{ be_const_key(content_send, -1), be_const_func(w_webserver_content_send) },
|
||||||
|
{ be_const_key(content_flush, -1), be_const_func(w_webserver_content_flush) },
|
||||||
|
{ be_const_key(redirect, 8), be_const_func(w_webserver_redirect) },
|
||||||
{ be_const_key(content_send_style, -1), be_const_func(w_webserver_content_send_style) },
|
{ be_const_key(content_send_style, -1), be_const_func(w_webserver_content_send_style) },
|
||||||
{ be_const_key(check_privileged_access, 12), be_const_func(w_webserver_check_privileged_access) },
|
{ be_const_key(arg, -1), be_const_func(w_webserver_arg) },
|
||||||
{ be_const_key(content_stop, -1), be_const_func(w_webserver_content_stop) },
|
{ be_const_key(content_stop, -1), be_const_func(w_webserver_content_stop) },
|
||||||
{ be_const_key(member, -1), be_const_func(w_webserver_member) },
|
{ be_const_key(arg_name, -1), be_const_func(w_webserver_arg_name) },
|
||||||
|
{ be_const_key(has_arg, -1), be_const_func(w_webserver_has_arg) },
|
||||||
{ be_const_key(arg_size, -1), be_const_func(w_webserver_argsize) },
|
{ be_const_key(arg_size, -1), be_const_func(w_webserver_argsize) },
|
||||||
|
{ be_const_key(check_privileged_access, 11), be_const_func(w_webserver_check_privileged_access) },
|
||||||
|
{ be_const_key(on, 3), be_const_func(w_webserver_on) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static be_define_const_map(
|
static be_define_const_map(
|
||||||
m_libwebserver_map,
|
m_libwebserver_map,
|
||||||
13
|
15
|
||||||
);
|
);
|
||||||
|
|
||||||
static be_define_const_module(
|
static be_define_const_module(
|
||||||
|
|
|
@ -6,6 +6,7 @@ partition = module('partition')
|
||||||
|
|
||||||
import flash
|
import flash
|
||||||
import string
|
import string
|
||||||
|
import webserver
|
||||||
|
|
||||||
#- remove trailing NULL chars from a buffer before converting to string -#
|
#- remove trailing NULL chars from a buffer before converting to string -#
|
||||||
#- Berry strings can contain NULL, but this messes up C-Berry interface -#
|
#- Berry strings can contain NULL, but this messes up C-Berry interface -#
|
||||||
|
@ -80,8 +81,6 @@ class Partition_info
|
||||||
var flags
|
var flags
|
||||||
|
|
||||||
def init(raw)
|
def init(raw)
|
||||||
import string
|
|
||||||
|
|
||||||
if raw == nil || !issubclass(bytes, raw)
|
if raw == nil || !issubclass(bytes, raw)
|
||||||
self.type = 0
|
self.type = 0
|
||||||
self.subtype = 0
|
self.subtype = 0
|
||||||
|
@ -132,7 +131,7 @@ class Partition_info
|
||||||
try
|
try
|
||||||
var addr = self.start
|
var addr = self.start
|
||||||
var magic_byte = flash.read(addr, 1).get(0, 1)
|
var magic_byte = flash.read(addr, 1).get(0, 1)
|
||||||
if magic_byte != 0xE9 raise "internal_error", string.format("Invalid magic_byte 0x%02X (should be 0xE9)", magic_byte) end
|
if magic_byte != 0xE9 return -1 end
|
||||||
|
|
||||||
var seg_count = flash.read(addr+1, 1).get(0, 1)
|
var seg_count = flash.read(addr+1, 1).get(0, 1)
|
||||||
# print("Segment count", seg_count)
|
# print("Segment count", seg_count)
|
||||||
|
@ -161,7 +160,6 @@ class Partition_info
|
||||||
end
|
end
|
||||||
|
|
||||||
def tostring()
|
def tostring()
|
||||||
import string
|
|
||||||
var type_s = ""
|
var type_s = ""
|
||||||
var subtype_s = ""
|
var subtype_s = ""
|
||||||
if self.type == 0 type_s = "app"
|
if self.type == 0 type_s = "app"
|
||||||
|
@ -392,6 +390,13 @@ class Partition
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_ota_slot(n)
|
||||||
|
for slot: self.slots
|
||||||
|
if slot.is_ota() == n return slot end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
#- compute the highest ota<x> partition -#
|
#- compute the highest ota<x> partition -#
|
||||||
def ota_max()
|
def ota_max()
|
||||||
var ota_max = 0
|
var ota_max = 0
|
||||||
|
@ -417,6 +422,11 @@ class Partition
|
||||||
self.otadata = Partition_otadata(ota_max, otadata_offset)
|
self.otadata = Partition_otadata(ota_max, otadata_offset)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# get the active OTA app partition number
|
||||||
|
def get_active()
|
||||||
|
return self.otadata.active_otadata
|
||||||
|
end
|
||||||
|
|
||||||
#- change the active partition -#
|
#- change the active partition -#
|
||||||
def set_active(n)
|
def set_active(n)
|
||||||
if n < 0 || n > self.ota_max() raise "value_error", "Invalid ota partition number" end
|
if n < 0 || n > self.ota_max() raise "value_error", "Invalid ota partition number" end
|
||||||
|
@ -477,6 +487,277 @@ class Partition
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------
|
||||||
|
- Parser manager for ESP32
|
||||||
|
-
|
||||||
|
-------------------------------------------------------------#
|
||||||
|
|
||||||
|
class Partition_manager : Driver
|
||||||
|
|
||||||
|
def init()
|
||||||
|
end
|
||||||
|
|
||||||
|
# create a method for adding a button to the main menu
|
||||||
|
# the button 'Partition Manager' redirects to '/part_mgr?'
|
||||||
|
def web_add_button()
|
||||||
|
webserver.content_send(
|
||||||
|
"<form id=but_part_mgr style='display: block;' action='part_mgr' method='get'><button>Partition Manager</button></form>")
|
||||||
|
end
|
||||||
|
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
# Show a single OTA Partition
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
def page_show_partition(slot, active, ota_num)
|
||||||
|
#- define `bdis` style for gray disabled buttons -#
|
||||||
|
webserver.content_send("<fieldset><style>.bdis{background:#888;}.bdis:hover{background:#888;}</style>")
|
||||||
|
webserver.content_send(string.format("<legend><b title='Start: 0x%03X 000'> %s%s</b></legend>",
|
||||||
|
slot.start / 0x1000, slot.label, active ? " (active)" : ""))
|
||||||
|
|
||||||
|
webserver.content_send(string.format("<p><b>Partition size: </b>%i KB</p>", slot.size / 1024))
|
||||||
|
var used = slot.get_image_size()
|
||||||
|
if used >= 0
|
||||||
|
webserver.content_send(string.format("<p><b>Used: </b>%i KB</p>", used / 1024))
|
||||||
|
webserver.content_send(string.format("<p><b>Free: </b>%i KB</p>", (slot.size - used) / 1024))
|
||||||
|
else
|
||||||
|
webserver.content_send("<p><b>Used</b>: unknwon</p>")
|
||||||
|
webserver.content_send("<p><b>Free</b>: unknwon</p>")
|
||||||
|
end
|
||||||
|
if !active && used >= 0
|
||||||
|
webserver.content_send("<p><form id=setactive style='display: block;' action='/part_mgr' method='post' ")
|
||||||
|
webserver.content_send("onsubmit='return confirm(\"This will change the active partition and cause a restart.\");'>")
|
||||||
|
webserver.content_send("<button name='setactive' class='button bgrn'>Switch To This Partition</button>")
|
||||||
|
webserver.content_send(string.format("<input name='ota' type='hidden' value='%d'>", ota_num))
|
||||||
|
webserver.content_send("</form></p>")
|
||||||
|
else
|
||||||
|
# put a fake disabled button
|
||||||
|
webserver.content_send("<p><form style='display: block;'>")
|
||||||
|
if used >= 0
|
||||||
|
webserver.content_send("<button name='setactive' class='button bdis' disabled title=\"No need to click, it's already the active partition\">Current Active Partition</button>")
|
||||||
|
else
|
||||||
|
webserver.content_send("<button name='setactive' class='button bdis' disabled>Empty Partition</button>")
|
||||||
|
end
|
||||||
|
webserver.content_send("</form></p>")
|
||||||
|
end
|
||||||
|
|
||||||
|
webserver.content_send("<p></p></fieldset><p></p>")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
# Show a single OTA Partition
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
def page_show_spiffs(slot, free_mem)
|
||||||
|
webserver.content_send(string.format("<fieldset><legend><b title='Start: 0x%03X 000'> %s</b></legend>",
|
||||||
|
slot.start / 0x1000, slot.label))
|
||||||
|
|
||||||
|
webserver.content_send(string.format("<p><b>Partition size:</b> %i KB</p>", slot.size / 1024))
|
||||||
|
|
||||||
|
if free_mem != nil
|
||||||
|
webserver.content_send(string.format("<p><b>Max size: </b>%i KB</p>", (slot.size + free_mem) / 1024))
|
||||||
|
webserver.content_send(string.format("<p><b>Unallocated: </b>%i KB</p>", free_mem / 1024))
|
||||||
|
end
|
||||||
|
|
||||||
|
#- display Resize button -#
|
||||||
|
webserver.content_send("<hr><p><b>New size:</b> (multiple of 16 KB)</p>")
|
||||||
|
webserver.content_send("<form action='/part_mgr' method='post' ")
|
||||||
|
webserver.content_send("onsubmit='return confirm(\"This will DELETE the content of the file system and cause a restart.\");'>")
|
||||||
|
webserver.content_send(string.format("<input type='number' min='0' max='%d' step='16' name='spiffs_size' value='%i'>", (slot.size + free_mem) / 1024, ((slot.size + free_mem) / 1024 / 16)*16))
|
||||||
|
webserver.content_send("<p></p><button name='resize' class='button bred'>Resize SPIFFS</button></form></p>")
|
||||||
|
webserver.content_send("<p></p></fieldset><p></p>")
|
||||||
|
end
|
||||||
|
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
#- Show each partition one after the other - only OTA and SPIFFS
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
def page_show_partitions(p)
|
||||||
|
# display ota partitions
|
||||||
|
for slot: p.slots
|
||||||
|
# is the slot app?
|
||||||
|
var ota_num = slot.is_ota()
|
||||||
|
if ota_num != nil
|
||||||
|
# we have an OTA partition
|
||||||
|
self.page_show_partition(slot, ota_num == p.otadata.active_otadata, ota_num)
|
||||||
|
elif slot.is_spiffs()
|
||||||
|
var flash_size = tasmota.memory()['flash'] * 1024
|
||||||
|
var used_size = (slot.start + slot.size)
|
||||||
|
self.page_show_spiffs(slot, slot == p.slots[-1] ? flash_size - used_size : nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
#- Display the Re-partition section
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
def page_show_repartition(p)
|
||||||
|
if p.get_active() != 0
|
||||||
|
webserver.content_send("<p style='width:320px;'>Re-partition can be done only if 'app0' is active.</p>")
|
||||||
|
else
|
||||||
|
# we can proceed
|
||||||
|
var app0 = p.get_ota_slot(0)
|
||||||
|
var app0_size_kb = ((app0.size / 1024 + 63) / 64) * 64 # rounded to upper 64kb
|
||||||
|
var app0_used_kb = (((app0.get_image_size()) / 1024 / 64) + 1) * 64
|
||||||
|
var flash_size_kb = tasmota.memory()['flash']
|
||||||
|
var app_size_max = 1984 # Max OTA size (4096 - 64) / 2 rounded to lowest 64KB
|
||||||
|
|
||||||
|
webserver.content_send("<p><b>Resize app Partitions</b></p>")
|
||||||
|
webserver.content_send(string.format("<p><b>Min:</b> %i KB</p>", app0_used_kb))
|
||||||
|
webserver.content_send(string.format("<p><b>Max:</b> %i KB</p>", app_size_max))
|
||||||
|
webserver.content_send("<p><b>New:</b> (multiple of 64 KB)</p>")
|
||||||
|
webserver.content_send("<form action='/part_mgr' method='post' ")
|
||||||
|
webserver.content_send("onsubmit='return confirm(\"This will DELETE the content of the file system and cause a restart.\");'>")
|
||||||
|
webserver.content_send(string.format("<input type='number' min='%d' max='%d' step='64' name='repartition' value='%i'>", app0_used_kb, app_size_max, app0_size_kb))
|
||||||
|
webserver.content_send("<p></p><button name='resize' class='button bred'>Resize Partitions</button></form></p>")
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#- this method displays the web page -#
|
||||||
|
def page_part_mgr()
|
||||||
|
if !webserver.check_privileged_access() return nil end
|
||||||
|
var p = partition.Partition()
|
||||||
|
|
||||||
|
webserver.content_start("Partition Manager") #- title of the web page -#
|
||||||
|
webserver.content_send_style() #- send standard Tasmota styles -#
|
||||||
|
|
||||||
|
# webserver.content_send("<p style='width:340px;'><b style='color:#f56'>Warning:</b> This can brick your device. Don't use unless you know what you are doing.</p>")
|
||||||
|
webserver.content_send("<fieldset><legend><b> Partition Manager</b></legend><p></p>")
|
||||||
|
webserver.content_send("<p style='width:320px;'><b style='color:#f56'>Warning:</b> This can brick your device.</p>")
|
||||||
|
self.page_show_partitions(p)
|
||||||
|
webserver.content_send("<p></p></fieldset><p></p>")
|
||||||
|
|
||||||
|
webserver.content_send("<fieldset><legend><b> Re-partition</b></legend><p></p>")
|
||||||
|
self.page_show_repartition(p)
|
||||||
|
webserver.content_send("<p></p></fieldset><p></p>")
|
||||||
|
|
||||||
|
|
||||||
|
webserver.content_button(webserver.BUTTON_MANAGEMENT) #- button back to management page -#
|
||||||
|
webserver.content_stop() #- end of web page -#
|
||||||
|
end
|
||||||
|
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
#- this is the controller, called using POST and changing parameters
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
def page_part_ctl()
|
||||||
|
if !webserver.check_privileged_access() return nil end
|
||||||
|
|
||||||
|
#- check that the partition is valid -#
|
||||||
|
var p = partition.Partition()
|
||||||
|
|
||||||
|
try
|
||||||
|
if webserver.has_arg("ota")
|
||||||
|
#- OTA switch partition -#
|
||||||
|
var ota_target = int(webserver.arg("ota"))
|
||||||
|
if ota_target < 0 || ota_target > p.ota_max() raise "value_error", string.format("Invalid partition #%d", ota_target) end
|
||||||
|
var ota_slot = p.get_ota_slot(ota_target)
|
||||||
|
|
||||||
|
if ota_slot == nil || ota_slot.get_image_size() < 0
|
||||||
|
raise "value_error", string.format("Invalid OTA slot #%d", ota_target)
|
||||||
|
end
|
||||||
|
|
||||||
|
print(string.format("Trying to change active partition to %d", ota_target))
|
||||||
|
#- do the change -#
|
||||||
|
p.set_active(ota_target)
|
||||||
|
p.otadata.save() #- write to disk -#
|
||||||
|
|
||||||
|
#- and force restart -#
|
||||||
|
webserver.redirect("/?rst=")
|
||||||
|
elif webserver.has_arg("spiffs_size")
|
||||||
|
#- SPIFFS size change -#
|
||||||
|
var spiffs_size_kb = int(webserver.arg("spiffs_size"))
|
||||||
|
var spiffs_slot = p.slots[-1] # last slot
|
||||||
|
|
||||||
|
var spiffs_max_size = ((tasmota.memory()['flash'] - (spiffs_slot.start / 1024)) / 16) * 16
|
||||||
|
|
||||||
|
if spiffs_slot == nil || !spiffs_slot.is_spiffs() raise "value_error", "Last slot is not SPIFFS type" end
|
||||||
|
var flash_size_kb = tasmota.memory()['flash']
|
||||||
|
if spiffs_size_kb < 0 || spiffs_size_kb > spiffs_max_size
|
||||||
|
raise "value_error", string.format("Invalid spiffs_size %i, should be between 0 and %i", spiffs_size_kb, spiffs_max_size)
|
||||||
|
end
|
||||||
|
if spiffs_size_kb == spiffs_slot.size/1024 raise "value_error", "SPIFFS size unchanged, abort" end
|
||||||
|
|
||||||
|
#- write the new SPIFFS partition size -#
|
||||||
|
spiffs_slot.size = spiffs_size_kb * 1024
|
||||||
|
p.save()
|
||||||
|
p.invalidate_spiffs() # erase SPIFFS or data is corrupt
|
||||||
|
|
||||||
|
#- and force restart -#
|
||||||
|
webserver.redirect("/?rst=")
|
||||||
|
|
||||||
|
elif webserver.has_arg("repartition")
|
||||||
|
if p.get_active() != 0 raise "value_error", "Can't repartition unless active partition is app0" end
|
||||||
|
#- complete repartition -#
|
||||||
|
var app0 = p.get_ota_slot(0)
|
||||||
|
var app1 = p.get_ota_slot(1)
|
||||||
|
var spiffs = p.slots[-1]
|
||||||
|
|
||||||
|
if !spiffs.is_spiffs() raise 'internal_error', 'No SPIFFS partition found' end
|
||||||
|
if app0 == nil || app1 == nil
|
||||||
|
raise "internal_error", "Unable to find partitions app0 and app1"
|
||||||
|
end
|
||||||
|
if p.ota_max() != 1
|
||||||
|
raise "internal_error", "There are more than 2 OTA partition, abort"
|
||||||
|
end
|
||||||
|
var app0_size_kb = ((app0.size / 1024 + 63) / 64) * 64 # rounded to upper 64kb
|
||||||
|
var app0_used_kb = (((app0.get_image_size()) / 1024 / 64) + 1) * 64
|
||||||
|
var flash_size_kb = tasmota.memory()['flash']
|
||||||
|
var app_size_max = 1984 # Max OTA size (4096 - 64) / 2 rounded to lowest 64KB
|
||||||
|
|
||||||
|
var part_size_kb = int(webserver.arg("repartition"))
|
||||||
|
if part_size_kb < app0_used_kb || part_size_kb > app_size_max
|
||||||
|
raise "value_error", string.printf("Invalid partition size %i KB, should be between %i and %i", part_size_kb, app0_used_kb, app_size_max)
|
||||||
|
end
|
||||||
|
if part_size_kb == app0_size_kb raise "value_error", "No change to partition size, abort" end
|
||||||
|
|
||||||
|
#- all good, proceed -#
|
||||||
|
# resize app0
|
||||||
|
app0.size = part_size_kb * 1024
|
||||||
|
# change app1
|
||||||
|
app1.start = app0.start + app0.size
|
||||||
|
app1.size = part_size_kb * 1024
|
||||||
|
# change spiffs
|
||||||
|
spiffs.start = app1.start + app1.size
|
||||||
|
spiffs.size = flash_size_kb * 1024 - spiffs.start
|
||||||
|
|
||||||
|
p.save()
|
||||||
|
p.invalidate_spiffs() # erase SPIFFS or data is corrupt
|
||||||
|
#- and force restart -#
|
||||||
|
webserver.redirect("/?rst=")
|
||||||
|
else
|
||||||
|
raise "value_error", "Unknown command"
|
||||||
|
end
|
||||||
|
except .. as e, m
|
||||||
|
print(string.format("BRY: Exception> '%s' - %s", e, m))
|
||||||
|
#- display error page -#
|
||||||
|
webserver.content_start("Parameter error") #- title of the web page -#
|
||||||
|
webserver.content_send_style() #- send standard Tasmota styles -#
|
||||||
|
|
||||||
|
webserver.content_send(string.format("<p style='width:340px;'><b>Exception:</b><br>'%s'<br>%s</p>", e, m))
|
||||||
|
# webserver.content_send("<p></p></fieldset><p></p>")
|
||||||
|
|
||||||
|
webserver.content_button(webserver.BUTTON_MANAGEMENT) #- button back to management page -#
|
||||||
|
webserver.content_stop() #- end of web page -#
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
# respond to web_add_handler() event to register web listeners
|
||||||
|
#- ---------------------------------------------------------------------- -#
|
||||||
|
#- this is called at Tasmota start-up, as soon as Wifi/Eth is up and web server running -#
|
||||||
|
def web_add_handler()
|
||||||
|
#- we need to register a closure, not just a function, that captures the current instance -#
|
||||||
|
webserver.on("/part_mgr", / -> self.page_part_mgr(), webserver.HTTP_GET)
|
||||||
|
webserver.on("/part_mgr", / -> self.page_part_ctl(), webserver.HTTP_POST)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
#- create and register driver in Tasmota -#
|
||||||
|
partition_manager = Partition_manager()
|
||||||
|
tasmota.add_driver(partition_manager)
|
||||||
|
## can be removed if put in 'autoexec.bat'
|
||||||
|
partition_manager.web_add_handler()
|
||||||
|
|
||||||
partition.Partition = Partition
|
partition.Partition = Partition
|
||||||
|
|
||||||
return partition
|
return partition
|
||||||
|
|
Binary file not shown.
|
@ -1,104 +0,0 @@
|
||||||
#-------------------------------------------------------------
|
|
||||||
- Parser manager for ESP32
|
|
||||||
-
|
|
||||||
-------------------------------------------------------------#
|
|
||||||
|
|
||||||
import flash
|
|
||||||
import string
|
|
||||||
import partition
|
|
||||||
import webserver
|
|
||||||
|
|
||||||
class Partition_manager : Driver
|
|
||||||
|
|
||||||
def init()
|
|
||||||
end
|
|
||||||
|
|
||||||
# create a method for adding a button to the main menu
|
|
||||||
# the button 'Partition Manager' redirects to '/part_mgr?'
|
|
||||||
def web_add_button()
|
|
||||||
webserver.content_send(
|
|
||||||
"<form id=but_part_mgr style='display: block;' action='part_mgr' method='get'><button>Partition Manager</button></form>")
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Show a single OTA Partition
|
|
||||||
#
|
|
||||||
def page_show_partition(slot, active)
|
|
||||||
webserver.content_send(string.format("<fieldset><legend><b title='Start: 0x%03X 000'> %s%s</b></legend>",
|
|
||||||
slot.start / 0x1000, slot.label, active ? " (active)" : ""))
|
|
||||||
|
|
||||||
webserver.content_send(string.format("<p><b>Partition size: </b>%i KB</p>", slot.size / 1024))
|
|
||||||
var used = slot.get_image_size()
|
|
||||||
if used >= 0
|
|
||||||
webserver.content_send(string.format("<p><b>Used: </b>%i KB</p>", used / 1024))
|
|
||||||
webserver.content_send(string.format("<p><b>Free: </b>%i KB</p>", (slot.size - used) / 1024))
|
|
||||||
else
|
|
||||||
webserver.content_send("<p><b>Used: unknwon")
|
|
||||||
webserver.content_send("<p><b>Free: unknwon")
|
|
||||||
end
|
|
||||||
|
|
||||||
webserver.content_send("<p></p></fieldset><p></p>")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Show a single OTA Partition
|
|
||||||
#
|
|
||||||
def page_show_spiffs(slot, free_mem)
|
|
||||||
webserver.content_send(string.format("<fieldset><legend><b title='Start: 0x%03X 000'> %s</b></legend>",
|
|
||||||
slot.start / 0x1000, slot.label))
|
|
||||||
|
|
||||||
webserver.content_send(string.format("<p><b>Partition size: </b>%i KB</p>", slot.size / 1024))
|
|
||||||
if free_mem != nil
|
|
||||||
webserver.content_send(string.format("<p><b>Unallocated: </b>%i KB</p>", free_mem / 1024))
|
|
||||||
end
|
|
||||||
|
|
||||||
webserver.content_send("<p></p></fieldset><p></p>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def page_show_partitions()
|
|
||||||
var p = partition.Partition()
|
|
||||||
|
|
||||||
# display ota partitions
|
|
||||||
for slot: p.slots
|
|
||||||
# is the slot app?
|
|
||||||
var ota_num = slot.is_ota()
|
|
||||||
if ota_num != nil
|
|
||||||
# we have an OTA partition
|
|
||||||
self.page_show_partition(slot, ota_num == p.otadata.active_otadata)
|
|
||||||
elif slot.is_spiffs()
|
|
||||||
var flash_size = tasmota.memory()['flash'] * 1024
|
|
||||||
var used_size = (slot.start + slot.size)
|
|
||||||
self.page_show_spiffs(slot, slot == p.slots[-1] ? flash_size - used_size : nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#- this method displays the web page -#
|
|
||||||
def page_part_mgr()
|
|
||||||
if !webserver.check_privileged_access() return nil end
|
|
||||||
|
|
||||||
webserver.content_start("Partition Manager") #- title of the web page -#
|
|
||||||
webserver.content_send_style() #- send standard Tasmota styles -#
|
|
||||||
|
|
||||||
webserver.content_send("<fieldset><legend><b> Partition Manager</b></legend><p></p>")
|
|
||||||
self.page_show_partitions()
|
|
||||||
webserver.content_send("<p></p></fieldset><p></p>")
|
|
||||||
|
|
||||||
webserver.content_button(webserver.BUTTON_MANAGEMENT) #- button back to management page -#
|
|
||||||
webserver.content_stop() #- end of web page -#
|
|
||||||
end
|
|
||||||
|
|
||||||
#- this is called at Tasmota start-up, as soon as Wifi/Eth is up and web server running -#
|
|
||||||
def web_add_handler()
|
|
||||||
#- we need to register a closure, not just a function, that captures the current instance -#
|
|
||||||
webserver.on("/part_mgr", / -> self.page_part_mgr())
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
#- create and register driver in Tasmota -#
|
|
||||||
partition_manager = Partition_manager()
|
|
||||||
tasmota.add_driver(partition_manager)
|
|
||||||
## to be removed if put in 'autoexec.bat'
|
|
||||||
partition_manager.web_add_handler()
|
|
|
@ -128,6 +128,21 @@ extern "C" {
|
||||||
be_return(vm);
|
be_return(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Berry: `webserver.redirect(string) -> nil`
|
||||||
|
//
|
||||||
|
int32_t w_webserver_redirect(struct bvm *vm);
|
||||||
|
int32_t w_webserver_redirect(struct bvm *vm) {
|
||||||
|
int32_t argc = be_top(vm); // Get the number of arguments
|
||||||
|
if (argc >= 1 && be_isstring(vm, 1)) {
|
||||||
|
const char * uri = be_tostring(vm, 1);
|
||||||
|
Webserver->sendHeader("Location", uri, true);
|
||||||
|
Webserver->send(302, "text/plain", "");
|
||||||
|
// Webserver->sendHeader(F("Location"), String(F("http://")) + Webserver->client().localIP().toString(), true);
|
||||||
|
be_return_nil(vm);
|
||||||
|
}
|
||||||
|
be_raise(vm, kTypeError, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// Berry: `webserver.content_start() -> nil`
|
// Berry: `webserver.content_start() -> nil`
|
||||||
//
|
//
|
||||||
int32_t w_webserver_content_start(struct bvm *vm);
|
int32_t w_webserver_content_start(struct bvm *vm);
|
||||||
|
@ -141,7 +156,7 @@ extern "C" {
|
||||||
be_raise(vm, kTypeError, nullptr);
|
be_raise(vm, kTypeError, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Berry: `webserver.content_send() -> nil`
|
// Berry: `webserver.content_send(string) -> nil`
|
||||||
//
|
//
|
||||||
int32_t w_webserver_content_send(struct bvm *vm);
|
int32_t w_webserver_content_send(struct bvm *vm);
|
||||||
int32_t w_webserver_content_send(struct bvm *vm) {
|
int32_t w_webserver_content_send(struct bvm *vm) {
|
||||||
|
@ -223,6 +238,20 @@ extern "C" {
|
||||||
be_raise(vm, kTypeError, nullptr);
|
be_raise(vm, kTypeError, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Berry: `webserver.arg_name(int) -> string`
|
||||||
|
//
|
||||||
|
// takes an int (index 0..args-1)
|
||||||
|
int32_t w_webserver_arg_name(struct bvm *vm);
|
||||||
|
int32_t w_webserver_arg_name(struct bvm *vm) {
|
||||||
|
int32_t argc = be_top(vm); // Get the number of arguments
|
||||||
|
if (argc >= 1 && be_isint(vm, 1)) {
|
||||||
|
int32_t idx = be_toint(vm, 1);
|
||||||
|
be_pushstring(vm, Webserver->argName(idx).c_str());
|
||||||
|
be_return(vm);
|
||||||
|
}
|
||||||
|
be_raise(vm, kTypeError, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// Berry: `webserver.has_arg(name:string) -> bool`
|
// Berry: `webserver.has_arg(name:string) -> bool`
|
||||||
//
|
//
|
||||||
int32_t w_webserver_has_arg(struct bvm *vm);
|
int32_t w_webserver_has_arg(struct bvm *vm);
|
||||||
|
|
Loading…
Reference in New Issue