diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1cb3f42cd..e542181b9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,7 +3,7 @@ **Related issue (if applicable):** fixes # ## Checklist: - - [ ] The pull request is done against the latest dev branch + - [ ] The pull request is done against the latest development branch - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR and the code change compiles without warnings - [ ] The code change is tested and works on Tasmota core ESP8266 V.2.7.4.9 diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml index fe06a70d2..9e8f61219 100644 --- a/.github/workflows/Tasmota_build.yml +++ b/.github/workflows/Tasmota_build.yml @@ -1013,6 +1013,29 @@ jobs: path: ./build_output/firmware + tasmota32-AF: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + platformio run -e tasmota32-AF + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + tasmota32-BG: needs: tasmota_pull runs-on: ubuntu-latest @@ -1560,6 +1583,7 @@ jobs: mkdir -p ./firmware/tasmota/languages mkdir -p ./firmware/tasmota32/languages mkdir -p ./firmware/tasmota32/ESP32_needed_files/ + mkdir -p ./firmware/tasmota32/Odroid_go_needed_files/ [ ! -f ./mv_firmware/tasmota.* ] || mv ./mv_firmware/tasmota.* ./firmware/tasmota/ [ ! -f ./mv_firmware/tasmota-sensors.* ] || mv ./mv_firmware/tasmota-sensors.* ./firmware/tasmota/ [ ! -f ./mv_firmware/tasmota-minimal.* ] || mv ./mv_firmware/tasmota-minimal.* ./firmware/tasmota/ @@ -1575,12 +1599,14 @@ jobs: [ ! -f ./mv_firmware/tasmota32-ir*.* ] || mv ./mv_firmware/tasmota32-ir*.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32-display.* ] || mv ./mv_firmware/tasmota32-display.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32-web*.* ] || mv ./mv_firmware/tasmota32-web*.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/tasmota32-odroidgo.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/ [ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/ rm ./firmware/tasmota32/*.gz rm ./firmware/tasmota32/languages/*.gz [ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/ + [ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/ [ ! -f ./FIRMWARE.md ] || mv -f ./FIRMWARE.md ./README.md - name: Commit files # transfer the new binaries back into the repository run: | diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml index 4169bd028..8c00d25e3 100644 --- a/.github/workflows/Tasmota_build_master.yml +++ b/.github/workflows/Tasmota_build_master.yml @@ -1013,6 +1013,29 @@ jobs: path: ./build_output/firmware + tasmota32-AF: + needs: tasmota_pull + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: | + platformio run -e tasmota32-AF + - uses: actions/upload-artifact@v2 + with: + name: firmware + path: ./build_output/firmware + + tasmota32-BG: needs: tasmota_pull runs-on: ubuntu-latest @@ -1560,6 +1583,7 @@ jobs: mkdir -p ./firmware/tasmota/languages mkdir -p ./firmware/tasmota32/languages mkdir -p ./firmware/tasmota32/ESP32_needed_files/ + mkdir -p ./firmware/tasmota32/Odroid_go_needed_files/ [ ! -f ./mv_firmware/tasmota.* ] || mv ./mv_firmware/tasmota.* ./firmware/tasmota/ [ ! -f ./mv_firmware/tasmota-sensors.* ] || mv ./mv_firmware/tasmota-sensors.* ./firmware/tasmota/ [ ! -f ./mv_firmware/tasmota-minimal.* ] || mv ./mv_firmware/tasmota-minimal.* ./firmware/tasmota/ @@ -1575,12 +1599,14 @@ jobs: [ ! -f ./mv_firmware/tasmota32-ir*.* ] || mv ./mv_firmware/tasmota32-ir*.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32-display.* ] || mv ./mv_firmware/tasmota32-display.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32-web*.* ] || mv ./mv_firmware/tasmota32-web*.* ./firmware/tasmota32/ + [ ! -f ./mv_firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/tasmota32-odroidgo.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/ [ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/ rm ./firmware/tasmota32/*.gz rm ./firmware/tasmota32/languages/*.gz [ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/ + [ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/ [ ! -f ./FIRMWARE.md ] || mv -f ./RELEASENOTES.md ./README.md - name: Commit files # transfer the new binaries back into the repository run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index bd49bc4c8..d542934fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. ## [9.2.0.2] ### Added - Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630) +- Command ``CTRange`` to specify the visible CT range the bulb is capable of +- Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels ### Breaking Changed - Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_RC522_CS`` diff --git a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp index f45b33095..74837242b 100644 --- a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp +++ b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp @@ -27,7 +27,7 @@ uint16_t Arduino_ST7789::GetColorFromIndex(uint8_t index) { } static const uint8_t PROGMEM - cmd_240x240[] = { // Initialization commands for 7789 screens + init_cmd[] = { // Initialization commands for 7789 screens 10, // 9 commands in list: ST7789_SWRESET, ST_CMD_DELAY, // 1: Software reset, no args, w/delay 150, // 150 ms delay @@ -38,14 +38,6 @@ static const uint8_t PROGMEM 10, // 10 ms delay ST7789_MADCTL , 1, // 4: Memory access ctrl (directions), 1 arg: 0x00, // Row addr/col addr, bottom to top refresh - ST7789_CASET , 4, // 5: Column addr set, 4 args, no delay: - 0x00, ST7789_240x240_XSTART, // XSTART = 0 - (ST7789_TFTWIDTH+ST7789_240x240_XSTART) >> 8, - (ST7789_TFTWIDTH+ST7789_240x240_XSTART) & 0xFF, // XEND = 240 - ST7789_RASET , 4, // 6: Row addr set, 4 args, no delay: - 0x00, ST7789_240x240_YSTART, // YSTART = 0 - (ST7789_TFTHEIGHT+ST7789_240x240_YSTART) >> 8, - (ST7789_TFTHEIGHT+ST7789_240x240_YSTART) & 0xFF, // YEND = 240 ST7789_INVON , ST_CMD_DELAY, // 7: Inversion ON 10, ST7789_NORON , ST_CMD_DELAY, // 8: Normal display on, no args, w/delay @@ -75,7 +67,7 @@ inline uint16_t swapcolor(uint16_t x) { // Constructor when using software SPI. All output pins are configurable. Arduino_ST7789::Arduino_ST7789(int8_t dc, int8_t rst, int8_t sid, int8_t sclk, int8_t cs, int8_t bp) - : Renderer(ST7789_TFTWIDTH, ST7789_TFTHEIGHT) + : Renderer(_width, _height) { _cs = cs; _dc = dc; @@ -91,7 +83,7 @@ Arduino_ST7789::Arduino_ST7789(int8_t dc, int8_t rst, int8_t sid, int8_t sclk, i // Constructor when using hardware SPI. Faster, but must use SPI pins // specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.) Arduino_ST7789::Arduino_ST7789(int8_t dc, int8_t rst, int8_t cs, int8_t bp) - : Renderer(ST7789_TFTWIDTH, ST7789_TFTHEIGHT) { + : Renderer(_width, _height) { _cs = cs; _dc = dc; _rst = rst; @@ -335,29 +327,59 @@ void Arduino_ST7789::setRotation(uint8_t m) { case 0: writedata(ST7789_MADCTL_MX | ST7789_MADCTL_MY | ST7789_MADCTL_RGB); - _xstart = _colstart; - // _ystart = _rowstart; - _ystart = 80; + _xstart = 0; + _ystart = 0; + if (_width==240 && _height==240) { + _xstart = ST7789_240x240_XSTART_R0; + _ystart = ST7789_240x240_YSTART_R0; + } + if (_width==135 && _height==240) { + _xstart = ST7789_135x240_XSTART_R0; + _ystart = ST7789_135x240_YSTART_R0; + } break; case 1: writedata(ST7789_MADCTL_MY | ST7789_MADCTL_MV | ST7789_MADCTL_RGB); - _ystart = _colstart; - // _xstart = _rowstart; - _xstart = 80; + _ystart = 0; + _xstart = 0; + if (_width==240 && _height==240) { + _xstart = ST7789_240x240_XSTART_R1; + _ystart = ST7789_240x240_YSTART_R1; + } + if (_width==240 && _height==135) { + _xstart = ST7789_135x240_XSTART_R1; + _ystart = ST7789_135x240_YSTART_R1; + } break; case 2: writedata(ST7789_MADCTL_RGB); - _xstart = _colstart; - _ystart = _rowstart; + _xstart = 0; + _ystart = 0; + if (_width==240 && _height==240) { + _xstart = ST7789_240x240_XSTART_R2; + _ystart = ST7789_240x240_YSTART_R2; + } + if (_width==135 && _height==240) { + _xstart = ST7789_135x240_XSTART_R2; + _ystart = ST7789_135x240_YSTART_R2; + } break; case 3: writedata(ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB); - _ystart = _colstart; - _xstart = _rowstart; + _xstart = 0; + _ystart = 0; + if (_width==240 && _height==240) { + _xstart = ST7789_240x240_XSTART_R3; + _ystart = ST7789_240x240_YSTART_R3; + } + if (_width==240 && _height==135) { + _xstart = ST7789_135x240_XSTART_R3; + _ystart = ST7789_135x240_YSTART_R3; + } break; } } @@ -533,12 +555,10 @@ inline void Arduino_ST7789::DC_LOW(void) { void Arduino_ST7789::init(uint16_t width, uint16_t height) { commonInit(NULL); - _colstart = ST7789_240x240_XSTART; - _rowstart = ST7789_240x240_YSTART; _height = height; _width = width; - displayInit(cmd_240x240); + displayInit(init_cmd); setRotation(2); diff --git a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h index 2d97346e7..3dfaa03a8 100644 --- a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h +++ b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h @@ -39,11 +39,23 @@ //#define SPI_HAS_TRANSACTION // already defined in SPI.h -#define ST7789_TFTWIDTH 240 -#define ST7789_TFTHEIGHT 240 +#define ST7789_240x240_XSTART_R0 0 +#define ST7789_240x240_YSTART_R0 80 +#define ST7789_240x240_XSTART_R1 80 +#define ST7789_240x240_YSTART_R1 0 +#define ST7789_240x240_XSTART_R2 0 +#define ST7789_240x240_YSTART_R2 0 +#define ST7789_240x240_XSTART_R3 0 +#define ST7789_240x240_YSTART_R3 0 -#define ST7789_240x240_XSTART 0 -#define ST7789_240x240_YSTART 0 +#define ST7789_135x240_XSTART_R0 53 +#define ST7789_135x240_YSTART_R0 40 +#define ST7789_135x240_XSTART_R1 40 +#define ST7789_135x240_YSTART_R1 52 +#define ST7789_135x240_XSTART_R2 52 +#define ST7789_135x240_YSTART_R2 40 +#define ST7789_135x240_XSTART_R3 40 +#define ST7789_135x240_YSTART_R3 53 #define ST_CMD_DELAY 0x80 // special signifier for command lists diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp index d7b53cf25..7d9d51a35 100755 --- a/tasmota/WiFiClientSecureLightBearSSL.cpp +++ b/tasmota/WiFiClientSecureLightBearSSL.cpp @@ -57,8 +57,8 @@ extern "C" { #include "coredecls.h" #define LOG_HEAP_SIZE(a) _Log_heap_size(a) void _Log_heap_size(const char *msg) { - register uint32_t *sp asm("a1"); - int freestack = 4 * (sp - g_pcont->stack); + register uint32_t *sp asm("a1"); + int freestack = 4 * (sp - g_pcont->stack); Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n", msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(), freestack, ESP.getFreeContStack()); @@ -98,60 +98,60 @@ make_stack_thunk_light(br_ssl_engine_sendrec_buf); // unless the Thunk was initialized. Thanks to AES128 GCM, we can keep // symetric processing on the stack void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvapp_ack(cc, len); - } else { - return br_ssl_engine_recvapp_ack(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_ack(cc, len); + } else { + return br_ssl_engine_recvapp_ack(cc, len); + } } unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvapp_buf(cc, len); - } else { - return br_ssl_engine_recvapp_buf(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_buf(cc, len); + } else { + return br_ssl_engine_recvapp_buf(cc, len); + } } void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvrec_ack(cc, len); - } else { - return br_ssl_engine_recvrec_ack(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_ack(cc, len); + } else { + return br_ssl_engine_recvrec_ack(cc, len); + } } unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvrec_buf(cc, len); - } else { - return br_ssl_engine_recvrec_buf(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_buf(cc, len); + } else { + return br_ssl_engine_recvrec_buf(cc, len); + } } void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendapp_ack(cc, len); - } else { - return br_ssl_engine_sendapp_ack(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_ack(cc, len); + } else { + return br_ssl_engine_sendapp_ack(cc, len); + } } unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendapp_buf(cc, len); - } else { - return br_ssl_engine_sendapp_buf(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_buf(cc, len); + } else { + return br_ssl_engine_sendapp_buf(cc, len); + } } void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendrec_ack(cc, len); - } else { - return br_ssl_engine_sendrec_ack(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_ack(cc, len); + } else { + return br_ssl_engine_sendrec_ack(cc, len); + } } unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendrec_buf(cc, len); - } else { - return br_ssl_engine_sendrec_buf(cc, len); - } + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_buf(cc, len); + } else { + return br_ssl_engine_sendrec_buf(cc, len); + } } // Use min_ instead of original thunk_ @@ -176,7 +176,7 @@ namespace BearSSL { void WiFiClientSecure_light::_clear() { // TLS handshake may take more than the 5 second default timeout - _timeout = 10000; // 10 seconds max, it should never go over 6 seconds + _timeout = 10000; // 10 seconds max, it should never go over 6 seconds _sc = nullptr; _ctx_present = false; @@ -185,17 +185,17 @@ void WiFiClientSecure_light::_clear() { _iobuf_out = nullptr; setBufferSizes(1024, 1024); // reasonable minimum _handshake_done = false; - _last_error = 0; + _last_error = 0; _recvapp_buf = nullptr; _recvapp_len = 0; - _fingerprint_any = true; // by default accept all fingerprints - _fingerprint1 = nullptr; - _fingerprint2 = nullptr; - _chain_P = nullptr; - _sk_ec_P = nullptr; - _ta_P = nullptr; + _fingerprint_any = true; // by default accept all fingerprints + _fingerprint1 = nullptr; + _fingerprint2 = nullptr; + _chain_P = nullptr; + _sk_ec_P = nullptr; + _ta_P = nullptr; _ta_size = 0; - _max_thunkstack_use = 0; + _max_thunkstack_use = 0; } // Constructor @@ -221,24 +221,24 @@ WiFiClientSecure_light::~WiFiClientSecure_light() { void WiFiClientSecure_light::allocateBuffers(void) { // We prefer to allocate all buffers at start, rather than lazy allocation and deallocation // in the long run it avoids heap fragmentation and improves stability - LOG_HEAP_SIZE("allocateBuffers before"); + LOG_HEAP_SIZE("allocateBuffers before"); _sc = std::make_shared(); - LOG_HEAP_SIZE("allocateBuffers ClientContext"); + LOG_HEAP_SIZE("allocateBuffers ClientContext"); _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); - LOG_HEAP_SIZE("allocateBuffers after"); + LOG_HEAP_SIZE("allocateBuffers after"); } void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, - unsigned allowed_usages, unsigned cert_issuer_key_type) { - _chain_P = cert; - _sk_ec_P = sk; + unsigned allowed_usages, unsigned cert_issuer_key_type) { + _chain_P = cert; + _sk_ec_P = sk; _allowed_usages = allowed_usages; _cert_issuer_key_type = cert_issuer_key_type; } void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta, size_t ta_size) { - _ta_P = ta; + _ta_P = ta; _ta_size = ta_size; } @@ -271,9 +271,9 @@ bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) { int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) { DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port); - clearLastError(); + clearLastError(); if (!WiFiClient::connect(ip, port)) { - setLastError(ERR_TCP_CONNECT); + setLastError(ERR_TCP_CONNECT); return 0; } return _connectSSL(nullptr); @@ -282,19 +282,19 @@ int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) { int WiFiClientSecure_light::connect(const char* name, uint16_t port) { DEBUG_BSSL("connect(%s,%d)\n", name, port); IPAddress remote_addr; - clearLastError(); + clearLastError(); if (!WiFi.hostByName(name, remote_addr)) { DEBUG_BSSL("connect: Name loopup failure\n"); - setLastError(ERR_CANT_RESOLVE_IP); + setLastError(ERR_CANT_RESOLVE_IP); return 0; } DEBUG_BSSL("connect(%s,%d)\n", remote_addr.toString().c_str(), port); if (!WiFiClient::connect(remote_addr, port)) { DEBUG_BSSL("connect: Unable to connect TCP socket\n"); - _last_error = ERR_TCP_CONNECT; + _last_error = ERR_TCP_CONNECT; return 0; } - LOG_HEAP_SIZE("Before calling _connectSSL"); + LOG_HEAP_SIZE("Before calling _connectSSL"); return _connectSSL(name); } @@ -355,7 +355,7 @@ size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem } } while (size); - LOG_HEAP_SIZE("_write"); + LOG_HEAP_SIZE("_write"); return sent_bytes; } @@ -399,7 +399,7 @@ int WiFiClientSecure_light::read(uint8_t *buf, size_t size) { int avail = available(); bool conn = connected(); if (!avail && conn) { - return 0; // We're still connected, but nothing to read + return 0; // We're still connected, but nothing to read } if (!avail && !conn) { DEBUG_BSSL("read: Not connected, none left available\n"); @@ -434,7 +434,7 @@ int WiFiClientSecure_light::read() { int WiFiClientSecure_light::available() { if (_recvapp_buf) { - return _recvapp_len; // Anything from last call? + return _recvapp_len; // Anything from last call? } _recvapp_buf = nullptr; _recvapp_len = 0; @@ -443,7 +443,7 @@ int WiFiClientSecure_light::available() { } int st = br_ssl_engine_current_state(_eng); if (st == BR_SSL_CLOSED) { - return 0; // Nothing leftover, SSL is closed + return 0; // Nothing leftover, SSL is closed } if (st & BR_SSL_RECVAPP) { _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len); @@ -620,24 +620,24 @@ static uint8_t htoi (unsigned char c) extern "C" { - // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c - void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) { - unsigned char * pin = in; - static const char * hex = "0123456789ABCDEF"; - char * pout = out; - for(; pin < in+insz; pout +=3, pin++){ - pout[0] = hex[(*pin>>4) & 0xF]; - pout[1] = hex[ *pin & 0xF]; - pout[2] = ':'; - if (pout + 3 - out > outsz){ - /* Better to truncate output string than overflow buffer */ - /* it would be still better to either return a status */ - /* or ensure the target buffer is large enough and it never happen */ - break; - } - } - pout[-1] = 0; - } + // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c + void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) { + unsigned char * pin = in; + static const char * hex = "0123456789ABCDEF"; + char * pout = out; + for(; pin < in+insz; pout +=3, pin++){ + pout[0] = hex[(*pin>>4) & 0xF]; + pout[1] = hex[ *pin & 0xF]; + pout[2] = ':'; + if (pout + 3 - out > outsz){ + /* Better to truncate output string than overflow buffer */ + /* it would be still better to either return a status */ + /* or ensure the target buffer is large enough and it never happen */ + break; + } + } + pout[-1] = 0; + } // BearSSL doesn't define a true insecure decoder, so we make one ourselves @@ -648,12 +648,12 @@ extern "C" { // Private x509 decoder state struct br_x509_pubkeyfingerprint_context { const br_x509_class *vtable; - bool done_cert; // did we parse the first cert already? - bool fingerprint_all; - uint8_t *pubkey_recv_fingerprint; + bool done_cert; // did we parse the first cert already? + bool fingerprint_all; + uint8_t *pubkey_recv_fingerprint; const uint8_t *fingerprint1; const uint8_t *fingerprint2; - unsigned usages; // pubkey usage + unsigned usages; // pubkey usage br_x509_decoder_context ctx; // defined in BearSSL }; @@ -662,15 +662,15 @@ extern "C" { br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; // Don't process anything but the first certificate in the chain if (!xc->done_cert) { - br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr); - } - (void)server_name; // ignore server name + br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr); + } + (void)server_name; // ignore server name } // Callback for each certificate present in the chain (but only operates // on the first one by design). static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) { - (void) ctx; // do nothing + (void) ctx; // do nothing (void) length; } @@ -686,7 +686,7 @@ extern "C" { // Callback on individual cert end. static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) { br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; - xc->done_cert = true; // first cert already processed + xc->done_cert = true; // first cert already processed } // **** Start patch Castellucci @@ -743,18 +743,18 @@ extern "C" { pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa); br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint - if (!xc->fingerprint_all) { - if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) { - return 0; - } - if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint2, 20)) { - return 0; - } - return 1; // no match, error - } else { - // Default (no validation at all) or no errors in prior checks = success. - return 0; - } + if (!xc->fingerprint_all) { + if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) { + return 0; + } + if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint2, 20)) { + return 0; + } + return 1; // no match, error + } else { + // Default (no validation at all) or no errors in prior checks = success. + return 0; + } */ // set fingerprint status byte to zero // FIXME: find a better way to pass this information @@ -796,7 +796,7 @@ extern "C" { xc->pubkey_recv_fingerprint[20] |= 2; // mark for update } if (!xc->pubkey_recv_fingerprint[20]) { - return 1; // not marked for update because no match, error + return 1; // not marked for update because no match, error } // the old fingerprint format matched, recompute new one for update @@ -822,9 +822,9 @@ extern "C" { // Set up the x509 insecure data structures for BearSSL core to use. void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx, - const uint8_t *fingerprint1, const uint8_t *fingerprint2, - uint8_t *recv_fingerprint, - bool fingerprint_all) { + const uint8_t *fingerprint1, const uint8_t *fingerprint2, + uint8_t *recv_fingerprint, + bool fingerprint_all) { static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = { sizeof(br_x509_pubkeyfingerprint_context), pubkeyfingerprint_start_chain, @@ -838,26 +838,26 @@ extern "C" { memset(ctx, 0, sizeof * ctx); ctx->vtable = &br_x509_pubkeyfingerprint_vtable; ctx->done_cert = false; - ctx->fingerprint1 = fingerprint1; - ctx->fingerprint2 = fingerprint2; - ctx->pubkey_recv_fingerprint = recv_fingerprint; - ctx->fingerprint_all = fingerprint_all; + ctx->fingerprint1 = fingerprint1; + ctx->fingerprint2 = fingerprint2; + ctx->pubkey_recv_fingerprint = recv_fingerprint; + ctx->fingerprint_all = fingerprint_all; } - // We limit to a single cipher to reduce footprint + // We limit to a single cipher to reduce footprint // we reference it, don't put in PROGMEM static const uint16_t suites[] = { #ifdef USE_MQTT_TLS_FORCE_EC_CIPHER - BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 #else - BR_TLS_RSA_WITH_AES_128_GCM_SHA256 + BR_TLS_RSA_WITH_AES_128_GCM_SHA256 #endif }; // Default initializion for our SSL clients static void br_ssl_client_base_init(br_ssl_client_context *cc) { br_ssl_client_zero(cc); - // forbid SSL renegociation, as we free the Private Key after handshake + // forbid SSL renegotiation, as we free the Private Key after handshake br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12); @@ -869,14 +869,14 @@ extern "C" { br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable); br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); - // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways) - br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable); - br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable); - br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32); + // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways) + br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable); + br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable); + br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32); #ifdef USE_MQTT_TLS_FORCE_EC_CIPHER - // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced - br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO + // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced + br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO #endif static const char * alpn_mqtt = "mqtt"; br_ssl_engine_set_protocol_names(&cc->eng, &alpn_mqtt, 1); @@ -886,110 +886,110 @@ extern "C" { // Called by connect() to do the actual SSL setup and handshake. // Returns if the SSL handshake succeeded. bool WiFiClientSecure_light::_connectSSL(const char* hostName) { - // Validation context, either full CA validation or checking only fingerprints + // Validation context, either full CA validation or checking only fingerprints #ifdef USE_MQTT_TLS_CA_CERT - br_x509_minimal_context *x509_minimal; + br_x509_minimal_context *x509_minimal; #else br_x509_pubkeyfingerprint_context *x509_insecure; #endif - LOG_HEAP_SIZE("_connectSSL.start"); + LOG_HEAP_SIZE("_connectSSL.start"); - do { // used to exit on Out of Memory error and keep all cleanup code at the same place - // ============================================================ - // allocate Thunk stack, move to alternate stack and initialize - stack_thunk_light_add_ref(); - LOG_HEAP_SIZE("Thunk allocated"); - DEBUG_BSSL("_connectSSL: start connection\n"); - _freeSSL(); - clearLastError(); - if (!stack_thunk_light_get_stack_bot()) break; + do { // used to exit on Out of Memory error and keep all cleanup code at the same place + // ============================================================ + // allocate Thunk stack, move to alternate stack and initialize + stack_thunk_light_add_ref(); + LOG_HEAP_SIZE("Thunk allocated"); + DEBUG_BSSL("_connectSSL: start connection\n"); + _freeSSL(); + clearLastError(); + if (!stack_thunk_light_get_stack_bot()) break; - _ctx_present = true; - _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr + _ctx_present = true; + _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - br_ssl_client_base_init(_sc.get()); + br_ssl_client_base_init(_sc.get()); - // ============================================================ - // Allocatte and initialize Decoder Context - LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation"); - // Only failure possible in the installation is OOM - #ifdef USE_MQTT_TLS_CA_CERT - x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context)); - if (!x509_minimal) break; - br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, _ta_size); - br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng)); - br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable); - br_ssl_engine_set_x509(_eng, &x509_minimal->vtable); + // ============================================================ + // Allocatte and initialize Decoder Context + LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation"); + // Only failure possible in the installation is OOM + #ifdef USE_MQTT_TLS_CA_CERT + x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context)); + if (!x509_minimal) break; + br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, _ta_size); + br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng)); + br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_x509(_eng, &x509_minimal->vtable); uint32_t now = UtcTime(); uint32_t cfg_time = CfgTime(); if (cfg_time > now) { now = cfg_time; } br_x509_minimal_set_time(x509_minimal, now / 86400 + 719528, now % 86400); - #else - x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context)); - //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context); - if (!x509_insecure) break; - br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any); - br_ssl_engine_set_x509(_eng, &x509_insecure->vtable); - #endif - LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation"); + #else + x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context)); + //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context); + if (!x509_insecure) break; + br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any); + br_ssl_engine_set_x509(_eng, &x509_insecure->vtable); + #endif + LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation"); - // ============================================================ - // Set send/receive buffers - br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + // ============================================================ + // Set send/receive buffers + br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); - // ============================================================ - // allocate Private key if needed, only if USE_MQTT_AWS_IOT - LOG_HEAP_SIZE("_connectSSL before PrivKey allocation"); - #ifdef USE_MQTT_AWS_IOT - // ============================================================ - // Set the EC Private Key, only USE_MQTT_AWS_IOT - // limited to P256 curve - br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1, - _sk_ec_P, _allowed_usages, - _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default()); - #endif // USE_MQTT_AWS_IOT + // ============================================================ + // allocate Private key if needed, only if USE_MQTT_AWS_IOT + LOG_HEAP_SIZE("_connectSSL before PrivKey allocation"); + #ifdef USE_MQTT_AWS_IOT + // ============================================================ + // Set the EC Private Key, only USE_MQTT_AWS_IOT + // limited to P256 curve + br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1, + _sk_ec_P, _allowed_usages, + _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default()); + #endif // USE_MQTT_AWS_IOT - // ============================================================ - // Start TLS connection, ALL - if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break; + // ============================================================ + // Start TLS connection, ALL + if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break; - auto ret = _wait_for_handshake(); - #ifdef DEBUG_ESP_SSL - if (!ret) { - DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError()); - } else { - DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus()); - } - #endif - LOG_HEAP_SIZE("_connectSSL.end"); - _max_thunkstack_use = stack_thunk_light_get_max_usage(); - stack_thunk_light_del_ref(); - //stack_thunk_light_repaint(); - LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk"); + auto ret = _wait_for_handshake(); + #ifdef DEBUG_ESP_SSL + if (!ret) { + DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError()); + } else { + DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus()); + } + #endif + LOG_HEAP_SIZE("_connectSSL.end"); + _max_thunkstack_use = stack_thunk_light_get_max_usage(); + stack_thunk_light_del_ref(); + //stack_thunk_light_repaint(); + LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk"); - #ifdef USE_MQTT_TLS_CA_CERT - free(x509_minimal); - #else - free(x509_insecure); - #endif - LOG_HEAP_SIZE("_connectSSL after release of Priv Key"); - return ret; - } while (0); + #ifdef USE_MQTT_TLS_CA_CERT + free(x509_minimal); + #else + free(x509_insecure); + #endif + LOG_HEAP_SIZE("_connectSSL after release of Priv Key"); + return ret; + } while (0); - // ============================================================ - // if we arrived here, this means we had an OOM error, cleaning up - setLastError(ERR_OOM); - DEBUG_BSSL("_connectSSL: Out of memory\n"); - stack_thunk_light_del_ref(); + // ============================================================ + // if we arrived here, this means we had an OOM error, cleaning up + setLastError(ERR_OOM); + DEBUG_BSSL("_connectSSL: Out of memory\n"); + stack_thunk_light_del_ref(); #ifdef USE_MQTT_TLS_CA_CERT - free(x509_minimal); + free(x509_minimal); #else - free(x509_insecure); + free(x509_insecure); #endif - LOG_HEAP_SIZE("_connectSSL clean_on_error"); - return false; + LOG_HEAP_SIZE("_connectSSL clean_on_error"); + return false; } }; diff --git a/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h b/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h index 91e1fab76..9138c4e8a 100644 --- a/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h +++ b/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h @@ -2,38 +2,39 @@ // compressed by tools/unishox/compress-html-uncompressed.py ///////////////////////////////////////////////////////////////////// -const size_t HTTP_SCRIPT_CONSOL_SIZE = 853; -const char HTTP_SCRIPT_CONSOL_COMPRESSED[] PROGMEM = "\x33\xBF\xAF\x71\xF0\xE3\x3A\x8B\x44\x3E\x1C\x67\x82\x30\x2F\x83\xAD\xCE\x41\x1D" - "\xD1\x87\x78\xF6\x99\xDF\xD0\x67\x56\x1F\x0F\xB3\xEC\xEA\xA3\xC0\x61\x3B\xF9\x56" - "\x8D\x78\x2E\x8E\xE8\x54\x77\x8F\x14\x7C\x63\x8E\xE9\xF7\x47\x21\xF6\x77\x8F\x05" - "\xA6\x0E\xE8\xC3\xE1\xF0\xE4\x3B\xC7\xB4\x83\x3E\x31\xC7\x74\xFB\x0C\xE4\x3E\xCE" - "\xF1\xE0\xB0\xF8\x7D\x9F\xA0\xCE\x43\xE1\xF6\x76\xC9\xF0\x78\x23\x21\x65\xF2\xD2" - "\x0F\x06\x8C\xCE\x7D\x47\x74\x33\xA1\x9D\x84\x2D\x9D\xE3\xC0\x21\x45\x3E\x1F\x67" - "\xD9\xE2\x8E\x9E\x0F\xF8\x10\x45\x58\x30\xF8\x71\x11\xBD\x2A\x01\xF1\xEE\x3E\x02" - "\x35\x13\xC1\xEE\xD3\x07\x74\x11\xA6\x1F\x87\xCF\x71\xDE\x3D\xBA\x60\xEE\x9B\x0F" - "\xE1\xF3\x85\x84\x11\xDE\x3D\xA6\xC3\xA5\x8E\xCF\xD1\xDD\x3B\xC7\x83\xDC\x6C\x3E" - "\x73\x1F\x44\x6C\x21\xA4\x11\x0A\xAA\x18\x5F\x66\xA1\x6F\xD4\x77\x4E\xF1\xE0\xD8" - "\x74\xCE\xFB\xB1\x0C\xBD\x57\x4C\x31\x57\xC3\xCC\xF8\x08\x7C\x28\x1D\xD0\x41\xCA" - "\x8E\x9F\x76\x21\x91\x7A\xAE\x99\xF0\xF8\x73\x0F\xD1\xFA\x23\x61\xD3\xD5\x74\x2F" - "\xC7\xC3\xE1\xCA\x6C\x81\x07\x87\x03\x69\xD4\x21\xE0\x43\xE1\xB0\xE9\xF7\xE1\x99" - "\xDE\x65\x4C\xD9\x47\x4F\x0C\x0B\x68\xEE\x9D\x87\xB8\xE4\x3B\x0E\xF1\xE0\xB4\x43" - "\xE0\x87\x4F\x0A\xD3\x14\x77\x4E\xF1\xE3\x4C\x1D\xD0\x44\x92\x7C\x3E\x1C\x67\x78" - "\xF6\x95\x02\x2F\x0A\x27\xB8\xDA\x09\x38\x29\xB4\xE8\x13\xE1\xEA\x14\x7E\x02\x2E" - "\x06\x76\xCF\x86\xD3\xC1\xEE\x05\xDE\x1E\x4F\x71\xE0\xD8\x74\xC1\x8F\x8E\xE9\xF6" - "\x43\xC4\xCA\x8F\xB3\xA8\xFB\x0F\xC7\x68\x33\x94\x7C\x3E\xCE\xD9\x68\x87\x6F\x0E" - "\xAA\xF8\xB6\x77\x8F\x06\xC3\xA7\x9F\x08\x77\x4E\xF1\xE0\xF7\x05\x47\xCF\x3A\x04" - "\x4E\x4A\x4E\xA3\xE8\x43\xBC\x78\xFB\xA1\x7F\xE4\x62\xC2\xF3\x3C\x1E\xE1\xF0\x8E" - "\xE8\x47\x78\xF0\x67\x7F\x42\x83\x3E\x1E\xF1\xEF\x9D\x41\xF0\x23\xF2\xF4\x28\xEE" - "\x9D\xE3\xDA\x08\x7C\xA2\x9D\x2C\x41\x09\x99\xBE\xA2\x0B\x7D\x4F\x9F\xCE\xE9\xF6" - "\x68\xCC\x84\xC1\xFE\x3E\xCE\xA0\x44\xE2\xDD\x82\x0F\x12\xA3\x81\x13\x97\xB3\xA8" - "\x33\xE3\x3A\x1A\x33\x22\x0F\x04\x67\x8D\x30\x77\x4E\x5F\xCF\x87\xC2\x0C\xFF\x1F" - "\xE3\x98\xCF\x87\xC2\x0C\xEF\x1E\xD1\xC7\x4B\x17\x58\x1E\x0D\x18\x13\xA6\x7C\x3E" - "\xF0\xC1\x83\xEC\xF0\x7B\x8E\x5F\xCF\x87\xC2\x0C\xED\x1D\xD3\xB6\x76\xC3\xE3\xF0" - "\x50\x60\x85\xC4\x31\xFA\x3F\x47\x74\x3E\x3E\x02\x24\xB3\xBC\x75\x0E\x04\x2E\x2E" - "\x85\x06\x7B\xC1\xF1\xD6\x72\x1E\xF9\xFE\x3F\xC7\xD9\xF6\x77\x8F\x3C\x67\xC3\xE1" - "\x06\x76\x8E\xE9\xC6\x7E\x1D\x67\x59\x07\xC0\x83\x88\x1C\x64\x0A\x78\x41\xC9\x67" - "\xC3\xE1\x06\x7E\x8F\xD1\xDD\x04\x4C\xC4\xFC\x39\x11\xFA\x3F\x44\x28\x33\xA0\xCC" - "\x18\x77\x4E\xF1\xD4\x28\x33\xA0\xBE\x04\x1E\x44\x01\x0B\x1C\x3B\xC7\x50\x7C\x7C" - "\x38\xCE\xF1\xEE\x3B\xC7\x83\xDC\x43\xE1\x1D\xD1\x47\x78\xF0"; +const size_t HTTP_SCRIPT_CONSOL_SIZE = 913; +const char HTTP_SCRIPT_CONSOL_COMPRESSED[] PROGMEM = "\x3D\xA1\x3A\x5E\xE3\xE1\xC6\x75\x16\x88\x7C\x38\xCE\xA2\x31\x47\x83\x02\xF8\x3A" + "\xDC\xE4\x11\xDD\x18\x77\x8F\x68\x4E\x90\x67\x56\x1F\x0F\xB3\xEC\xF0\x18\x4E\xFE" + "\x55\xA3\x5E\x0B\xA3\xBA\x15\x1D\xE0\x49\x9A\x62\x8E\xF1\xE2\x8F\x8C\x71\xDD\x3E" + "\xE8\xE4\x3E\xCE\xF1\xE0\xB4\xC1\xDD\x18\x7C\x3E\x1C\x87\x78\xF6\x90\x67\xC6\x38" + "\xEE\x9F\x61\x9C\x87\xD9\xDE\x3C\x16\x1F\x0F\xB3\xF4\x19\xC8\x7C\x3E\xCE\xD9\x3E" + "\x0F\x04\x64\x2C\xBE\x5A\x41\xE0\xD1\x99\xCF\xA8\xEE\x86\x74\x33\xB0\x85\xB3\xBC" + "\x78\x04\x28\xA7\xC3\xEC\xFB\x3C\x51\xD3\xC1\xFF\x02\x08\xAB\x06\x1F\x0E\x22\x37" + "\xA0\x1E\x3D\xC7\xC0\x46\xA2\x78\x3D\xDA\x60\xEE\x82\x34\xC3\xF0\xF9\xEE\x3B\xC7" + "\xB7\x4C\x1D\xD3\x61\xFC\x3E\x70\xB0\x82\x3B\xC7\xB4\xD8\x74\xB1\xD9\xFA\x3B\xA7" + "\x78\xF0\x7B\x8D\x87\xCE\x63\xE8\x8D\x84\x34\x82\x21\x55\x43\x0B\xEC\xD4\x2D\xFA" + "\x8E\xE9\xDE\x3C\x1B\x0E\x99\xDF\x76\x21\x97\xAA\xE9\x86\x2A\x2B\xF8\x79\x9F\x01" + "\x0F\x85\xF3\xBA\x08\x39\x51\xD3\xEE\xC4\x32\x2F\x55\xD3\x3E\x1F\x0E\x61\xFA\x3F" + "\x44\x6C\x3A\x7A\xAE\x85\xF8\xF8\x7C\x39\x4D\x90\x20\xF0\xFE\x6D\x3A\x84\x3C\x08" + "\x7C\x36\x1D\x3E\xFC\x33\x3B\xCC\xA9\x9B\x28\xE9\xE1\x81\x6D\x1D\xD3\xB0\xF7\x1C" + "\x87\x61\xDE\x3C\x16\x88\x7C\x10\xE9\xE1\x5A\x62\x8E\xE9\xDE\x3C\x69\x83\xBA\x08" + "\x92\x4F\x87\xC3\x8C\xEF\x1E\xD2\xA0\x45\xE1\x44\xF7\x1B\x41\x27\x05\x36\x9D\x02" + "\x7C\x3D\x42\x8F\xC0\x45\xC0\xCE\xD9\xF0\xDA\x78\x3D\xC0\xB9\xC3\xC8\x26\x71\xF4" + "\x15\x1F\x3C\xE8\x11\x39\x1D\x3A\x8F\xA1\x0E\xF1\xE0\xF7\x1E\xE3\xC1\xB0\xE9\x83" + "\x1F\x1D\xD3\xEC\x87\x89\x95\x1F\x67\x51\xF6\x1F\x8E\xD0\x67\x28\xF8\x7D\x9D\xB2" + "\xD1\x0E\xDE\x1D\x55\xF1\x6C\xEF\x1E\x0D\x87\x4F\x3E\x10\xEE\x9D\xE3\xC1\x80\x4A" + "\xC7\x4E\x53\x6D\x9D\xE3\xC1\xEE\x2F\xBA\x17\xFE\x46\x2C\x2F\x33\xC1\xEE\x1F\x08" + "\xEE\x84\x77\x8F\x01\x3A\x42\x83\x3E\x1E\xF1\xEF\x9D\x41\xF1\xF0\xE3\x20\x45\xE6" + "\xC4\x51\xDD\x3B\xC7\xB4\x10\xF9\x6D\x3A\x58\x82\x13\x33\x7D\x44\x16\xFA\x9F\x3F" + "\x9D\xD3\xEC\xD1\x99\x09\x83\xFC\x7D\x9D\x40\x89\xC6\xFB\x04\x1E\x2F\x47\x02\x27" + "\x34\x67\x50\x67\xC6\x74\x34\x66\x44\x1E\x08\xCF\x1A\x60\xEE\x9C\xBF\x9F\x0F\x84" + "\x19\xFE\x3F\xC7\x31\x9F\x0F\x84\x19\xDE\x3D\xA3\x8E\x96\x2E\xB0\x3C\x1A\x30\x27" + "\x4C\xF8\x7D\xE1\x83\x07\xD9\xE0\xF7\x1C\xBF\x9F\x0F\x84\x19\xDA\x3B\xA7\x6C\xED" + "\x87\xC7\xE0\xA0\xC1\x0B\x8A\xE3\xF4\x7E\x8E\xE8\x7C\x7C\x04\x49\x67\x78\xEA\x1C" + "\x08\x5C\x71\x0A\x0C\xF7\x83\xE3\xAC\xE4\x3D\xF3\xFC\x7F\x8F\xB3\xEC\xEF\x1E\x78" + "\xCF\x87\xC2\x0C\xED\x1D\xD3\x8C\xFC\x3A\xCE\xB2\x0F\x81\x07\x10\x38\xC8\x14\xF0" + "\x83\x92\xCF\x87\xC2\x0C\xFD\x1F\xA3\xBA\x08\x99\x89\xF8\x72\x23\xF4\x7E\x88\x50" + "\x67\x41\x98\x30\xEE\x9D\xE3\xA8\x50\x67\x41\x7C\x08\x3C\x9C\x02\x16\x38\x77\x8E" + "\xA0\xF8\xF8\x71\x9D\xE3\xDC\x77\x8F\x07\xB8\x87\xC2\x3B\xA2\x8E\xF1\xE0\xF7\x1B"; #define HTTP_SCRIPT_CONSOL Decompress(HTTP_SCRIPT_CONSOL_COMPRESSED,HTTP_SCRIPT_CONSOL_SIZE).c_str() \ No newline at end of file diff --git a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h index 2f61ce116..d7a530b43 100644 --- a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h +++ b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h @@ -2,26 +2,26 @@ // compressed by tools/unishox/compress-html-uncompressed.py ///////////////////////////////////////////////////////////////////// -const size_t HTTP_SCRIPT_ROOT_SIZE = 524; -const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x41\x59\xDD\x18\x77\x8F\x69\x9D\xFD\x59\xF0\xFB\x3E\xCF\x1A" - "\x60\xEE\x85\x67\x4B\xF8\xF0\xB1\xAF\xAB\xC7\x40\x9F\x0F\x50\xA3\xE1\xF0\xE4\x3B" - "\xC7\xB4\xAC\xF8\x30\xF0\x18\x4E\xFE\x55\xA3\x5E\x0B\xA3\xBA\x15\x1D\xE3\xC1\xEE" - "\xD3\x07\x74\xD8\x7F\x0F\x9C\x2C\x20\x8E\xF1\xED\x36\x1D\x2C\x76\x7E\x8E\xE9\xDE" - "\x3C\x1E\xE3\x61\xF3\x98\xFA\x23\x61\x0D\x20\x88\x55\x50\xC2\xFB\x35\x0B\x7E\xA3" - "\xBA\x77\x8F\x06\xC3\xA6\x77\xDD\x88\x65\xEA\xBA\x61\x8A\xBE\x1E\x67\xC0\x43\xDA" - "\x0E\xE9\xDE\x3D\xBA\x60\xEE\x9B\x0E\x9F\x76\x21\x91\x7A\xAE\x99\xF0\xF8\x73\x0F" - "\xD1\xFA\x23\x61\xD3\xD5\x74\x2F\xC7\xC3\xE1\xCA\x6C\x81\x07\x80\x7F\x1F\x0D\x87" - "\x4F\xBF\x0C\xCE\xF3\x2A\x2B\x66\xCA\x3A\x7D\x8C\x0A\xC3\x67\x74\xEC\x3D\xB4\x7B" - "\x8E\xC1\xE3\xA8\xF6\x1E\x95\x63\x82\x6B\xD4\x64\x13\x3E\x1F\x63\xFA\x25\x0A\x3C" - "\xCE\x46\xCF\xA3\xE8\xFB\x3F\x0F\x61\xDE\x20\x46\xC2\xBC\x08\x58\x57\xCF\xC3\xD2" - "\x85\x02\x4D\x71\xA0\x83\x5C\xEC\xA1\x47\xE1\xE9\x42\x02\x4E\x4E\x72\x99\x0C\x36" - "\x1E\x07\xC5\x6D\x33\xAF\xC3\x2C\x36\x79\xF6\x0F\xFE\xC6\x02\x56\x72\xC1\x0F\x1E" - "\x10\xFC\x3D\x0E\xCA\xF8\x24\xD9\x0C\xF7\x1D\x83\xC7\x51\xEC\x3E\x8F\xA3\xEC\xFC" - "\x3D\x04\xD3\x30\x43\xCE\xE9\x9B\x28\xEB\xB0\xB4\x7B\x8F\x30\xDF\x53\xF9\xE0\xC6" - "\x75\x1D\x63\xEF\x47\x85\x51\xE6\x7B\x0E\xF1\xE1\x8E\x3B\xA7\xD8\x47\x21\xF6\x77" - "\x8E\x85\xBD\xCF\xE4\x28\xA8\x86\x90\x47\xCF\x1E\x0F\x71\xEE\x3C\x1B\x0E\x98\x31" - "\xF1\xDD\x3E\xC8\x78\x99\x51\xF6\x75\x1F\x67\x43\xB4\x34\xF8\x72\x1F\x67\x6C\xAC" - "\xEA\xAF\x8B\x67\x78\xF0\x6C\x3A\x79\xF0\x87\x74\xEF\x1E\x02\xA3\xE7\x9D\x02\x27" - "\x23\x96\x75\x1F\x42\x1D\xE3\xC1\xEE"; +const size_t HTTP_SCRIPT_ROOT_SIZE = 574; +const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x3D\xA1\x3A\x46\x28\xF0\x60\x5F\x07\x5B\x9C\x82\xB3\xBA\x30\xEF\x1E\xDB\x3E\x0C" + "\x3F\xC7\xF8\xFB\x3E\xCF\x01\x84\xEF\xE5\x5A\x35\xE0\xBA\x3B\xA6\x28\xEF\x02\x4C" + "\xD0\xA8\xEF\x1E\x34\xC1\xDD\x36\x1F\xC3\xE7\x0B\x08\x23\xBC\x7B\x4D\x87\x4B\x1D" + "\x9F\xA3\xBA\x77\x8F\x71\xB0\xF9\xCC\x7D\x11\xB0\x86\x90\x44\x2A\x2B\xA8\x61\x7D" + "\x9A\x85\xBF\x51\xDD\x3B\xC7\x83\x61\xD3\x3B\xEE\xC4\x32\xF5\x5D\x30\xC5\x5F\x0F" + "\x33\xE0\x21\xEA\xE7\x74\xEF\x1E\xDD\x30\x77\x4D\x87\x4F\xBB\x10\xC8\xBD\x57\x4C" + "\xF8\x7C\x39\x87\xE8\xFD\x11\xB0\xE9\xEA\xBA\x17\xE3\xE1\xF0\xE5\x36\x77\x8F\x69" + "\x19\xDF\xD7\x8F\x86\xC3\xA7\xDF\x86\x67\x79\x95\x33\x65\x1D\x3E\xC6\x05\x61\xB3" + "\xBA\x76\x1E\xDA\x3D\xC7\x60\xF1\xD4\x7B\x0F\x4A\xB1\xC1\x35\xEA\x32\x09\x9F\x0F" + "\xB1\xFD\x12\x85\x1E\x67\x23\x67\xD1\xF4\x7D\x9F\x87\xB0\xEF\x10\x23\x61\x5E\x04" + "\x2C\x2B\xE7\xE1\xE9\x42\x81\x26\xB8\xD0\x41\xAE\x76\x50\xA3\xF0\xF4\xA1\x01\x27" + "\x27\x39\x4C\x86\x1B\x0F\x03\xE2\xB6\x99\xD7\xE1\x96\x1B\x3C\xFB\x07\xFF\x63\x01" + "\x2B\x39\x60\x87\x8F\x08\x7E\x1E\x87\x65\x7C\x12\x6C\x86\x7B\x8E\xC1\xE3\xA8\xF6" + "\x1F\x47\xD1\xF6\x7E\x1E\x82\x69\x98\x21\xE7\x74\xCD\x94\x75\xD8\x5A\x3D\xC7\x98" + "\x6F\xA9\xFC\xF0\x63\x3A\x8E\xB1\xF7\xA3\xC2\xA8\xF3\x3D\x87\x78\xF0\xC7\x1D\xD3" + "\xEC\x23\x90\xFB\x3B\xC7\x42\xDE\xE7\xF2\x14\x54\x43\x48\x23\xE7\x81\x7B\x90\x10" + "\xA8\xF9\xE7\x40\x89\x3A\xCE\xA3\xE8\x43\xBC\x78\x3D\xC7\xB8\xF0\x6C\x3A\x60\xC7" + "\xC7\x74\xFB\x21\xE2\x65\x47\xD9\xD4\x7D\x9D\x0E\xD0\xD3\xE1\xC8\x7D\x9D\xB2\xB3" + "\xAA\xBE\x2D\x9D\xE3\xC1\xB0\xE9\xE7\xC2\x1D\xD3\xBC\x78\x30\x09\x78\xD1\xCA\x6D" + "\xB3\xBC\x78\x3D\xC7\xB8"; #define HTTP_SCRIPT_ROOT Decompress(HTTP_SCRIPT_ROOT_COMPRESSED,HTTP_SCRIPT_ROOT_SIZE).c_str() \ No newline at end of file diff --git a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h index bb52fbbcd..1481a609c 100644 --- a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h +++ b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h @@ -2,32 +2,32 @@ // compressed by tools/unishox/compress-html-uncompressed.py ///////////////////////////////////////////////////////////////////// -const size_t HTTP_SCRIPT_ROOT_SIZE = 744; -const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x33\xBF\xAF\x98\xF0\xA3\xE1\xC8\x78\x23\x02\xF8\x3A\xDC\xE4\x15\x9D\xD1\x87\x78" - "\xF6\x99\xDF\xD5\x9F\x0F\xB3\xEC\xF1\xA6\x0E\xE8\x56\x74\xBF\x8F\x0B\x1A\xFA\xBC" - "\x74\x09\xF0\xF5\x0A\x3E\x1F\x0E\x43\xBC\x7B\x4A\xCF\x83\x0F\x01\x84\xEF\xE5\x5A" - "\x35\xE0\xBA\x3B\xA1\x51\xDE\x3C\x1E\xED\x30\x77\x4D\x87\xF0\xF9\xC2\xC2\x08\xEF" - "\x1E\xD3\x61\xD2\xC7\x67\xE8\xEE\x9D\xE3\xC1\xEE\x36\x1F\x39\x8F\xA2\x36\x10\xD2" - "\x08\x85\x55\x0C\x2F\xB3\x50\xB7\xEA\x3B\xA7\x78\xF0\x6C\x3A\x67\x7D\xD8\x86\x5E" - "\xAB\xA6\x18\xAB\xE1\xE6\x7C\x04\x3D\xA0\xEE\x9D\xE3\xDB\xA6\x0E\xE9\xB0\xE9\xF7" - "\x62\x19\x17\xAA\xE9\x9F\x0F\x87\x30\xFD\x1F\xA2\x36\x1D\x3D\x57\x42\xFC\x7C\x3E" - "\x1C\xA6\xC8\x10\x78\x07\xF1\xF0\xD8\x74\xFB\xF0\xCC\xEF\x32\xA6\x6C\xA3\xA7\xD8" - "\xC0\xAC\x36\x77\x4E\xC3\xDB\x47\xB8\xEC\x1E\x3A\x8F\x61\xE9\x56\x38\x26\xBD\x46" - "\x41\x33\xE1\xF6\x3F\xA2\x50\xA3\xCC\xE4\x6C\xFA\x3E\x8F\xB3\xF0\xF6\x1D\xE2\x04" - "\x6C\x2B\xC0\x85\x85\x7C\xFC\x3D\x28\x50\x24\xD7\x1A\x08\x35\xCE\xCA\x14\x7E\x1E" - "\x94\x20\x24\xE4\xE7\x29\x90\xC3\x61\xE0\x7C\x56\xD3\x3A\xFC\x32\xC3\x67\x9F\x60" - "\xFF\xEC\x60\x25\x67\x2C\x10\xF1\xE1\x0F\xC3\xD0\xEC\xAF\x82\x4D\x90\xCF\x71\xD8" - "\x3C\x75\x1E\xC3\xE8\xFA\x3E\xCF\xC3\xD0\x4D\x33\x04\x3C\xEE\x99\xB2\x8E\xBB\x0B" - "\x47\xB8\xF3\x0D\xF5\x3F\x9E\x0C\x67\x51\xD6\x3E\xF4\x78\x55\x1E\x67\xB0\xEF\x1E" - "\x18\xE3\xBA\x7D\x84\x72\x1F\x67\x78\xE8\x5B\xDC\xFE\x42\x8A\x88\x69\x04\x7C\xF1" - "\xE0\xF7\x1E\xE3\xC6\x98\x47\x77\xE6\x3C\x28\xEF\x23\xDA\x6C\x3A\x60\xC7\xC7\x74" - "\xFB\x21\xE2\x65\x47\xD9\xD4\x7D\x9D\x0E\xD0\xD3\xE1\xC8\x7D\x9D\xB2\xB3\xAA\xBE" - "\x2D\x9D\xE3\xC1\xB0\xE9\xE7\xC2\x1D\xD3\xBC\x78\x0A\x8F\x9E\x74\x08\x9C\x93\xD9" - "\xD4\x7D\x08\x77\x8F\x07\xB8\xF7\x02\x27\x2E\x9E\x66\x76\x77\x46\x5F\xCE\xAD\x33" - "\xBF\x9D\xE3\xDA\x15\x9D\xD3\xEC\xFD\x78\xCC\xF8\x7D\x9D\xBD\x33\xBF\x9D\xB3\xEC" - "\xFD\x9F\x67\x6C\x65\xFC\xEF\x1E\x01\x1B\x0D\xD0\x48\xC3\x41\x0B\x9C\x40\x53\xC5" - "\x3E\x63\xC2\x8F\x87\x19\x02\x36\x36\x33\xE7\x74\xC1\xDE\x3D\xBA\x61\x1D\xD3\x07" - "\x79\x1E\xD0\x50\xDE\x81\x0B\x2F\x3D\xC9\x85\xE6\x8F\x68\x26\x73\xD0\x08\x79\x81" - "\xEE"; +const size_t HTTP_SCRIPT_ROOT_SIZE = 844; +const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x09\xD2\xF9\x8F\x0A\x3E\x1C\x87\x51\x18\xA3\xC1\x81\x7C\x1D\x6E\x72\x0A\xCE\xE8" + "\xC3\xBC\x7B\x6C\xF8\x30\xFF\x1F\xE3\xEC\xFB\x3C\x06\x13\xBF\x95\x68\xD7\x82\xE8" + "\xEE\x98\xA3\xBC\x09\x33\x42\xA3\xBC\x78\xD3\x07\x74\xD8\x7F\x0F\x9C\x2C\x20\x8E" + "\xF1\xED\x36\x1D\x2C\x76\x7E\x8E\xE9\xDE\x3C\x1E\xE3\x61\xF3\x98\xFA\x23\x61\x0D" + "\x20\x88\x55\x50\xC2\xFB\x35\x0B\x7E\xA3\xBA\x77\x8F\x06\xC3\xA6\x77\xDD\x88\x65" + "\xEA\xBA\x61\x8A\xBE\x1E\x67\xC0\x43\xD6\x0E\xE9\xDE\x3D\xBA\x60\xEE\x9B\x0E\x9F" + "\x76\x21\x91\x7A\xAE\x99\xF0\xF8\x73\x0F\xD1\xFA\x23\x61\xD3\xD5\x74\x2F\xC7\xC3" + "\xE1\xCA\x6C\xEF\x1E\xD2\x33\xBF\xAF\x1F\x0D\x87\x4F\xBF\x0C\xCE\xF3\x2A\x2B\x66" + "\xCA\x3A\x7D\x8C\x0A\xC3\x67\x74\xEC\x3D\xB4\x7B\x8E\xC1\xE3\xA8\xF6\x1E\x95\x63" + "\x82\x6B\xD4\x64\x13\x3E\x1F\x63\xFA\x25\x0A\x3C\xCE\x46\xCF\xA3\xE8\xFB\x3F\x0F" + "\x61\xDE\x20\x46\xC2\xBC\x08\x58\x57\xCF\xC3\xD2\x85\x02\x4D\x71\xA0\x83\x5C\xEC" + "\xA1\x47\xE1\xE9\x42\x02\x4E\x4E\x72\x99\x0C\x36\x1E\x07\xC5\x6D\x33\xAF\xC3\x2C" + "\x36\x79\xF6\x0F\xFE\xC6\x02\x56\x72\xC1\x0F\x1E\x10\xFC\x3D\x0E\xCA\xF8\x24\xD9" + "\x0C\xF7\x1D\x83\xC7\x51\xEC\x3E\x8F\xA3\xEC\xFC\x3D\x04\xD3\x30\x43\xCE\xE9\x9B" + "\x28\xEB\xB0\xB4\x7B\x8F\x30\xDF\x53\xF9\xE0\xC6\x75\x1D\x63\xEF\x47\x85\x51\xE6" + "\x7B\x0E\xF1\xE1\x8E\x3B\xA7\xD8\x47\x21\xF6\x77\x8E\x85\xBD\xCF\xE4\x28\xA8\x86" + "\x90\x47\xCF\x03\x01\xE4\x0B\xE6\x3C\x28\xEF\x1E\xD0\xA8\xF9\xE7\x40\x89\x5E\xCE" + "\xA3\xE8\x43\xBC\x78\x3D\xC7\xB8\xF7\x02\x2D\xE3\x61\xD3\x06\x3E\x3B\xA7\xD9\x0F" + "\x13\x2A\x2B\x3E\xCE\xA3\xEC\xE8\x76\x86\x9F\x0E\x43\xEC\xED\x95\x9D\x55\xF1\x6C" + "\xEF\x1E\x0D\x87\x4F\x3E\x10\xEE\x9D\xE3\xC1\x80\x4B\xC9\x0E\x53\x6D\x9D\xE3\xC1" + "\xEE\x3D\xC4\x08\x9C\xD3\x79\x99\xD9\xDD\x19\x7F\x3A\xB4\xCE\xFE\x77\x8F\x68\x56" + "\x77\x4F\xB3\xF5\xE3\x33\xE1\xF6\x76\xF4\xCE\xFE\x76\xCF\xB3\xF6\x7D\x9D\xB1\x97" + "\xF3\xBC\x78\x04\x6C\x37\x41\x23\x0D\x04\x1E\x7E\x4F\x2A\x01\xA7\x8A\x7C\xC7\x85" + "\x1F\x0E\x32\x04\x6C\x6C\x67\xCE\xE9\x83\xBC\x7B\x74\xC1\xDD\x30\x77\x8F\x68\x26" + "\x70\xBA\x09\x9C\x3F\x82\x87\x0C\xA0\x85\xA7\x9E\xE6\x17\x98\x2F\x64\x2A\x01\x87" + "\xAF\x9E\xE3"; #define HTTP_SCRIPT_ROOT Decompress(HTTP_SCRIPT_ROOT_COMPRESSED,HTTP_SCRIPT_ROOT_SIZE).c_str() \ No newline at end of file diff --git a/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h b/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h index 51cfb66a3..0351e828e 100644 --- a/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h +++ b/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h @@ -1,49 +1,54 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = - "var sn=0,id=0;" // Scroll position, Get most of weblog initially - "function l(p){" // Console log and command service - "var c,o='',t;" - "clearTimeout(lt);" - "t=eb('t1');" - "if(p==1){" - "c=eb('c1');" // Console command id - "o='&c1='+encodeURIComponent(c.value);" - "c.value='';" - "t.scrollTop=99999;" - "sn=t.scrollTop;" + "{" + "let sn=0,id=0,ft;" // Scroll position, Get most of weblog initially + "function l(p){" // Console log and command service + "let c,o='';" + "clearTimeout(lt);" + "clearTimeout(ft);" + "t=eb('t1');" + "if(p==1){" + "c=eb('c1');" // Console command id + "o='&c1='+encodeURIComponent(c.value);" + "c.value='';" + "t.scrollTop=99999;" + "sn=t.scrollTop;" + "}" + "if(t.scrollTop>=sn){" // User scrolled back so no updates + "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) + "x=new XMLHttpRequest();" + "x.onreadystatechange=function(){" + "if(x.readyState==4&&x.status==200){" + "let z,d;" + "d=x.responseText.split(/}1/);" // Field separator + "id=d.shift();" + "if(d.shift()==0){t.value='';}" + "z=d.shift();" + "if(z.length>0){t.value+=z;}" + "t.scrollTop=99999;" + "sn=t.scrollTop;" + "clearTimeout(ft);" + "lt=setTimeout(l,%d);" // webrefresh timer.... + "}" + "};" + "x.open('GET','cs?c2='+id+o,true);" // Related to Webserver->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp)) + "x.send();" + "ft=setTimeout(l,20000);" // fail timeout, triggered 20s after asking for XHR + "}" + "return false;" "}" - "if(t.scrollTop>=sn){" // User scrolled back so no updates - "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) - "x=new XMLHttpRequest();" - "x.onreadystatechange=function(){" - "if(x.readyState==4&&x.status==200){" - "var z,d;" - "d=x.responseText.split(/}1/);" // Field separator - "id=d.shift();" - "if(d.shift()==0){t.value='';}" - "z=d.shift();" - "if(z.length>0){t.value+=z;}" - "t.scrollTop=99999;" - "sn=t.scrollTop;" - "}" - "};" - "x.open('GET','cs?c2='+id+o,true);" // Related to Webserver->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp)) - "x.send();" - "}" - "lt=setTimeout(l,%d);" - "return false;" - "}" - "wl(l);" // Load initial console text + "wl(l);" // Load initial console text - // Console command history - "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown - "function h(){" -// "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete - "eb('c1').addEventListener('keydown',function(e){" - "var b=eb('c1'),c=e.keyCode;" // c1 = Console command id - "if(38==c||40==c){b.autocomplete='off';}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete - "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp - "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown - "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history - "});" - "}" - "wl(h);"; // Add console command key eventlistener after name has been synced with id (= wl(jd)) + // Console command history + "let hc=[],cn=0;" // hc = History commands, cn = Number of history being shown + "function h(){" + // "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete + "eb('c1').addEventListener('keydown',function(e){" + "let b=eb('c1'),c=e.keyCode;" // c1 = Console command id + "if(38==c||40==c){b.autocomplete='off';}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete + "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp + "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown + "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history + "});" + "}" + "wl(h);" // Add console command key eventlistener after name has been synced with id (= wl(jd)) + "}"; diff --git a/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h b/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h index 729bb54a5..dd2cdc796 100644 --- a/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h +++ b/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h @@ -1,11 +1,9 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = + "{let ft;" "function la(p){" - "var a='';" - "if(la.arguments.length==1){" - "a=p;" - "clearTimeout(lt);" - "}" - "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) + "a=p||'';" + "clearTimeout(ft);clearTimeout(lt);" + "if(x!=null){x.abort()}" // Abort if no response within 2 seconds (happens on restart 1) "x=new XMLHttpRequest();" "x.onreadystatechange=function(){" "if(x.readyState==4&&x.status==200){" @@ -16,9 +14,11 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = ".replace(/{e}/g,\"\")" ".replace(/{c}/g,\"%%'>
hasArg("m") "x.send();" - "lt=setTimeout(la,%d);" // Settings.web_refresh - "}"; + "ft=setTimeout(la,20000);" // 20s failure timeout + "}}"; diff --git a/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h b/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h index 2d76a8c91..ddb5e6941 100644 --- a/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h +++ b/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h @@ -1,11 +1,8 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = - "var rfsh=1;" + "let rfsh=1,ft;" "function la(p){" - "var a='';" - "if(la.arguments.length==1){" - "a=p;" - "clearTimeout(lt);" - "}" + "a=p||'';" + "clearTimeout(ft);clearTimeout(lt);" "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) "x=new XMLHttpRequest();" "x.onreadystatechange=function(){" @@ -17,12 +14,16 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = ".replace(/{e}/g,\"\")" ".replace(/{c}/g,\"%%'>
hasArg("m") "x.send();" - "lt=setTimeout(la,%d);" // Settings.web_refresh + "ft=setTimeout(la,20000);" // 20s failure timeout "}" "}" "function seva(par,ivar){" @@ -34,11 +35,13 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = "rfsh=0;" "}" "function pr(f){" - "if (f) {" + "if(f){" + "clearTimeout(lt);clearTimeout(ft);" "lt=setTimeout(la,%d);" "rfsh=1;" - "} else {" - "clearTimeout(lt);" + "}else{" + "clearTimeout(lt);clearTimeout(ft);" "rfsh=0;" "}" - "}"; + "}" + ; diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 17cd2fe53..3fd70be4e 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -425,6 +425,8 @@ #define D_CMND_DIMMER_RANGE "DimmerRange" #define D_CMND_DIMMER_STEP "DimmerStep" #define D_CMND_HSBCOLOR "HSBColor" +#define D_CMND_VIRTUALCT "VirtualCT" +#define D_CMND_CTRANGE "CTRange" #define D_CMND_LED "Led" #define D_CMND_LEDTABLE "LedTable" #define D_CMND_FADE "Fade" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 7c354c8bf..cc75735cf 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -309,6 +309,7 @@ #define LIGHT_WHITE_BLEND_MODE false // [SetOption105] White Blend Mode - used to be `RGBWWTable` last value `0`, now deprecated in favor of this option #define LIGHT_VIRTUAL_CT false // [SetOption106] Virtual CT - Creates a virtual White ColorTemp for RGBW lights #define LIGHT_VIRTUAL_CT_CW false // [SetOption107] Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false) +#define LIGHT_VIRTUAL_CT_POINTS 3 // Number of reference points for Virtual CT (min 2, default 3) // -- Energy -------------------------------------- #define ENERGY_VOLTAGE_ALWAYS false // [SetOption21] Enable show voltage even if powered off @@ -494,6 +495,7 @@ #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code) #define USE_LIGHT_PALETTE // Add support for color palette (+0k7 code) +#define USE_LIGHT_VIRTUAL_CT // Add support for Virtual White Color Temperature (+1.1k code) #define USE_DGR_LIGHT_SEQUENCE // Add support for device group light sequencing (requires USE_DEVICE_GROUPS) (+0k2 code) // -- Counter input ------------------------------- diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 34a3bd2e3..bc1121123 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -941,6 +941,11 @@ void CmndSetoption(void) else if (4 == ptype) { // SetOption82 .. 113 bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload); switch (pindex) { +#ifdef USE_LIGHT + case 0: // SetOption 82 - (Alexa) Reduced CT range for Alexa (1) + setAlexaCTRange(); + break; +#endif case 3: // SetOption85 - Enable Device Groups case 6: // SetOption88 - PWM Dimmer Buttons control remote devices case 15: // SetOption97 - Set Baud rate for TuyaMCU serial communication (0 = 9600 or 1 = 115200) diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index 52bd19c58..6e4169e82 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -508,6 +508,7 @@ // -- Optional light modules ---------------------- //#undef USE_LIGHT // Enable Dimmer/Light support +#undef USE_LIGHT_VIRTUAL_CT // Disable support for Virtual White Color Temperature (SO106) #undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) @@ -643,6 +644,7 @@ //#undef USE_SONOFF_D1 // Disable support for Sonoff D1 Dimmer (+0k7 code) // -- Optional light modules ---------------------- +#undef USE_LIGHT_VIRTUAL_CT // Disable support for Virtual White Color Temperature (SO106) //#undef USE_LIGHT // Also disable all Dimmer/Light support #undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 2cd84f780..f0b94bcec 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -132,6 +132,10 @@ const char kLightCommands[] PROGMEM = "|" // No prefix D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_DIMMER_STEP "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR + "|" D_CMND_CTRANGE +#ifdef USE_LIGHT_VIRTUAL_CT + "|" D_CMND_VIRTUALCT +#endif // USE_LIGHT_VIRTUAL_CT #ifdef USE_LIGHT_PALETTE "|" D_CMND_PALETTE #endif // USE_LIGHT_PALETTE @@ -144,6 +148,10 @@ void (* const LightCommand[])(void) PROGMEM = { &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndDimmerStep, &CmndLedTable, &CmndFade, &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, &CmndWhite, &CmndChannel, &CmndHsbColor, + &CmndCTRange, +#ifdef USE_LIGHT_VIRTUAL_CT + &CmndVirtualCT, +#endif // USE_LIGHT_VIRTUAL_CT #ifdef USE_LIGHT_PALETTE &CmndPalette, #endif // USE_LIGHT_PALETTE @@ -181,6 +189,12 @@ const uint16_t CT_MAX = 500; // 2000K // Ranges used for Alexa const uint16_t CT_MIN_ALEXA = 200; // also 5000K const uint16_t CT_MAX_ALEXA = 380; // also 2600K +// Virtual CT default values +typedef uint8_t vct_pivot_t[LST_MAX]; +const size_t CT_PIVOTS = LIGHT_VIRTUAL_CT_POINTS; +const vct_pivot_t CT_PIVOTS_RGB PROGMEM = { 255, 255, 255, 0, 0 }; +const vct_pivot_t CT_PIVOTS_CWW PROGMEM = { 0, 0, 0, 255, 0 }; +const vct_pivot_t CT_PIVOTS_WWW PROGMEM = { 0, 0, 0, 0, 255 }; // New version of Gamma correction compute // Instead of a table, we do a multi-linear approximation, which is close enough @@ -308,6 +322,12 @@ struct LIGHT { uint16_t pwm_min = 0; // minimum value for PWM, from DimmerRange, 0..1023 uint16_t pwm_max = 1023; // maxumum value for PWM, from DimmerRange, 0..1023 + + // Virtual CT + uint16_t vct_ct[CT_PIVOTS]; // CT value for each segment +#ifdef USE_LIGHT_VIRTUAL_CT + vct_pivot_t vct_color[CT_PIVOTS]; // array of 3 colors each with 5 values +#endif } Light; power_t LightPower(void) @@ -378,13 +398,6 @@ class LightStateClass { uint8_t _briCT = 255; uint8_t _color_mode = LCM_RGB; // RGB by default - // the CT range below represents the rendered range, - // This is due to Alexa whose CT range is 199..383 - // Hence setting Min=200 and Max=380 makes Alexa use the full range - // Please note that you can still set CT to 153..500, but any - // value below _ct_min_range or above _ct_max_range not change the CT - uint16_t _ct_min_range = CT_MIN; // the minimum CT rendered range - uint16_t _ct_max_range = CT_MAX; // the maximum CT rendered range public: LightStateClass() { @@ -535,22 +548,7 @@ class LightStateClass { inline uint16_t getCT() const { return _ct; // 153..500, or CT_MIN..CT_MAX - } - - // get the CT value within the range into a 10 bits 0..1023 value - uint16_t getCT10bits() const { - return changeUIntScale(_ct, _ct_min_range, _ct_max_range, 0, 1023); - } - - inline void setCTRange(uint16_t ct_min_range, uint16_t ct_max_range) { - _ct_min_range = ct_min_range; - _ct_max_range = ct_max_range; - } - - inline void getCTRange(uint16_t *ct_min_range, uint16_t *ct_max_range) const { - if (ct_min_range) { *ct_min_range = _ct_min_range; } - if (ct_max_range) { *ct_max_range = _ct_max_range; } - } + } // get current color in XY format void getXY(float *x, float *y) { @@ -603,7 +601,7 @@ class LightStateClass { setColorMode(LCM_RGB); // try deactivating CT mode, setColorMode() will check which is legal } else { ct = (ct < CT_MIN ? CT_MIN : (ct > CT_MAX ? CT_MAX : ct)); - _ww = changeUIntScale(ct, _ct_min_range, _ct_max_range, 0, 255); + _ww = changeUIntScale(ct, Light.vct_ct[0], Light.vct_ct[CT_PIVOTS-1], 0, 255); _wc = 255 - _ww; _ct = ct; addCTMode(); @@ -932,15 +930,6 @@ public: return prev; } - void setAlexaCTRange(bool alexa_ct_range) { - // depending on SetOption82, full or limited CT range - if (alexa_ct_range) { - _state->setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); // 200..380 - } else { - _state->setCTRange(CT_MIN, CT_MAX); // 153..500 - } - } - inline bool isCTRGBLinked() { return _ct_rgb_linked; } @@ -1190,6 +1179,68 @@ uint8_t change10to8(uint16_t v) { return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); } +/*********************************************************************************************\ + * CT (White Color Temperature) +\*********************************************************************************************/ +// +// Ensure that invariants for Virtual CT are good: +// - CT_MIN <= ct[0] <= ct[1] <= ct[2] <= CT_MAX + +#ifdef USE_LIGHT_VIRTUAL_CT +void checkVirtualCT(void) { + if (Light.vct_ct[0] < CT_MIN) { Light.vct_ct[0] = CT_MIN; } + if (Light.vct_ct[CT_PIVOTS-1] > CT_MAX) { Light.vct_ct[CT_PIVOTS-1] = CT_MAX; } + for (uint32_t i = 0; i < CT_PIVOTS-1; i++) { + if (Light.vct_ct[i+1] < Light.vct_ct[i]) { Light.vct_ct[i+1] = Light.vct_ct[i]; } + } +} +#endif // USE_LIGHT_VIRTUAL_CT + +#ifdef USE_LIGHT_VIRTUAL_CT +// Init default values for virtual CT, depending on the number of channels +void initCTRange(uint32_t channels) { + if (channels == 4) { + if (Settings.flag4.virtual_ct_cw) { // Hardware White is Cold White + memcpy_P(Light.vct_color[0], CT_PIVOTS_CWW, sizeof(Light.vct_color[0])); // Cold white + memcpy_P(Light.vct_color[1], CT_PIVOTS_RGB, sizeof(Light.vct_color[1])); // Warm white + } else { // Hardware White is Warm White + memcpy_P(Light.vct_color[0], CT_PIVOTS_RGB, sizeof(Light.vct_color[0])); // Cold white + memcpy_P(Light.vct_color[1], CT_PIVOTS_CWW, sizeof(Light.vct_color[1])); // Warm white + } + } else if (channels == 5) { + memcpy_P(Light.vct_color[0], CT_PIVOTS_CWW, sizeof(Light.vct_color[0])); // Cold white + memcpy_P(Light.vct_color[1], CT_PIVOTS_WWW, sizeof(Light.vct_color[1])); // Warm white + } else { + memcpy_P(Light.vct_color[0], CT_PIVOTS_RGB, sizeof(Light.vct_color[0])); // Cold white + memcpy_P(Light.vct_color[1], CT_PIVOTS_RGB, sizeof(Light.vct_color[1])); // Warm white + } + for (uint32_t i = 1; i < CT_PIVOTS-1; i++) { + memcpy_P(Light.vct_color[i+1], Light.vct_color[i], sizeof(Light.vct_color[0])); // Copy slot 1 into slot 2 (slot 2 in unused) + } + checkVirtualCT(); +} +#endif // USE_LIGHT_VIRTUAL_CT + +void getCTRange(uint16_t * min_ct, uint16_t * max_ct) { + if (min_ct != nullptr) { *min_ct = Light.vct_ct[0]; } + if (max_ct != nullptr) { *max_ct = Light.vct_ct[CT_PIVOTS-1]; } +} + +void setCTRange(uint16_t ct_min, uint16_t ct_max) { + Light.vct_ct[0] = ct_min; + for (uint32_t i = 1; i < CT_PIVOTS; i++) { + Light.vct_ct[i] = ct_max; // all slots above [1] are not used + } +} + +void setAlexaCTRange(void) { // depending on SetOption82, full or limited CT range + if (Settings.flag4.alexa_ct_range) { + setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); + } else { + setCTRange(CT_MIN, CT_MAX); + } +} + /*********************************************************************************************\ * Gamma correction \*********************************************************************************************/ @@ -1299,9 +1350,14 @@ bool LightModuleInit(void) } else if ((Settings.param[P_RGB_REMAP] & 128) && (LST_RGBW <= pwm_channels)) { // SetOption37 // if RGBW or RGBCW, and SetOption37 >= 128, we manage RGB and W separately, hence adding a device TasmotaGlobal.devices_present++; - } else if ((Settings.flag4.virtual_ct) && (LST_RGBW == pwm_channels)) { - Light.virtual_ct = true; // enabled - TasmotaGlobal.light_type++; // create an additional virtual 5th channel + } else { +#ifdef USE_LIGHT_VIRTUAL_CT + initCTRange(pwm_channels); + if ((Settings.flag4.virtual_ct) && (LST_RGB <= pwm_channels)) { + Light.virtual_ct = true; // enabled + TasmotaGlobal.light_type += 5 - pwm_channels; // pretend it is a 5 channels bulb + } +#endif // USE_LIGHT_VIRTUAL_CT } return (TasmotaGlobal.light_type > LT_BASIC); @@ -1367,7 +1423,7 @@ void LightInit(void) light_controller.setSubType(Light.subtype); light_controller.loadSettings(); - light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + setAlexaCTRange(); light_controller.calcLevels(); // calculate the initial values (#8058) if (LST_SINGLE == Light.subtype) { @@ -1859,7 +1915,6 @@ void LightAnimate(void) bool power_off = false; // make sure we update CT range in case SetOption82 was changed - light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); Light.strip_timer_counter++; // set sleep parameter: either settings, @@ -1966,7 +2021,6 @@ void LightAnimate(void) uint16_t cur_col_10[LST_MAX]; // 10 bits resolution Light.update = false; - bool rgbwwtable_applied = false; // did we already applied RGBWWTable (ex: in white_blend_mode or virtual_ct) // first set 8 and 10 bits channels for (uint32_t i = 0; i < LST_MAX; i++) { @@ -1975,58 +2029,19 @@ void LightAnimate(void) cur_col_10[i] = change8to10(Light.new_color[i]); } + bool rgbwwtable_applied_white = false; // did we already applied RGBWWTable to white channels (ex: in white_blend_mode or virtual_ct) if (Light.pwm_multi_channels) { calcGammaMultiChannels(cur_col_10); } else { - calcGammaBulbs(cur_col_10); - - // Now see if we need to mix RGB and True White - // Valid only for LST_RGBW, LST_RGBCW, rgbwwTable[4] is zero, and white is zero (see doc) - if ((LST_RGBW <= Light.subtype) && (Settings.flag4.white_blend_mode) && (0 == cur_col_10[3]+cur_col_10[4])) { - uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]); - for (uint32_t i=0; i<3; i++) { - // substract white and adjust according to rgbwwTable - uint32_t adjust10 = change8to10(Settings.rgbwwTable[i]); - cur_col_10[i] = changeUIntScale(cur_col_10[i] - min_rgb_10, 0, 1023, 0, adjust10); - } - - // compute the adjusted white levels for 10 and 8 bits - uint32_t adjust_w_10 = changeUIntScale(Settings.rgbwwTable[3], 0, 255, 0, 1023); - uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); // set white power down corrected with rgbwwTable[3] - if (LST_RGBW == Light.subtype) { - // we simply set the white channel - cur_col_10[3] = white_10; - } else { // LST_RGBCW - // we distribute white between cold and warm according to CT value - uint32_t ct = light_state.getCT10bits(); - cur_col_10[4] = changeUIntScale(ct, 0, 1023, 0, white_10); - cur_col_10[3] = white_10 - cur_col_10[4]; - } - rgbwwtable_applied = true; - } else if ((Light.virtual_ct) && (0 == cur_col_10[0]+cur_col_10[1]+cur_col_10[2])) { - // virtual_ct is on and we don't have any RGB set - uint16_t sw_white = Settings.flag4.virtual_ct_cw ? cur_col_10[4] : cur_col_10[3]; // white power for virtual RGB - uint16_t hw_white = Settings.flag4.virtual_ct_cw ? cur_col_10[3] : cur_col_10[4]; // white for hardware LED - uint32_t adjust_sw = change8to10(Settings.flag4.virtual_ct_cw ? Settings.rgbwwTable[4] : Settings.rgbwwTable[3]); - uint32_t adjust_hw = change8to10(Settings.flag4.virtual_ct_cw ? Settings.rgbwwTable[3] : Settings.rgbwwTable[4]); - // set the target channels. Note: Gamma correction was arleady applied - cur_col_10[3] = changeUIntScale(hw_white, 0, 1023, 0, adjust_hw); - cur_col_10[4] = 0; // we don't actually have a 5the channel - sw_white = changeUIntScale(sw_white, 0, 1023, 0, adjust_sw); // pre-adjust virtual channel - for (uint32_t i=0; i<3; i++) { - uint32_t adjust = change8to10(Settings.rgbwwTable[i]); - cur_col_10[i] = changeUIntScale(sw_white, 0, 1023, 0, adjust); - } - rgbwwtable_applied = true; - } + // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs In %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]); + rgbwwtable_applied_white = calcGammaBulbs(cur_col_10); // true means that one PWM channel is used for CT + // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs Out %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]); } // Apply RGBWWTable only if not Settings.flag4.white_blend_mode - if (!rgbwwtable_applied) { - for (uint32_t i = 0; i 1023) ? 1023 : white_bri10; // max 1023 - -#ifdef ESP8266 - if ((PHILIPS == TasmotaGlobal.module_type) || (Settings.flag4.pwm_ct_mode)) { // channel 1 is the color tone, mapped to cold channel (0..255) - // Xiaomi Philips bulbs follow a different scheme: - cur_col_10[cw1] = light_state.getCT10bits(); - // channel 0=intensity, channel1=temperature - if (Settings.light_correction) { // gamma correction - cur_col_10[cw0] = ledGamma10_10(white_bri10_1023); // 10 bits gamma correction - } else { - cur_col_10[cw0] = white_bri10_1023; // no gamma, extend to 10 bits - } - } else -#endif // ESP8266 - if (Settings.light_correction) { - // if sum of both channels is > 255, then channels are probably uncorrelated - if (white_bri10 <= 1031) { // take a margin of 8 above 1023 to account for rounding errors - // we calculate the gamma corrected sum of CW + WW - uint16_t white_bri_gamma10 = ledGamma10_10(white_bri10_1023); - // then we split the total energy among the cold and warm leds - cur_col_10[cw0] = changeUIntScale(cur_col_10[cw0], 0, white_bri10_1023, 0, white_bri_gamma10); - cur_col_10[cw1] = changeUIntScale(cur_col_10[cw1], 0, white_bri10_1023, 0, white_bri_gamma10); - } else { - cur_col_10[cw0] = ledGamma10_10(cur_col_10[cw0]); - cur_col_10[cw1] = ledGamma10_10(cur_col_10[cw1]); - } - } - } +// +// Compute the Gamma correction for CW/WW +// Can be used for 2-channels (channels 0,1) or 5 channels (channels 3,4) +// +// It is implicitly called by calcGammaBulb5Channels() +// +// In: +// - 2 channels CW/WW in 10 bits format (0..1023) +// Out: +// - 2 channels CW/WW in 10 bits format, with Gamma corretion (if enabled), replaced in place +// - white_bri10: global brightness of white channel, split over CW/WW (basically the sum of CW+WW, but it's easier to compute on this basis) +// - white_free_cw: signals that CW/WW are free mode, and not linked via CT. This is used when channels are manually set on a channel per channel basis. CT is ignored +// +void calcGammaBulbCW(uint16_t cw10[2], uint16_t *white_bri10_out, bool *white_free_cw_out) { + uint16_t white_bri10 = cw10[0] + cw10[1]; // cumulated brightness + bool white_free_cw = (white_bri10 > 1031); // take a margin of 8 above 1023 to account for rounding errors + white_bri10 = (white_bri10 > 1023) ? 1023 : white_bri10; // max 1023 if (Settings.light_correction) { - // then apply gamma correction to RGB channels - if (LST_RGB <= Light.subtype) { - for (uint32_t i = 0; i < 3; i++) { - cur_col_10[i] = ledGamma10_10(cur_col_10[i]); - } - } - // If RGBW or Single channel, also adjust White channel - if ((LST_SINGLE == Light.subtype) || (LST_RGBW == Light.subtype)) { - cur_col_10[Light.subtype - 1] = ledGamma10_10(cur_col_10[Light.subtype - 1]); + if (white_free_cw) { + cw10[0] = ledGamma10_10(cw10[0]); + cw10[1] = ledGamma10_10(cw10[1]); + } else { + uint16_t white_bri10_gamma = ledGamma10_10(white_bri10); // gamma corrected white + // now distributed among both channels + cw10[0] = changeUIntScale(cw10[0], 0, white_bri10, 0, white_bri10_gamma); + cw10[1] = changeUIntScale(cw10[1], 0, white_bri10, 0, white_bri10_gamma); + // now use white_bri10_gamma as a reference + white_bri10 = white_bri10_gamma; } } + if (white_bri10_out != nullptr) { *white_bri10_out = white_bri10; } + if (white_free_cw_out != nullptr) { *white_free_cw_out = white_free_cw; } +} + +// +// Calculate the gamma correction for all 5 channels RGBCW +// Computation is valid for 1,3,4,5 channels +// 2-channels bulbs must be handled separately +// +// In: +// - 5 channels RGBCW in 10 bits format (0..1023) +// Out: +// - 5 channels RGBCW in 10 bits format, with Gamma corretion (if enabled), replaced in place +// - white_bri10: global brightness of white channel, split over CW/WW (basically the sum of CW+WW, but it's easier to compute on this basis) +// - white_free_cw: signals that CW/WW are free mode, and not linked via CT. This is used when channels are manually set on a channel per channel basis. CT is ignored +// +void calcGammaBulb5Channels(uint16_t col10[LST_MAX], uint16_t *white_bri10_out, bool *white_free_cw) { + for (uint32_t i = 0; i < 3; i++) { + if (Settings.light_correction) { + col10[i] = ledGamma10_10(col10[i]); + } + } + calcGammaBulbCW(&col10[3], white_bri10_out, white_free_cw); +} + +// sale but converts from 8 bits to 10 bits first +void calcGammaBulb5Channels_8(uint8_t in8[LST_MAX], uint16_t col10[LST_MAX]) { + for (uint32_t i = 0; i < LST_MAX; i++) { + col10[i] = change8to10(in8[i]); + } + calcGammaBulb5Channels(col10, nullptr, nullptr); +} + +bool calcGammaBulbs(uint16_t cur_col_10[5]) { + bool rgbwwtable_applied_white = false; + bool pwm_ct = false; + bool white_free_cw = false; // true if White channels are uncorrelated. Happens when CW+WW>255, i.e. manually setting white channels to exceed to total power of a single channel (may harm the power supply) + // Various values needed for accurate White calculation + // CT value streteched to 0..1023 (from within CT range, so not necessarily from 153 to 500). 0=Cold, 1023=Warm + uint16_t ct = light_state.getCT(); + uint16_t ct_10 = changeUIntScale(ct, Light.vct_ct[0], Light.vct_ct[CT_PIVOTS-1], 0, 1023); + + uint16_t white_bri10 = 0; // White total brightness normalized to 0..1023 + // uint32_t cw1 = Light.subtype - 1; // address for the ColorTone PWM + uint32_t cw0 = Light.subtype - 2; // address for the White Brightness PWM + + // calc basic gamma correction for all types + if ((LST_SINGLE == Light.subtype) || (LST_RGB <= Light.subtype)) { + calcGammaBulb5Channels(cur_col_10, &white_bri10, &white_free_cw); + } else if (LST_COLDWARM == Light.subtype) { + calcGammaBulbCW(cur_col_10, &white_bri10, &white_free_cw); + } + + // Now we know ct_10 and white_bri10 (gamma corrected if needed) + +#ifdef ESP8266 + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + if ((PHILIPS == TasmotaGlobal.module_type) || (Settings.flag4.pwm_ct_mode)) { // channel 1 is the color tone, mapped to cold channel (0..255) + pwm_ct = true; + // Xiaomi Philips bulbs follow a different scheme: + // channel 0=intensity, channel1=temperature + cur_col_10[cw0] = white_bri10; + cur_col_10[cw0+1] = ct_10; + return false; // avoid any interference + } + } +#endif // ESP8266 + + // Now see if we need to mix RGB and White + // Valid only for LST_RGBW, LST_RGBCW, SetOption105 1, and white is zero (see doc) + if ((LST_RGBW <= Light.subtype) && (Settings.flag4.white_blend_mode) && (0 == cur_col_10[3]+cur_col_10[4])) { + uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]); + cur_col_10[0] -= min_rgb_10; + cur_col_10[1] -= min_rgb_10; + cur_col_10[2] -= min_rgb_10; + + // Add to white level + uint32_t adjust_w_10 = change8to10(Settings.rgbwwTable[3]); // take the correction factor, bought back to 10 bits + white_bri10 += changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); // set white power down corrected with rgbwwTable[3] + white_bri10 = (white_bri10 > 1023) ? 1023 : white_bri10; // max 1023 + rgbwwtable_applied_white = true; + } + +#ifdef USE_LIGHT_VIRTUAL_CT + // compute virtual CT, which is suppsed to be compatible with white_blend_mode + if (Light.virtual_ct && (!white_free_cw) && (LST_RGBW <= Light.subtype)) { // any light with a white channel + vct_pivot_t *pivot = &Light.vct_color[0]; + uint16_t *from_ct = &Light.vct_ct[0]; + + for (uint32_t i = 1; i < CT_PIVOTS-1; i++) { + if (ct > Light.vct_ct[i]) { // if above mid-point, take range [1]..[2] instead of [0]..[1] + pivot++; + from_ct++; + } + } + uint16_t from10[LST_MAX]; + uint16_t to10[LST_MAX]; + calcGammaBulb5Channels_8(*pivot, from10); + calcGammaBulb5Channels_8(*(pivot+1), to10); + + vct_pivot_t *pivot1 = pivot + 1; + // AddLog_P(LOG_LEVEL_INFO, PSTR("+++ from_ct %d, to_ct %d [%03X,%03X,%03X,%03X,%03X] - [%03X,%03X,%03X,%03X,%03X]"), + // *from_ct, *(from_ct+1), (*pivot)[0], (*pivot)[1], (*pivot)[2], (*pivot)[3], (*pivot)[4], + // (*pivot1)[0], (*pivot1)[1], (*pivot1)[2], (*pivot1)[3], (*pivot1)[4]); + // AddLog_P(LOG_LEVEL_INFO, PSTR("+++ from10 [%03X,%03X,%03X,%03X,%03X] - to 10 [%03X,%03X,%03X,%03X,%03X]"), + // from10[0],from10[0],from10[0],from10[0],from10[4], + // to10[0],to10[0],to10[0],to10[0],to10[4]); + + // set both CW/WW to zero since their previous value don't count anymore + cur_col_10[3] = 0; + cur_col_10[4] = 0; + + // Add the interpolated point to each component + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10[i] += changeUIntScale(changeUIntScale(ct, *from_ct, *(from_ct+1), from10[i], to10[i]), 0, 1023, 0, white_bri10); + if (cur_col_10[i] > 1023) { cur_col_10[i] = 1023; } + } + } else +#endif // USE_LIGHT_VIRTUAL_CT + // compute the actual levels for CW/WW + // We know ct_10 and white_bri_10 (which may be Gamma corrected) + // cur_col_10[cw0] and cur_col_10[cw1] were unmodified up to now + if (LST_RGBW == Light.subtype) { + cur_col_10[3] = white_bri10; // simple case, we set the White level to the required brightness + } else if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + // if sum of both channels is > 255, then channels are probably uncorrelated + if (!white_free_cw) { + // then we split the total energy among the cold and warm leds + cur_col_10[cw0+1] = changeUIntScale(ct_10, 0, 1023, 0, white_bri10); + cur_col_10[cw0] = white_bri10 - cur_col_10[cw0+1]; + } + } + return rgbwwtable_applied_white; } #ifdef USE_DEVICE_GROUPS @@ -3045,6 +3174,83 @@ void CmndWakeupDuration(void) ResponseCmndNumber(Settings.light_wakeup); } +void CmndCTRange(void) +{ + // Format is "CTRange ctmin,ctmax" + // Ex: + // CTRange 153,500 + // CTRange + // CTRange 200,350 + char *p; + strtok_r(XdrvMailbox.data, ",", &p); + if (p != nullptr) { + int32_t ct_min = strtol(XdrvMailbox.data, nullptr, 0); + int32_t ct_max = strtol(p, nullptr, 0); + if ( (ct_min >= CT_MIN) && (ct_min <= CT_MAX) && + (ct_max >= CT_MIN) && (ct_max <= CT_MAX) && + (ct_min <= ct_max) + ) { + setCTRange(ct_min, ct_max); + } else { + return; // error + } + } + Response_P(PSTR("{\"%s\":\"%d,%d\"}"), XdrvMailbox.command, Light.vct_ct[0], Light.vct_ct[CT_PIVOTS-1]); +} + +#ifdef USE_LIGHT_VIRTUAL_CT +void CmndVirtualCT(void) +{ + if (!Settings.flag4.virtual_ct) { + ResponseCmndChar_P(PSTR("You need to enable `SetOption106 1`")); + return; + } + if (XdrvMailbox.data[0] == ('{')) { + // parse JSON + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return; } + + uint32_t idx = 0; + for (auto key : root) { + if (idx >= CT_PIVOTS) { ResponseCmndChar_P(PSTR("Too many points")); return; } + + int32_t ct_val = strtol(key.getStr(), nullptr, 0); + if ((ct_val < CT_MIN) || (ct_val > CT_MAX)) { ResponseCmndChar_P(PSTR("CT out of range")); return; } + char * color = (char*) key.getValue().getStr(); + // call color parser + Light.vct_ct[idx] = ct_val; + if (LightColorEntry(color, strlen(color))) { + memcpy(&Light.vct_color[idx], Light.entry_color, sizeof(Light.vct_color[idx])); + } + idx++; + } + for (uint32_t i = idx-1; i < CT_PIVOTS-1; i++) { + Light.vct_ct[i+1] = Light.vct_ct[i]; + memcpy(&Light.vct_color[i+1], &Light.vct_color[i], sizeof(Light.vct_color[0])); + } + } + checkVirtualCT(); + + Response_P(PSTR("{\"%s\":{"), XdrvMailbox.command); + uint32_t pivot_len = CT_PIVOTS; + vct_pivot_t * pivot = &Light.vct_color[0]; + if (Light.vct_ct[1] >= Light.vct_ct[2]) { pivot_len = 2; } // only 2 points are valid + + bool end = false; + for (uint32_t i = 0; (i < CT_PIVOTS) && !end; i++) { + if ((i >= CT_PIVOTS-1) || (Light.vct_ct[i] >= Light.vct_ct[i+1])) { + end = true; + } + ResponseAppend_P(PSTR("\"%d\":\"%02X%02X%02X%02X%02X\"%c"), Light.vct_ct[i], + (*pivot)[0], (*pivot)[1], (*pivot)[2], (*pivot)[3], (*pivot)[4], + end ? '}' : ','); + pivot++; + } + ResponseJsonEnd(); +} +#endif // USE_LIGHT_VIRTUAL_CT + #ifdef USE_LIGHT_PALETTE void CmndPalette(void) { diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino index 13a7864c5..51a0a9c5b 100644 --- a/tasmota/xdrv_16_tuyamcu.ino +++ b/tasmota/xdrv_16_tuyamcu.ino @@ -517,7 +517,7 @@ bool TuyaSetChannels(void) Tuya.Snapshot[0] = changeUIntScale(Light.current_color[0], 0, 255, 0, 100); Tuya.Snapshot[1] = changeUIntScale(Light.current_color[1], 0, 255, 0, 100); } else { // CT Light or RGBWC - light_state.getCTRange(&Tuya.CTMin, &Tuya.CTMax); // SetOption82 - Reduce the CT range from 153..500 to 200..380 to accomodate with Alexa range + getCTRange(&Tuya.CTMin, &Tuya.CTMax); // SetOption82 - Reduce the CT range from 153..500 to 200..380 to accomodate with Alexa range Tuya.Snapshot[0] = light_state.getDimmer(); Tuya.Snapshot[1] = light_state.getCT(); } diff --git a/tasmota/xdsp_12_ST7789.ino b/tasmota/xdsp_12_ST7789.ino index 3c85f168a..1c9ec1aff 100644 --- a/tasmota/xdsp_12_ST7789.ino +++ b/tasmota/xdsp_12_ST7789.ino @@ -64,11 +64,11 @@ void ST7789_InitDriver() if (XDSP_12 == Settings.display_model) { - if (Settings.display_width != ST7789_TFTWIDTH) { - Settings.display_width = ST7789_TFTWIDTH; + if (!Settings.display_width) { + Settings.display_width = 240; } - if (Settings.display_height != ST7789_TFTHEIGHT) { - Settings.display_height = ST7789_TFTHEIGHT; + if (!Settings.display_height) { + Settings.display_height = 240; } // disable screen buffer @@ -130,9 +130,15 @@ void ST7789_InitDriver() #ifdef SHOW_SPLASH // Welcome text - renderer->setTextFont(2); renderer->setTextColor(ST7789_WHITE,ST7789_BLACK); - renderer->DrawStringAt(30, 100, "ST7789 TFT!", ST7789_WHITE,0); + int fontSize = 2; + renderer->setTextFont(2); + if (Settings.display_width<240) { + fontSize = 1; + } + renderer->setTextFont(fontSize); + int fontHeight = 12 * fontSize; + renderer->DrawStringAt(30, (Settings.display_height-fontHeight)/2, "ST7789 TFT!", ST7789_WHITE,0); delay(1000); #endif diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/xsns_29_mcp230xx.ino index 6a960968a..27c91773b 100644 --- a/tasmota/xsns_29_mcp230xx.ino +++ b/tasmota/xsns_29_mcp230xx.ino @@ -196,6 +196,7 @@ void MCP230xx_ApplySettings(void) I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); #endif // USE_MCP230xx_OUTPUT } +#ifdef USE_MCP230xx_OUTPUT TasmotaGlobal.devices_present -= mcp230xx_oldoutpincount; mcp230xx_oldoutpincount = 0; for (uint32_t idx=0;idx