mirror of https://github.com/arendst/Tasmota.git
546 lines
18 KiB
C++
546 lines
18 KiB
C++
/*
|
|
xdrv_52_3_berry_webserver.ino - Berry scripting language, webserver module
|
|
|
|
Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// also includes tcp_client
|
|
|
|
#ifdef USE_BERRY
|
|
|
|
#ifdef USE_WEBCLIENT
|
|
|
|
#include <berry.h>
|
|
#include "HttpClientLight.h"
|
|
#include "be_sys.h"
|
|
|
|
// Tasmota extension
|
|
extern File * be_get_arduino_file(void *hfile);
|
|
|
|
String wc_UrlEncode(const String& text) {
|
|
const char hex[] = "0123456789ABCDEF";
|
|
|
|
String encoded = "";
|
|
int len = text.length();
|
|
int i = 0;
|
|
while (i < len) {
|
|
char decodedChar = text.charAt(i++);
|
|
|
|
if (('a' <= decodedChar && decodedChar <= 'z') ||
|
|
('A' <= decodedChar && decodedChar <= 'Z') ||
|
|
('0' <= decodedChar && decodedChar <= '9') ||
|
|
('=' == decodedChar)) {
|
|
encoded += decodedChar;
|
|
} else {
|
|
encoded += '%';
|
|
encoded += hex[decodedChar >> 4];
|
|
encoded += hex[decodedChar & 0xF];
|
|
}
|
|
|
|
}
|
|
return encoded;
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* Int constants
|
|
*********************************************************************************************/
|
|
// const be_const_member_t webserver_constants[] = {
|
|
// { "BUTTON_CONFIGURATION", BUTTON_CONFIGURATION },
|
|
// { "BUTTON_INFORMATION", BUTTON_INFORMATION },
|
|
// { "BUTTON_MAIN", BUTTON_MAIN },
|
|
// { "BUTTON_MANAGEMENT", BUTTON_MANAGEMENT },
|
|
// { "BUTTON_MODULE", BUTTON_MODULE },
|
|
// { "HTTP_ADMIN", HTTP_ADMIN },
|
|
// { "HTTP_ANY", HTTP_ANY },
|
|
// { "HTTP_GET", HTTP_GET },
|
|
// { "HTTP_MANAGER", HTTP_MANAGER },
|
|
// { "HTTP_MANAGER_RESET_ONLY", HTTP_MANAGER_RESET_ONLY },
|
|
// { "HTTP_OFF", HTTP_OFF },
|
|
// { "HTTP_OPTIONS", HTTP_OPTIONS },
|
|
// { "HTTP_POST", HTTP_POST },
|
|
// { "HTTP_USER", HTTP_USER },
|
|
// };
|
|
|
|
/*********************************************************************************************\
|
|
* Native functions mapped to Berry functions
|
|
*
|
|
* import webclient
|
|
*
|
|
\*********************************************************************************************/
|
|
extern "C" {
|
|
// Berry: ``
|
|
//
|
|
int32_t wc_init(struct bvm *vm);
|
|
int32_t wc_init(struct bvm *vm) {
|
|
WiFiClient * wcl = new WiFiClient();
|
|
be_pushcomptr(vm, (void*) wcl);
|
|
be_setmember(vm, 1, ".w");
|
|
HTTPClientLight * cl = new HTTPClientLight();
|
|
cl->setUserAgent(USE_BERRY_WEBCLIENT_USERAGENT);
|
|
cl->setConnectTimeout(USE_BERRY_WEBCLIENT_TIMEOUT); // set default timeout
|
|
be_pushcomptr(vm, (void*) cl);
|
|
be_setmember(vm, 1, ".p");
|
|
be_return_nil(vm);
|
|
}
|
|
|
|
int32_t wc_tcp_init(struct bvm *vm);
|
|
int32_t wc_tcp_init(struct bvm *vm) {
|
|
WiFiClient * wcl = new WiFiClient();
|
|
be_pushcomptr(vm, (void*) wcl);
|
|
be_setmember(vm, 1, ".w");
|
|
be_return_nil(vm);
|
|
}
|
|
|
|
HTTPClientLight * wc_getclient(struct bvm *vm) {
|
|
be_getmember(vm, 1, ".p");
|
|
void *p = be_tocomptr(vm, -1);
|
|
be_pop(vm, 1);
|
|
return (HTTPClientLight*) p;
|
|
}
|
|
|
|
WiFiClient * wc_getwificlient(struct bvm *vm) {
|
|
be_getmember(vm, 1, ".w");
|
|
void *p = be_tocomptr(vm, -1);
|
|
be_pop(vm, 1);
|
|
return (WiFiClient*) p;
|
|
}
|
|
|
|
int32_t wc_deinit(struct bvm *vm);
|
|
int32_t wc_deinit(struct bvm *vm) {
|
|
// int32_t argc = be_top(vm); // Get the number of arguments
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
if (cl != nullptr) { delete cl; }
|
|
be_pushnil(vm);
|
|
be_setmember(vm, 1, ".p");
|
|
WiFiClient * wcl = wc_getwificlient(vm);
|
|
if (wcl != nullptr) { delete wcl; }
|
|
be_setmember(vm, 1, ".w");
|
|
be_return_nil(vm);
|
|
}
|
|
|
|
int32_t wc_tcp_deinit(struct bvm *vm);
|
|
int32_t wc_tcp_deinit(struct bvm *vm) {
|
|
WiFiClient * wcl = wc_getwificlient(vm);
|
|
if (wcl != nullptr) { delete wcl; }
|
|
be_setmember(vm, 1, ".w");
|
|
be_return_nil(vm);
|
|
}
|
|
|
|
// wc.url_encode(string) -> string
|
|
int32_t wc_urlencode(struct bvm *vm);
|
|
int32_t wc_urlencode(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 2 && be_isstring(vm, 2)) {
|
|
const char * s = be_tostring(vm, 2);
|
|
String url = wc_UrlEncode(String(s));
|
|
be_pushstring(vm, url.c_str());
|
|
be_return(vm); /* return self */
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// wc.begin(url:string) -> self
|
|
int32_t wc_begin(struct bvm *vm);
|
|
int32_t wc_begin(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc == 1 || !be_tostring(vm, 2)) { be_raise(vm, "attribute_error", "missing URL as string"); }
|
|
const char * url = be_tostring(vm, 2);
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
// open connection
|
|
if (!cl->begin(url)) {
|
|
be_raise(vm, "value_error", "unsupported protocol");
|
|
}
|
|
be_pushvalue(vm, 1);
|
|
be_return(vm); /* return self */
|
|
}
|
|
|
|
// tcp.connect(address:string, port:int [, timeout_ms:int]) -> bool
|
|
int32_t wc_tcp_connect(struct bvm *vm);
|
|
int32_t wc_tcp_connect(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 3 && be_isstring(vm, 2) && be_isint(vm, 3)) {
|
|
WiFiClient * tcp = wc_getwificlient(vm);
|
|
const char * address = be_tostring(vm, 2);
|
|
int32_t port = be_toint(vm, 3);
|
|
int32_t timeout = USE_BERRY_WEBCLIENT_TIMEOUT; // default timeout of 2 seconds
|
|
if (argc >= 4) {
|
|
timeout = be_toint(vm, 4);
|
|
}
|
|
// open connection
|
|
bool success = tcp->connect(address, port, timeout);
|
|
be_pushbool(vm, success);
|
|
be_return(vm); /* return self */
|
|
}
|
|
be_raise(vm, "attribute_error", NULL);
|
|
}
|
|
|
|
// wc.close(void) -> nil
|
|
int32_t wc_close(struct bvm *vm);
|
|
int32_t wc_close(struct bvm *vm) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
cl->end();
|
|
be_return_nil(vm);
|
|
}
|
|
|
|
// tcp.close(void) -> nil
|
|
int32_t wc_tcp_close(struct bvm *vm);
|
|
int32_t wc_tcp_close(struct bvm *vm) {
|
|
WiFiClient * tcp = wc_getwificlient(vm);
|
|
tcp->stop();
|
|
be_return_nil(vm);
|
|
}
|
|
|
|
// tcp.available(void) -> int
|
|
int32_t wc_tcp_available(struct bvm *vm);
|
|
int32_t wc_tcp_available(struct bvm *vm) {
|
|
WiFiClient * tcp = wc_getwificlient(vm);
|
|
int32_t available = tcp->available();
|
|
be_pushint(vm, available);
|
|
be_return(vm);
|
|
}
|
|
|
|
// wc.wc_set_timeouts([http_timeout_ms:int, tcp_timeout_ms:int]) -> self
|
|
int32_t wc_set_timeouts(struct bvm *vm);
|
|
int32_t wc_set_timeouts(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
if (argc >= 2) {
|
|
cl->setTimeout(be_toint(vm, 2));
|
|
}
|
|
if (argc >= 3) {
|
|
cl->setConnectTimeout(be_toint(vm, 3));
|
|
}
|
|
be_pushvalue(vm, 1);
|
|
be_return(vm); /* return self */
|
|
}
|
|
|
|
// wc.set_useragent(user_agent:string) -> self
|
|
int32_t wc_set_useragent(struct bvm *vm);
|
|
int32_t wc_set_useragent(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 2 && be_isstring(vm, 2)) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
const char * useragent = be_tostring(vm, 2);
|
|
cl->setUserAgent(String(useragent));
|
|
be_pushvalue(vm, 1);
|
|
be_return(vm); /* return self */
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// wc.wc_set_auth(auth:string | (user:string, password:string)) -> self
|
|
int32_t wc_set_auth(struct bvm *vm);
|
|
int32_t wc_set_auth(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 2 && be_isstring(vm, 2) && (argc < 3 || be_isstring(vm, 3))) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
const char * user = be_tostring(vm, 2);
|
|
if (argc == 2) {
|
|
cl->setAuthorization(user);
|
|
} else {
|
|
const char * password = be_tostring(vm, 3);
|
|
cl->setAuthorization(user, password);
|
|
}
|
|
be_pushvalue(vm, 1);
|
|
be_return(vm); /* return self */
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// wc.addheader(name:string, value:string [, first:bool=false [, replace:bool=true]]) -> nil
|
|
int32_t wc_addheader(struct bvm *vm);
|
|
int32_t wc_addheader(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 3 && (be_isstring(vm, 2) || be_isstring(vm, 2))) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
|
|
const char * name = be_tostring(vm, 2);
|
|
const char * value = be_tostring(vm, 3);
|
|
bool first = false;
|
|
bool replace = true;
|
|
if (argc >= 4) {
|
|
first = be_tobool(vm, 4);
|
|
}
|
|
if (argc >= 5) {
|
|
replace = be_tobool(vm, 5);
|
|
}
|
|
// do the call
|
|
cl->addHeader(String(name), String(value), first, replace);
|
|
be_return_nil(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// cw.connected(void) -> bool
|
|
int32_t wc_connected(struct bvm *vm);
|
|
int32_t wc_connected(struct bvm *vm) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
be_pushbool(vm, cl->connected());
|
|
be_return(vm); /* return code */
|
|
}
|
|
|
|
// tcp.connected(void) -> bool
|
|
int32_t wc_tcp_connected(struct bvm *vm);
|
|
int32_t wc_tcp_connected(struct bvm *vm) {
|
|
WiFiClient * tcp = wc_getwificlient(vm);
|
|
be_pushbool(vm, tcp->connected());
|
|
be_return(vm); /* return code */
|
|
}
|
|
|
|
// tcp.write(bytes | string) -> int
|
|
int32_t wc_tcp_write(struct bvm *vm);
|
|
int32_t wc_tcp_write(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 2 && (be_isstring(vm, 2) || be_isbytes(vm, 2))) {
|
|
WiFiClient * tcp = wc_getwificlient(vm);
|
|
const char * buf = nullptr;
|
|
size_t buf_len = 0;
|
|
if (be_isstring(vm, 2)) { // string
|
|
buf = be_tostring(vm, 2);
|
|
buf_len = strlen(buf);
|
|
} else { // bytes
|
|
buf = (const char*) be_tobytes(vm, 2, &buf_len);
|
|
}
|
|
size_t bw = tcp->write(buf, buf_len);
|
|
be_pushint(vm, bw);
|
|
be_return(vm); /* return code */
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// tcp.read() -> string
|
|
int32_t wc_tcp_read(struct bvm *vm);
|
|
int32_t wc_tcp_read(struct bvm *vm) {
|
|
WiFiClient * tcp = wc_getwificlient(vm);
|
|
int32_t max_read = -1; // by default read as much as we can
|
|
if (be_top(vm) >= 2 && be_isint(vm, 2)) {
|
|
max_read = be_toint(vm, 2);
|
|
}
|
|
int32_t btr = tcp->available();
|
|
if (btr <= 0) {
|
|
be_pushstring(vm, "");
|
|
} else {
|
|
if ((max_read >= 0) && (btr > max_read)) {
|
|
btr = max_read;
|
|
}
|
|
char * buf = (char*) be_pushbuffer(vm, btr);
|
|
int32_t btr2 = tcp->read((uint8_t*) buf, btr);
|
|
be_pushnstring(vm, buf, btr2);
|
|
}
|
|
be_return(vm); /* return code */
|
|
}
|
|
|
|
// tcp.readbytes() -> bytes
|
|
int32_t wc_tcp_readbytes(struct bvm *vm);
|
|
int32_t wc_tcp_readbytes(struct bvm *vm) {
|
|
WiFiClient * tcp = wc_getwificlient(vm);
|
|
int32_t max_read = -1; // by default read as much as we can
|
|
if (be_top(vm) >= 2 && be_isint(vm, 2)) {
|
|
max_read = be_toint(vm, 2);
|
|
}
|
|
int32_t btr = tcp->available();
|
|
if (btr <= 0) {
|
|
be_pushbytes(vm, nullptr, 0);
|
|
} else {
|
|
if ((max_read >= 0) && (btr > max_read)) {
|
|
btr = max_read;
|
|
}
|
|
uint8_t * buf = (uint8_t*) be_pushbuffer(vm, btr);
|
|
int32_t btr2 = tcp->read(buf, btr);
|
|
be_pushbytes(vm, buf, btr2);
|
|
}
|
|
be_return(vm); /* return code */
|
|
}
|
|
|
|
void wc_errorCodeMessage(int32_t httpCode, uint32_t http_connect_time) {
|
|
if (httpCode < 0) {
|
|
if (httpCode <= -1000) {
|
|
AddLog(LOG_LEVEL_INFO, D_LOG_HTTP "TLS connection error %d after %d ms", -httpCode - 1000, millis() - http_connect_time);
|
|
} else if (httpCode == -1) {
|
|
AddLog(LOG_LEVEL_INFO, D_LOG_HTTP "Connection timeout after %d ms", httpCode, millis() - http_connect_time);
|
|
} else {
|
|
AddLog(LOG_LEVEL_INFO, D_LOG_HTTP "Connection error %d after %d ms", httpCode, millis() - http_connect_time);
|
|
}
|
|
} else {
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Connected in %d ms, stack low mark %d"),
|
|
millis() - http_connect_time, uxTaskGetStackHighWaterMark(nullptr));
|
|
}
|
|
}
|
|
|
|
// cw.GET(void) -> httpCode:int
|
|
int32_t wc_GET(struct bvm *vm);
|
|
int32_t wc_GET(struct bvm *vm) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
uint32_t http_connect_time = millis();
|
|
int32_t httpCode = cl->GET();
|
|
wc_errorCodeMessage(httpCode, http_connect_time);
|
|
be_pushint(vm, httpCode);
|
|
be_return(vm); /* return code */
|
|
}
|
|
|
|
// wc.POST(string | bytes) -> httpCode:int
|
|
int32_t wc_POST(struct bvm *vm);
|
|
int32_t wc_POST(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 2 && (be_isstring(vm, 2) || be_isbytes(vm, 2))) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
const char * buf = nullptr;
|
|
size_t buf_len = 0;
|
|
if (be_isstring(vm, 2)) { // string
|
|
buf = be_tostring(vm, 2);
|
|
buf_len = strlen(buf);
|
|
} else { // bytes
|
|
buf = (const char*) be_tobytes(vm, 2, &buf_len);
|
|
}
|
|
uint32_t http_connect_time = millis();
|
|
int32_t httpCode = cl->POST((uint8_t*)buf, buf_len);
|
|
wc_errorCodeMessage(httpCode, http_connect_time);
|
|
be_pushint(vm, httpCode);
|
|
be_return(vm); /* return code */
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
int32_t wc_getstring(struct bvm *vm);
|
|
int32_t wc_getstring(struct bvm *vm) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
int32_t sz = cl->getSize();
|
|
// abort if we exceed 32KB size, things will not go well otherwise
|
|
if (sz >= 32767) {
|
|
be_raise(vm, "value_error", "response size too big (>32KB)");
|
|
}
|
|
String payload = cl->getString();
|
|
be_pushstring(vm, payload.c_str());
|
|
cl->end(); // free allocated memory ~16KB
|
|
be_return(vm); /* return code */
|
|
}
|
|
|
|
int32_t wc_writefile(struct bvm *vm);
|
|
int32_t wc_writefile(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 2 && be_isstring(vm, 2)) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
const char * fname = be_tostring(vm, 2);
|
|
|
|
void * f = be_fopen(fname, "w");
|
|
int ret = -1;
|
|
if (f) {
|
|
File * fptr = be_get_arduino_file(f);
|
|
ret = cl->writeToStream(fptr);
|
|
}
|
|
be_fclose(f);
|
|
be_pushint(vm, ret);
|
|
be_return(vm); /* return code */
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
int32_t wc_getsize(struct bvm *vm);
|
|
int32_t wc_getsize(struct bvm *vm) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
be_pushint(vm, cl->getSize());
|
|
be_return(vm); /* return code */
|
|
}
|
|
}
|
|
|
|
extern size_t FlashWriteSubSector(uint32_t address_start, const uint8_t *data, size_t size);
|
|
|
|
const uint32_t STREAM_FLASH_PROGRESS_THRESHOLD_KB = 100; // display log entry every 100kB
|
|
const uint32_t STREAM_FLASH_PROGRESS_THRESHOLD = STREAM_FLASH_PROGRESS_THRESHOLD_KB * 1024;
|
|
|
|
class StreamFlash: public Stream
|
|
{
|
|
public:
|
|
StreamFlash(uint32_t addr) : addr_start(addr), offset(0) {};
|
|
|
|
size_t write(const uint8_t *buffer, size_t size) override {
|
|
// AddLog(LOG_LEVEL_INFO, "FLASH: addr=%p hex=%*_H size=%i", addr_start + offset, 32, buffer, size);
|
|
if (size > 0) {
|
|
esp_err_t ret = spi_flash_write(addr_start + offset, buffer, size);
|
|
if (ret != ESP_OK) { return 0; } // error
|
|
offset += size;
|
|
|
|
// shall we display a progress indicator?
|
|
if (((offset - size) / STREAM_FLASH_PROGRESS_THRESHOLD) != (offset / STREAM_FLASH_PROGRESS_THRESHOLD)) {
|
|
AddLog(LOG_LEVEL_DEBUG, D_LOG_UPLOAD "Progress %d kB", offset / 1024);
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
size_t write(uint8_t data) override {
|
|
write(&data, 1);
|
|
return 1;
|
|
}
|
|
|
|
int available() override { return 0; }
|
|
int read() override { return -1; }
|
|
int peek() override { return -1; }
|
|
void flush() override { }
|
|
|
|
protected:
|
|
uint32_t addr_start; // start address
|
|
uint32_t offset; // how many bytes have already been written
|
|
};
|
|
|
|
extern "C" {
|
|
int32_t wc_writeflash(struct bvm *vm);
|
|
int32_t wc_writeflash(struct bvm *vm) {
|
|
int32_t argc = be_top(vm);
|
|
if (argc >= 2 && be_isint(vm, 2)) {
|
|
HTTPClientLight * cl = wc_getclient(vm);
|
|
uint32_t addr = be_toint(vm, 2);
|
|
if (addr < 0x10000 || addr >= 0x400000) {
|
|
be_raisef(vm, "value_error", "invalid flash address 0x04X", addr);
|
|
}
|
|
if (addr & (SPI_FLASH_SEC_SIZE-1) != 0) {
|
|
be_raisef(vm, "value_error", "invalid flash address, must be at %iKB boundary 0x%04X", SPI_FLASH_SEC_SIZE/1024, addr);
|
|
}
|
|
int32_t size = cl->getSize();
|
|
if (size <= 0 || addr+size >= 0x400000) {
|
|
be_raisef(vm, "value_error", "invalid flash size 0x%04X", size);
|
|
}
|
|
|
|
int32_t size_rounded_4k = (size + SPI_FLASH_SEC_SIZE - 1) & ~(SPI_FLASH_SEC_SIZE - 1);
|
|
|
|
// erase region
|
|
esp_err_t ret;
|
|
AddLog(LOG_LEVEL_DEBUG, D_LOG_UPLOAD "erasing flash at 0x%04X length 0x%04X", addr, size_rounded_4k);
|
|
ret = spi_flash_erase_range(addr, size_rounded_4k);
|
|
if (ret != ESP_OK) {
|
|
be_raisef(vm, "internal_error", "unable to erase flash region (%i)", ret);
|
|
}
|
|
|
|
StreamFlash flash_writer(addr);
|
|
AddLog(LOG_LEVEL_DEBUG, D_LOG_UPLOAD "writing flash at 0x%04X size 0x%04X", addr, size);
|
|
int32_t written = cl->writeToStream(&flash_writer);
|
|
if (written <= 0) {
|
|
be_raisef(vm, "internal_error", "unable to write flash (%i)", written);
|
|
}
|
|
if (written != size) {
|
|
be_raisef(vm, "internal_error", "failed, written %i bytes vs %i", written, size);
|
|
}
|
|
AddLog(LOG_LEVEL_DEBUG, D_LOG_UPLOAD "flash writing succesful");
|
|
|
|
be_pushint(vm, written);
|
|
be_return(vm); /* return code */
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
}
|
|
|
|
#endif // USE_WEBCLIENT
|
|
#endif // USE_BERRY
|