diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp b/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp
index 495c18ad8..c7cdeba0a 100644
--- a/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp
+++ b/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp
@@ -249,10 +249,12 @@ void ICACHE_RAM_ATTR ILI9488::fastSPIwrite(uint8_t d,uint8_t dc) {
#else
// ESP32 section
void ILI9488::writedata(uint8_t d) {
+ ILI9488_START
fastSPIwrite(d,1);
}
void ILI9488::writecommand(uint8_t c) {
+ ILI9488_START
fastSPIwrite(c,0);
}
diff --git a/tasmota/tasmota_template_ESP32.h b/tasmota/tasmota_template_ESP32.h
index 7d47b0409..aea45ea6a 100644
--- a/tasmota/tasmota_template_ESP32.h
+++ b/tasmota/tasmota_template_ESP32.h
@@ -127,7 +127,8 @@ enum UserSelectablePins {
GPIO_WEBCAM_PSRCS,
GPIO_BOILER_OT_RX, GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin
GPIO_WINDMETER_SPEED, // WindMeter speed counter pin
- GPIO_KEY1_TC, // Touch pin as button
+ GPIO_KEY1_TC, // Touch pin as button
+ GPIO_BL0940_RX, // BL0940 serial interface
GPIO_SENSOR_END };
enum ProgramSelectablePins {
@@ -216,7 +217,8 @@ const char kSensorNames[] PROGMEM =
D_GPIO_WEBCAM_HSD "|"
D_GPIO_WEBCAM_PSRCS "|"
D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|"
- D_SENSOR_WINDMETER_SPEED "|" D_SENSOR_BUTTON "_tc"
+ D_SENSOR_WINDMETER_SPEED "|" D_SENSOR_BUTTON "_tc|"
+ D_SENSOR_BL0940_RX
;
const char kSensorNamesFixed[] PROGMEM =
@@ -403,6 +405,9 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_LE01MR_TX), // F7F LE-01MR energy meter tx pin
AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin
#endif // IFDEF:USE_LE01MR
+#ifdef USE_BL0940
+ AGPIO(GPIO_BL0940_RX), // BL0940 Serial interface
+#endif
#endif // USE_ENERGY_SENSOR
// Serial
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index 6bf15230e..7fcd82a9f 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -48,6 +48,47 @@ enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTASLAVE };
static const char * HEADER_KEYS[] = { "User-Agent", };
+#ifdef USE_UNISHOX_COMPRESSION
+#ifdef USE_JAVASCRIPT_ES6
+// insert D_HTML_LANGUAGE later
+const size_t HTTP_HEADER1_SIZE = 377;
+const char HTTP_HEADER1_COMPRESSED[] PROGMEM = "\x3D\x0F\xE1\x10\x98\x1D\x19\x0C\x64\x85\x50\xD0\x8F\xC3\xD0\x55\x0D\x09\x05\x7C"
+ "\x3C\x7C\x3F\xB2\xEC\xD7\xE6\x86\x7D\x78\xFE\xCB\xB3\x5F\x9A\x1A\x0C\x2B\xF7\x8F"
+ "\x87\xB0\xF6\x1F\x87\xA0\xA7\x62\x1F\x87\xA0\xD7\x56\x83\x15\x7F\xF3\xA3\xE1\xF6"
+ "\x2E\x8C\x1D\x67\x3E\x7D\x90\x21\x52\xEB\x1A\xCF\x87\xB0\xCF\x58\xF8\xCC\xFD\x1E"
+ "\xC4\x1E\x75\x3E\xA3\xE1\xEC\x1F\xD1\x28\x51\xF0\x46\x67\xA1\xB3\xAC\x7F\x44\xA1"
+ "\x47\x56\xF6\xD6\xD8\x47\x5F\x83\xB0\x99\xF0\xE4\x3A\x88\x5F\x9F\xCE\xBF\x07\x61"
+ "\x58\xE0\x99\xF3\xB0\xF6\x1D\x87\xE1\xE9\x5B\x41\x33\xF0\xFA\xF2\x3A\xD1\xF5\xE3"
+ "\xD0\xEC\x04\x19\x67\xA7\x83\xFE\x8C\xA3\xF0\xCE\xFE\x8D\x87\xCE\x16\x10\x47\x50"
+ "\x54\x75\x56\x1D\x54\x30\xEA\x18\x19\xF0\xFB\x3E\xCF\x0C\x71\xF3\xC7\xC3\xF0\x4C"
+ "\x0C\x58\xD7\xD4\x74\x1E\x74\x4C\x26\x35\xF5\x10\xE3\x22\xD1\x0E\xEF\x8E\xF1\xE0"
+ "\xD5\xE0\x48\xBA\x6A\x16\xFE\x64\x5E\x61\x30\xEB\x3E\x77\x7C\x77\x8F\x1E\x18\x7C"
+ "\xD3\xE1\xF8\xC7\x1D\xDD\x3B\xC7\x4A\x32\x18\xCF\x87\x74\x11\xA4\x1F\x0F\x87\xDD"
+ "\x33\x65\x1F\x67\x68\xFB\x19\x7E\xF0\xFE\x7C\x43\xEC\xF3\x04\x19\xC7\x78\xF0\x3E"
+ "\x11\xF0\xC1\xF0\xFC\x1F\xDE\x13\x07\xCE\x96\x20\x84\xCC\xDF\x51\x05\xBE\xA7\xCF"
+ "\xE7\x74\xFB\x0B\x2C\x43\xEC\xEA\x30\x77\x8F\x06";
+#define HTTP_HEADER1 Decompress(HTTP_HEADER1_COMPRESSED,HTTP_HEADER1_SIZE).c_str()
+#else
+const size_t HTTP_HEADER1_SIZE = 431;
+const char HTTP_HEADER1_COMPRESSED[] PROGMEM = "\x3D\x0F\xE1\x10\x98\x1D\x19\x0C\x64\x85\x50\xD0\x8F\xC3\xD0\x55\x0D\x09\x05\x7C"
+ "\x3C\x7C\x3F\xB2\xEC\xD7\xE6\x86\x7D\x78\xFE\xCB\xB3\x5F\x9A\x1A\x0C\x2B\xF7\x8F"
+ "\x87\xB0\xF6\x1F\x87\xA0\xA7\x62\x1F\x87\xA0\xD7\x56\x83\x15\x7F\xF3\xA3\xE1\xF6"
+ "\x2E\x8C\x1D\x67\x3E\x7D\x90\x21\x52\xEB\x1A\xCF\x87\xB0\xCF\x58\xF8\xCC\xFD\x1E"
+ "\xC4\x1E\x75\x3E\xA3\xE1\xEC\x1F\xD1\x28\x51\xF0\x46\x67\xA1\xB3\xAC\x7F\x44\xA1"
+ "\x47\x56\xF6\xD6\xD8\x47\x5F\x83\xB0\x99\xF0\xE4\x3A\x88\x5F\x9F\xCE\xBF\x07\x61"
+ "\x58\xE0\x99\xF3\xB0\xF6\x1D\x87\xE1\xE9\x5B\x41\x33\xF0\xFA\xF2\x3A\xD1\xF5\xE3"
+ "\xD0\xEC\x04\x19\x67\xA7\x83\xFE\x8C\xA3\xF0\xCE\xFE\x8D\x87\xCE\x16\x10\x47\x50"
+ "\x54\x75\x56\x1D\x54\x30\xEA\x18\x19\xF0\xFB\x3E\xCF\x06\x05\xF0\x75\xB9\xC9\x8E"
+ "\x3B\xBE\x3B\xC7\xB7\xEE\x85\xFF\x90\x98\x18\xB1\xAF\xA8\xE8\x3C\xE8\x98\x4C\x6B"
+ "\xEA\x21\xC6\x45\xA2\x1D\xDF\x1D\xE3\xC1\xEE\x04\x4C\x38\xD5\xE0\x4F\xC3\x8D\x42"
+ "\xDF\xCC\x8B\xCC\x26\x1D\x67\xC1\x27\x0D\xF0\xC3\xBB\xA7\x78\xF6\xB1\xC7\x77\x4E"
+ "\xF1\xD2\x8C\x86\x33\xE1\xDD\x04\x69\x07\xC3\xE1\xF7\x4C\xD9\x47\xD9\xDA\x3E\xC6"
+ "\x5F\xBC\x3F\x9F\x10\xFB\x3C\xC1\x06\x70\x23\xE3\xE3\xE1\x1D\xD3\x07\x78\xF6\x8F"
+ "\xEF\x09\x83\xE7\x4B\x10\x42\x66\x6F\xA8\x82\xDF\x53\xE7\xF3\xBA\x7D\x85\x96\x21"
+ "\xF6\x75\x18\x3B\xC7\x83\xDC";
+#define HTTP_HEADER1 Decompress(HTTP_HEADER1_COMPRESSED,HTTP_HEADER1_SIZE).c_str()
+#endif // USE_JAVASCRIPT_ES6
+#else
const char HTTP_HEADER1[] PROGMEM =
""
"
"
@@ -78,7 +119,8 @@ const char HTTP_HEADER1[] PROGMEM =
"function wl(f){" // Execute multiple window.onload
"window.addEventListener('load',f);"
"}";
-#endif
+#endif // USE_JAVASCRIPT_ES6
+#endif //USE_UNISHOX_COMPRESSION
const char HTTP_SCRIPT_COUNTER[] PROGMEM =
"var cn=180;" // seconds
@@ -91,6 +133,58 @@ const char HTTP_SCRIPT_COUNTER[] PROGMEM =
"}"
"wl(u);";
+#ifdef USE_UNISHOX_COMPRESSION
+#ifdef USE_SCRIPT_WEB_DISPLAY
+const size_t HTTP_SCRIPT_ROOT_SIZE = 706;
+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\xD8\x60\x87\x60\x43\xF0\xF4\x3B\x2B\xE0\x93\x64\x33\xDC\x76\x0F\x1D"
+ "\x47\xB0\xFA\x3E\x8F\xB3\xF0\xF4\x13\x4C\xC1\x0F\x60\xA6\x6C\xA3\xAE\xC2\xD1\xEE"
+ "\x3C\xC3\x7D\x4F\xE7\x83\x19\xD4\x75\x8F\xBD\x1E\x15\x47\x99\xEC\x3B\xC7\x86\x38"
+ "\xEE\x9F\x61\x1C\x87\xD9\xDE\x3A\x16\xF7\x3F\x90\xA2\xA2\x1A\x41\x1F\x3C\x78\x3D"
+ "\xC7\xB8\xF1\xA6\x11\xDD\xF9\x8F\x0A\x3B\xC8\xF6\x9B\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\x20\x36\x75\x1F"
+ "\x42\x1D\xE3\xC1\xEE\x3D\xC0\x89\xCA\x77\x99\x9D\x9D\xD1\x97\xF3\xAB\x4C\xEF\xE7"
+ "\x78\xF6\x85\x67\x74\xFB\x3F\x5E\x33\x3E\x1F\x67\x6F\x4C\xEF\xE7\x6C\xFB\x3F\x67"
+ "\xD9\xDB\x19\x7F\x3B\xC7\x80\x46\xC3\x74\x12\x30\xD0\x42\xE6\x78\x14\xF1\x4F\x98"
+ "\xF0\xA3\xE1\xC6\x40\x8D\x8D\x8C\xF9\xDD\x30\x77\x8F\x6E\x98\x47\x74\xC1\xDE\x47"
+ "\xB4\x14\x37\xA0\x42\xCB\xCF\x72\x61\x79\xA3\xDA\x09\x9C\xE1\x02\x1E\x60\x7B\x8D";
+#define HTTP_SCRIPT_ROOT Decompress(HTTP_SCRIPT_ROOT_COMPRESSED,HTTP_SCRIPT_ROOT_SIZE).c_str()
+#else
+const size_t HTTP_SCRIPT_ROOT_SIZE = 486;
+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\x4D\x86\x08\x76\x04\x3F"
+ "\x0F\x43\xB2\xBE\x09\x36\x43\x3D\xC7\x60\xF1\xD4\x7B\x0F\xA3\xE8\xFB\x3F\x0F\x41"
+ "\x34\xCC\x10\xF6\x0A\x66\xCA\x3A\xEC\x2D\x1E\xE3\xCC\x37\xD4\xFE\x78\x31\x9D\x47"
+ "\x58\xFB\xD1\xE1\x54\x79\x9E\xC3\xBC\x78\x63\x8E\xE9\xF6\x11\xC8\x7D\x9D\xE3\xA1"
+ "\x6F\x73\xF9\x0A\x2A\x2B\x21\xA4\x11\xF3\xC7\x83\xDC\x7B\x8F\x06\xC3\xA6\x0C\x7C"
+ "\x77\x4F\xB2\x1E\x26\x54\x7D\x9D\x47\xD9\xD0\xED\x0D\x3E\x1C\x87\xD9\xDB\x2B\x3A"
+ "\xAB\xE2\xD9\xDE\x3C\x1B\x0E\x9E\x7C\x21\xDD\x3B\xC7\x80\xA8\xF9\xE7\x40\x89\xC7"
+ "\xB5\x9D\x47\xD0\x87\x78\xF0\x7B\x8D";
+#define HTTP_SCRIPT_ROOT Decompress(HTTP_SCRIPT_ROOT_COMPRESSED,HTTP_SCRIPT_ROOT_SIZE).c_str()
+#endif // USE_SCRIPT_WEB_DISPLAY
+#else
const char HTTP_SCRIPT_ROOT[] PROGMEM =
#ifdef USE_SCRIPT_WEB_DISPLAY
"var rfsh=1;"
@@ -151,7 +245,22 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM =
"lt=setTimeout(la,%d);" // Settings.web_refresh
"}";
#endif // USE_SCRIPT_WEB_DISPLAY
+#endif //USE_UNISHOX_COMPRESSION
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_SCRIPT_ROOT_PART2_SIZE = 222;
+const char HTTP_SCRIPT_ROOT_PART2_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x41\x06\x77\x4C\xCE\xAD\x3A\x86\x1D\xE3\xDB\xA6\x0E\xEB\x1C"
+ "\x77\x4F\xBF\x1F\x67\x78\xEF\x1E\xDD\x30\x77\x4C\xCF\x87\xC3\xEC\x51\xF6\x7F\x8F"
+ "\xF1\x99\xF0\xF8\x7D\x88\x7D\x9D\xE3\xDA\x67\x7F\x5E\x08\xF8\xC7\x1D\xD3\xEF\xC1"
+ "\x1C\xC3\xEC\xEF\x1D\x08\xCE\xC2\x16\xCF\x2A\x01\x85\x87\x9D\x3D\x46\x41\x33\xA0"
+ "\xEB\x0C\xD0\x7B\xF8\x2F\x84\x3E\x1F\x61\x6F\x3B\xF9\xD6\x3D\xFB\x13\x5F\x51\xDD"
+ "\xAC\x5F\xD1\xE1\x54\x75\x7C\x78\x71\xDD\x3E\xCE\xDF\x82\x3B\x67\xD9\xF4\x7D\x1D"
+ "\x40\x89\x14\x10\xE2\x9D\xE3\xA8\x57\x82\x3B\xA7\xD9\xDB\x04\x1D\x14\xE5\x10\x21"
+ "\xE8\xA7\x6C\xFB\x3A\x8E\x46\xCF\xA3\xE8\xEA\xD6\x7D\x1F\x47\x78\xEF\x1F\x67\x83"
+ "\xDC\x7B\x88\x2B\x3B\xA7\xD9\xFA\x3E\xCE\xD9\x99\xDB\xD3\xB6\x7D\x9F\x0F\xB3\xB6"
+ "\x30\xEF\x1E\x0F\x70\xF8\x47\x74\x2B\x3B\xC7\x83";
+#define HTTP_SCRIPT_ROOT_PART2 Decompress(HTTP_SCRIPT_ROOT_PART2_COMPRESSED,HTTP_SCRIPT_ROOT_PART2_SIZE).c_str()
+#else
const char HTTP_SCRIPT_ROOT_PART2[] PROGMEM =
"function lc(v,i,p){"
"if(eb('s')){" // Check if Saturation is in DOM otherwise javascript fails on la()
@@ -163,6 +272,7 @@ const char HTTP_SCRIPT_ROOT_PART2[] PROGMEM =
"la('&'+v+i+'='+p);"
"}"
"wl(la);";
+#endif //USE_UNISHOX_COMPRESSION
const char HTTP_SCRIPT_WIFI[] PROGMEM =
"function c(l){"
@@ -173,6 +283,42 @@ const char HTTP_SCRIPT_WIFI[] PROGMEM =
const char HTTP_SCRIPT_RELOAD_TIME[] PROGMEM =
"setTimeout(function(){location.href='.';},%d);";
+#ifdef USE_UNISHOX_COMPRESSION
+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";
+#define HTTP_SCRIPT_CONSOL Decompress(HTTP_SCRIPT_CONSOL_COMPRESSED,HTTP_SCRIPT_CONSOL_SIZE).c_str()
+#else
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
@@ -222,6 +368,7 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM =
"});"
"}"
"wl(h);"; // Add console command key eventlistener after name has been synced with id (= wl(jd))
+#endif //USE_UNISHOX_COMPRESSION
const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM =
"}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace
@@ -231,6 +378,34 @@ const char HTTP_MODULE_TEMPLATE_REPLACE_INDEX[] PROGMEM =
const char HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX[] PROGMEM =
"}2%d'>%s}3"; // }2 and }3 are used in below os.replace
+#if defined(USE_UNISHOX_COMPRESSION) && defined(ESP32)
+// no compression on ESP8266, we would lose 16 bytes
+const size_t HTTP_SCRIPT_MODULE_TEMPLATE_SIZE = 602;
+const char HTTP_SCRIPT_MODULE_TEMPLATE_COMPRESSED[] PROGMEM = "\x33\xBF\xAC\xF1\xD4\x2B\xC7\x83\x02\xF8\x3A\xDC\xE4\x1B\x3B\xBA\x75\x1A\x8E\xF1"
+ "\xED\x33\xBF\xAC\x3E\x09\x81\x8B\x1A\xFA\x8E\x81\xFD\xDD\x32\x61\x31\xAF\xA8\xEE"
+ "\x9F\x78\x32\xB7\x38\xFB\x3B\xC7\x8C\x3A\x53\x36\x51\x07\x9D\x4F\xA8\xF9\xA7\x83"
+ "\x51\xD2\xC6\x0C\x7C\x21\x06\x2B\x42\x10\xEE\xE1\xDE\x3C\x1E\xE0\x44\xCD\xB2\x8E"
+ "\xE8\xF1\xD5\xE0\x41\xCD\xAC\xF9\xE3\xF4\x71\x91\xB0\xC1\x86\x71\x9D\x44\x38\xF8"
+ "\x71\x9D\x44\x19\xD4\x30\xEA\x08\xEA\xA3\xE1\xAB\xC7\x74\xFB\x3C\x85\x1F\x67\x6C"
+ "\x78\xEF\x1D\x42\xCF\x9E\x3F\x47\x19\x1B\x0E\x37\x08\xC1\xE0\x23\xE5\x1D\x01\x07"
+ "\x4D\xF1\xD0\x27\xC3\xD4\x28\xF0\x63\x3E\x77\x74\xF8\x11\xE3\x4F\x1A\x75\x9D\x67"
+ "\x78\xF6\x8C\x04\x5B\xC7\xBD\xA7\x59\xC8\x7B\xE7\x42\x19\x7F\x7D\x45\xD8\x23\x3C"
+ "\x0C\x3A\x7D\x8D\xC3\x36\x08\x3B\x70\x24\xE0\x87\x78\xF0\x7B\x82\x3E\x0A\x04\xAC"
+ "\xC8\xE3\x3C\x16\x9E\x81\x1E\x34\xED\x9D\xB3\xBC\x7B\x43\x3E\x0A\xF1\xEF\x69\xEF"
+ "\x82\x17\x2A\x01\xE7\x8D\x30\x77\x6C\xF8\x7C\x0C\xEF\x1E\xD1\xC0\x89\x50\xE3\x70"
+ "\x8C\x1E\x07\x7D\xD9\xA1\xE0\xF7\x1E\xEF\x1F\x87\xE1\xF0\xE6\x90\x21\x64\x47\x21"
+ "\xE0\xB4\xF4\x3E\x0E\x04\x2C\x8D\x9D\xD3\xBB\xA7\xA1\xC8\xCE\xF1\xDA\x3B\xA7\xD9"
+ "\xDC\x3E\xCE\xD9\x69\xDE\x3C\xF4\xEA\xA3\xBC\x78\x3D\xCC\x71\xDD\x3E\xC5\x1F\x67"
+ "\x6C\x78\xEF\x1D\x0C\xEC\x21\x6C\xF8\x2C\xED\x9C\x87\x82\xA3\xA7\xA8\xC8\x26\x74"
+ "\x33\xDF\x68\xED\x0B\x68\xC8\xF8\x77\x47\x1F\x87\x19\xDE\x3B\x47\xD9\xF6\x79\x9F"
+ "\x64\x2B\x44\x11\xF1\xF6\x08\xDC\x58\xF8\xD0\xEE\xF8\xEA\x1E\x04\x3E\x42\xF3\xC7"
+ "\x4F\xB1\x81\x58\x6C\xEE\x9D\x87\xB8\xE5\x1D\x84\x3C\x75\x1E\xC3\xD0\x10\x78\x4B"
+ "\x40\x83\x9E\x9F\x67\xB0\xEF\x02\x35\xD3\x96\x76\x10\xF1\xD4\x7B\x0F\x43\xB0\x10"
+ "\x6F\x1F\x87\xB0\xEF\x1E\x18\xE3\xBA\x7D\x8F\x1F\x67\x6C\x78\xEF\x1D\x37\xB9\xFC"
+ "\x85\x15\x10\xD2\x08\xF9\x80\x8D\x48\x10\x72\x13\xBA\x3C\x7A\x1C\x48\xEF\x1D\xA2"
+ "\x04\x3E\x47\x4F\x3F\x1E\x34\xC0\x20\xD0\x3D\xA0\x85\xC9\xF9\xE0\xF7\x1E\xE3";
+#define HTTP_SCRIPT_MODULE_TEMPLATE Decompress(HTTP_SCRIPT_MODULE_TEMPLATE_COMPRESSED,HTTP_SCRIPT_MODULE_TEMPLATE_SIZE).c_str()
+#else
const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM =
#ifdef ESP8266
"var os;"
@@ -264,7 +439,25 @@ const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM =
"if(g<99){ot(g,s);}"
"}";
#endif // ESP8266 - ESP32
+#endif //USE_UNISHOX_COMPRESSION
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_SCRIPT_TEMPLATE_SIZE = 303;
+const char HTTP_SCRIPT_TEMPLATE_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x41\x08\x77\x45\x9D\x46\x0E\xF1\xED\x33\xBF\xA3\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\xC7\x4E\xE9\xDE\x3D\xBA\x60"
+ "\xEE\xD0\xAD\xF1\xD3\xEE\xC4\x32\x2F\x55\xD3\x3E\x1F\x0E\x61\xFA\x3F\x45\x42\xB7"
+ "\xC7\x4F\x55\xD0\xBF\x1F\x0F\x87\x29\xB3\xBC\x7B\x48\x10\x70\x43\xBC\x78\x3D\xC7"
+ "\xB8\xF0\x6C\x3A\x60\xC7\xC7\x74\xFB\x21\xE2\x65\x47\xD9\xD4\x2C\xEA\xAF\x8B\x67"
+ "\x78\xF0\x6C\x3A\x79\xF0\x87\x74\xEF\x1E\x0F\x71\x9D\xFD\x06\x78\x04\x4E\x2A\x01"
+ "\x4D\x87\x21\xDD\x21\xC0\x83\xBF\xE9\xD4\x6B\x3A\x87\x8E\xA3\x43\xAB\x0F\x18\x7C"
+ "\x1C\x74\xFB\xF0\xCC\xEF\x32\xA6\x6C\xA3\xA7\x86\x05\xB4\x77\x4E\xC3\xDC\x72\x1D"
+ "\x87\x78\xF0\x46\x87\xCC\x3A\x78\x56\x98\xA3\xBA\x77\x8F\x1A\x60\xEE\xB1\xC7\x74"
+ "\xFB\xF1\xC8\x7D\x9D\xE3\xA1\x19\xD8\x42\xD9\xF0\xF8\x7D\x9F\x67\x78\xF6\x82\x55"
+ "\x03\x43\xC1\xEE\x1E\x04\x5C\x44\x10\xB2\x93\xEC\xEA\x3E\xCE\xF1\xE3\x3C\x7C\x3D"
+ "\x86";
+#define HTTP_SCRIPT_TEMPLATE Decompress(HTTP_SCRIPT_TEMPLATE_COMPRESSED,HTTP_SCRIPT_TEMPLATE_SIZE).c_str()
+#else
const char HTTP_SCRIPT_TEMPLATE[] PROGMEM =
"function ld(u,f){"
"var x=new XMLHttpRequest();"
@@ -286,6 +479,8 @@ const char HTTP_SCRIPT_TEMPLATE[] PROGMEM =
"}"
"g=o.shift().split(',');" // GPIO - Array separator
"os=\""; // }2'0'>None (0)}3}2'17'>Button1 (17)}3...
+#endif //USE_UNISHOX_COMPRESSION
+
const char HTTP_SCRIPT_TEMPLATE2[] PROGMEM =
"j=0;"
"for(i=0;i<" STR(MAX_USER_PINS) ";i++){" // Supports 13 GPIOs
@@ -333,6 +528,19 @@ const char HTTP_SCRIPT_INFO_END[] PROGMEM =
"}"
"wl(i);";
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_HEAD_LAST_SCRIPT_SIZE = 226;
+const char HTTP_HEAD_LAST_SCRIPT_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x46\xB1\x0E\xE9\xDE\x3D\xA6\x77\xF5\x47\xC3\x8C\xEA\x2D\x3E"
+ "\x09\x81\x8B\x1A\xFA\x8E\x86\xA1\x6F\xE6\x45\xE6\x13\x0E\xB3\xE5\x61\x04\x77\x4F"
+ "\xBD\xE1\x82\xE8\xEA\x1C\x2E\xAB\x38\xEA\xA6\x6C\xAB\xFB\xB3\xAB\xCC\x26\x1D\x1F"
+ "\x67\x78\xF0\x3E\x2B\x42\x67\x77\x4E\x81\x3E\x1E\xA1\x47\xE1\xF2\x8E\xF1\xED\xD3"
+ "\x07\x77\x4F\x7A\x8F\x7C\xEF\x1E\xDD\x3D\xEA\x3D\xF3\xDE\x3E\xFA\xC6\xB3\xEC\xF7"
+ "\xCF\x87\x77\x4F\x7A\x8F\x7C\xE8\x2A\x2B\xFC\x57\x55\xFD\x1C\x2E\x99\xDD\x3E\xF4"
+ "\x43\xEC\xEF\x1F\xA3\xF4\x77\x4F\xE0\x27\x57\xEB\x1A\xCF\xB3\xBC\x77\x8E\xF1\xDA"
+ "\x04\x1C\x87\x44\x3E\xCF\x7C\xF3\x04\x7C\xB0\xF0\x7B\xA8\xED\x9D\xB3\xC1\xEE\x3D"
+ "\xC3\xE1\x1D\xD3\x58\x87\x78\xF0\x7A\x1D\x9E\x0F\xFA\x32\x8F\xC3";
+#define HTTP_HEAD_LAST_SCRIPT Decompress(HTTP_HEAD_LAST_SCRIPT_COMPRESSED,HTTP_HEAD_LAST_SCRIPT_SIZE).c_str()
+#else
const char HTTP_HEAD_LAST_SCRIPT[] PROGMEM =
"function jd(){" // Add label name='' based on provided id=''
"var t=0,i=document.querySelectorAll('input,button,textarea,select');"
@@ -345,7 +553,29 @@ const char HTTP_HEAD_LAST_SCRIPT[] PROGMEM =
"}"
"wl(jd);" // Add name='' to any id='' in input,button,textarea,select
"";
+#endif //USE_UNISHOX_COMPRESSION
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_HEAD_STYLE1_SIZE = 591;
+const char HTTP_HEAD_STYLE1_COMPRESSED[] PROGMEM = "\x3D\x3D\x46\x41\x33\xF0\x4D\x33\x3A\x8C\x6B\x08\x4F\x3A\x3A\xB7\x86\x0B\xA3\xAB"
+ "\xCC\x26\x1D\x1E\xD1\x96\x20\x9B\xC3\xC7\x99\xCD\x21\x86\xC3\xC1\x8C\xEA\x3A\xFD"
+ "\xA6\xD6\x79\x9C\x84\xC6\x9E\x0F\x70\x21\xE1\xA7\xB4\x75\x86\x68\x3D\xFC\x17\xC2"
+ "\x1E\x67\x91\xF4\x71\xF1\x1B\x0F\x07\xB8\x61\xED\x1B\x7F\x1E\xDE\x3C\xCE\x33\xA6"
+ "\x93\x1A\x8E\x33\xC1\xEE\x2D\xE1\x82\xE8\xF6\x8F\xE8\x94\x28\xF3\x39\x1B\x3E\x8F"
+ "\xA3\xC1\x0E\xC3\x61\xD7\xED\x36\xEF\x0F\x1E\x63\xB3\xE2\x3F\x9D\x63\xB0\xD8\x78"
+ "\x3A\xC7\xD8\xE3\x4D\xA3\xAC\x14\xAD\x0D\xC3\x68\x29\x57\x04\xCD\x84\x3C\x0B\x3E"
+ "\x08\x7B\x6E\xF0\xC1\x74\x7B\xD4\x64\x31\x9F\x03\x14\xC3\x34\x1D\x86\xC3\xDF\x04"
+ "\x1E\x11\x41\x06\x8F\xEC\x4D\xC3\xDF\x04\x3D\xF1\x8D\x3C\x02\x0F\x03\x87\x5F\xF4"
+ "\x78\x55\x1E\x67\x38\x86\x1B\x0F\x06\x6F\xF5\xA1\xD8\x47\x5D\x85\xA3\xDC\x79\x9D"
+ "\x67\x21\x0C\x04\x9C\xCF\xF7\xC3\xCC\x10\xF1\xE3\x89\x1F\x47\xD1\xE0\xF7\x10\x21"
+ "\x71\x3E\x09\x1C\x28\x82\xC7\x2A\x01\x54\xCD\x95\x7F\x76\x7B\x7E\xFD\xA6\xD6\x79"
+ "\x82\x1E\xA0\x78\x04\x2C\xC8\xE7\xCF\xA3\xE8\xF0\x42\x9E\x8F\x0A\xA3\xCC\xE5\xCF"
+ "\x90\xC3\x61\xE0\x11\xF8\xFA\xC3\x37\xF3\x01\x60\xF9\xE7\x62\xEB\x01\x6B\x45\x1D"
+ "\x82\x19\x1E\xDA\x66\xCA\x04\x2E\x0A\x83\x7D\x4F\xE0\x83\xC9\xE9\x8B\x1B\xA1\x19"
+ "\x1E\x66\x6F\xE2\x5F\x59\xD5\xEB\xEF\x1D\x7E\x7F\xD3\x2A\x01\x9B\x98\x1E\xEA\x10"
+ "\x11\x39\x7D\x38\xC8\x61\xB0\xF0\x7B\x8D";
+#define HTTP_HEAD_STYLE1 Decompress(HTTP_HEAD_STYLE1_COMPRESSED,HTTP_HEAD_STYLE1_SIZE).c_str()
+#else
const char HTTP_HEAD_STYLE1[] PROGMEM =
""
@@ -847,7 +1098,11 @@ void WSContentStart_P(const char* title, bool auth)
WSContentBegin(200, CT_HTML);
if (title != nullptr) {
+#ifdef USE_UNISHOX_COMPRESSION
+ WSContentSend_P(HTTP_HEADER1, D_HTML_LANGUAGE, SettingsText(SET_DEVICENAME), title);
+#else
WSContentSend_P(HTTP_HEADER1, SettingsText(SET_DEVICENAME), title);
+#endif //USE_UNISHOX_COMPRESSION
}
}
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index 888fa3cb8..5a79d50b5 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -1975,6 +1975,19 @@ chknext:
fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(PMEM_SIZE);
goto exit;
}
+ if (!strncmp(vname,"rnd(",4)) {
+ // tasmota switch state
+ GetNumericResult(vname+4,OPER_EQU,&fvar,0);
+ if (fvar<0) {
+ randomSeed(-fvar);
+ fvar=0;
+ } else {
+ fvar=random(fvar);
+ }
+ // skip ] bracket
+ len++;
+ goto exit;
+ }
break;
case 's':
if (!strncmp(vname,"secs",4)) {
@@ -3786,6 +3799,76 @@ const char HTTP_FORM_SDC_HREF[] PROGMEM =
#endif
+uint8_t *script_ex_ptr;
+uint16_t uplsize;
+uint8_t sc_state;
+
+// upload script and start immediately
+void script_upload_start(void) {
+
+ //AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: file upload execute"));
+
+ HTTPUpload& upload = Webserver->upload();
+ if (upload.status == UPLOAD_FILE_START) {
+ //AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload start"));
+ script_ex_ptr=(uint8_t*)glob_script_mem.script_ram;
+ //AddLog_P2(LOG_LEVEL_INFO, PSTR("HTP: upload file %s, %d"),upload.filename.c_str(),upload.totalSize);
+
+ if (strcmp(upload.filename.c_str(),"execute_script")) {
+ Web.upload_error=1;
+ WSSend(500, CT_PLAIN, F("500: wrong filename"));
+ return;
+ }
+ if (upload.totalSize>=glob_script_mem.script_size) {
+ Web.upload_error=1;
+ WSSend(500, CT_PLAIN, F("500: file to large"));
+ return;
+ }
+ uplsize=0;
+
+ sc_state = bitRead(Settings.rule_enabled, 0);
+ bitWrite(Settings.rule_enabled,0,0);
+
+ } else if(upload.status == UPLOAD_FILE_WRITE) {
+ //AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload write"));
+ uint32_t csiz=upload.currentSize;
+ uint32_t tsiz=glob_script_mem.script_size-1;
+ if (uplsize" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS));
+ WSContentSend_P(PSTR("
"));
+ WSContentSend_P(PSTR(""),"/upl",D_UPL_DONE);
+ //WSContentSpaceButton(BUTTON_MAIN);
+ WSContentStop();
+ }
+
WSContentStart_P(S_SCRIPT_FILE_UPLOAD);
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR);
@@ -3931,18 +4025,6 @@ void Script_FileUploadConfiguration(void)
File upload_file;
-void ScriptFileUploadSuccess(void) {
- WSContentStart_P(S_INFORMATION);
- WSContentSendStyle();
- WSContentSend_P(PSTR("" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS));
- WSContentSend_P(PSTR("
"));
- WSContentSend_P(PSTR(""),"/upl",D_UPL_DONE);
- //WSContentSpaceButton(BUTTON_MAIN);
- WSContentStop();
-}
-
-
void script_upload(void) {
@@ -4090,8 +4172,31 @@ void HandleScriptConfiguration(void) {
WSContentSend_P(HTTP_SCRIPT_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentStop();
- }
+}
+void SaveScript(void) {
+
+#ifdef EEP_SCRIPT_SIZE
+ if (glob_script_mem.flags&1) {
+ EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram);
+ }
+#endif // EEP_SCRIPT_SIZE
+
+#ifdef USE_SCRIPT_FATFS
+ if (glob_script_mem.flags&1) {
+ fsp->remove(FAT_SCRIPT_NAME);
+ File file=fsp->open(FAT_SCRIPT_NAME,FILE_WRITE);
+ file.write((const uint8_t*)glob_script_mem.script_ram,FAT_SCRIPT_SIZE);
+ file.close();
+ }
+#endif // USE_SCRIPT_FATFS
+
+#ifdef LITTLEFS_SCRIPT_SIZE
+ if (glob_script_mem.flags&1) {
+ SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,LITTLEFS_SCRIPT_SIZE);
+ }
+#endif // LITTLEFS_SCRIPT_SIZE
+}
void ScriptSaveSettings(void) {
@@ -4148,29 +4253,14 @@ void ScriptSaveSettings(void) {
bitWrite(Settings.rule_enabled, 0, 0);
}
+ SaveScript();
-#ifdef EEP_SCRIPT_SIZE
- if (glob_script_mem.flags&1) {
- EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram);
- }
-#endif // EEP_SCRIPT_SIZE
-
-#ifdef USE_SCRIPT_FATFS
- if (glob_script_mem.flags&1) {
- fsp->remove(FAT_SCRIPT_NAME);
- File file=fsp->open(FAT_SCRIPT_NAME,FILE_WRITE);
- file.write((const uint8_t*)glob_script_mem.script_ram,FAT_SCRIPT_SIZE);
- file.close();
- }
-#endif // USE_SCRIPT_FATFS
-
-#ifdef LITTLEFS_SCRIPT_SIZE
- if (glob_script_mem.flags&1) {
- SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,LITTLEFS_SCRIPT_SIZE);
- }
-#endif // LITTLEFS_SCRIPT_SIZE
}
+ SaveScriptEnd();
+}
+
+void SaveScriptEnd(void) {
if (glob_script_mem.script_mem) {
Scripter_save_pvars();
free(glob_script_mem.script_mem);
@@ -4183,7 +4273,7 @@ void ScriptSaveSettings(void) {
uint32_t len_compressed = SCRIPT_COMPRESS(glob_script_mem.script_ram, strlen(glob_script_mem.script_ram), Settings.rules[0], MAX_SCRIPT_SIZE-1);
if (len_compressed > 0) {
Settings.rules[0][len_compressed] = 0;
- AddLog_P2(LOG_LEVEL_INFO,PSTR("script compressed to %d %%"),len_compressed * 100 / strlen(glob_script_mem.script_ram));
+ AddLog_P2(LOG_LEVEL_INFO,PSTR("script compressed to %d bytes = %d %%"),len_compressed,len_compressed * 100 / strlen(glob_script_mem.script_ram));
} else {
AddLog_P2(LOG_LEVEL_INFO, PSTR("script compress error: %d"), len_compressed);
}
@@ -4204,8 +4294,6 @@ void ScriptSaveSettings(void) {
#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT)
-
-
#define HUE_DEV_MVNUM 5
#define HUE_DEV_NSIZE 16
struct HUE_SCRIPT {
@@ -5738,7 +5826,9 @@ void script_task1(void *arg) {
//if (timet1",3,0);
+ if (bitRead(Settings.rule_enabled, 0)) {
+ Run_Scripter(">t1",3,0);
+ }
}
}
@@ -5752,7 +5842,9 @@ void script_task2(void *arg) {
//if (timet2",3,0);
+ if (bitRead(Settings.rule_enabled, 0)) {
+ Run_Scripter(">t2",3,0);
+ }
}
}
uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) {
@@ -5997,6 +6089,8 @@ bool Xdrv10(uint8_t function)
case FUNC_WEB_ADD_HANDLER:
Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration);
Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration);
+ Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);},script_upload_start);
+ Webserver->on("/exs", HTTP_GET,ScriptExecuteUploadSuccess);
#ifdef USE_SCRIPT_FATFS
Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);},script_upload);
diff --git a/tasmota/xnrg_14_bl0940.ino b/tasmota/xnrg_14_bl0940.ino
index 99a738e81..655f3d8df 100644
--- a/tasmota/xnrg_14_bl0940.ino
+++ b/tasmota/xnrg_14_bl0940.ino
@@ -29,7 +29,9 @@
#define XNRG_14 14
-#define BL0940_PULSES_NOT_INITIALIZED -1
+#define BL0940_PREF 1430
+#define BL0940_UREF 33000
+#define BL0940_IREF 2750
#define BL0940_BUFFER_SIZE 36
@@ -42,6 +44,7 @@
#define BL0940_READ_COMMAND 0x50 // 0x58 according to documentation
#define BL0940_FULL_PACKET 0xAA
+
#define BL0940_PACKET_HEADER 0x55 // 0x58 according to documentation
#include
@@ -52,14 +55,12 @@ struct BL0940 {
long voltage = 0;
long current = 0;
long power = 0;
- long power_cycle_first = 0;
- long cf_pulses = 0;
- long cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED;
+// long power_cycle_first = 0;
+// long cf_pulses = 0;
float temperature;
int byte_counter = 0;
uint8_t *rx_buffer = nullptr;
- uint8_t power_invalid = 0;
bool received = false;
} Bl0940;
@@ -85,9 +86,10 @@ void Bl0940Received(void) {
Bl0940.voltage = Bl0940.rx_buffer[12] << 16 | Bl0940.rx_buffer[11] << 8 | Bl0940.rx_buffer[10];
Bl0940.current = Bl0940.rx_buffer[6] << 16 | Bl0940.rx_buffer[5] << 8 | Bl0940.rx_buffer[4];
Bl0940.power = Bl0940.rx_buffer[18] << 16 | Bl0940.rx_buffer[17] << 8 | Bl0940.rx_buffer[16];
- Bl0940.cf_pulses = Bl0940.rx_buffer[24] << 16 | Bl0940.rx_buffer[23] << 8 | Bl0940.rx_buffer[22];
+// Bl0940.cf_pulses = Bl0940.rx_buffer[24] << 16 | Bl0940.rx_buffer[23] << 8 | Bl0940.rx_buffer[22];
uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28];
- Bl0940.temperature = ((170.0f/448.0f)*(((float)tps1/2.0f)-32.0f))-45.0f;
+ float t = ((170.0f/448.0f)*(((float)tps1/2.0f)-32.0f))-45.0f;
+ Bl0940.temperature = ConvertTemp(t);
if (Energy.power_on) { // Powered on
Energy.voltage[0] = (float)Bl0940.voltage / Settings.energy_voltage_calibration;
@@ -99,7 +101,7 @@ void Bl0940Received(void) {
Energy.current[0] = 0;
}
} else { // Powered off
- Bl0940.power_cycle_first = 0;
+// Bl0940.power_cycle_first = 0;
Energy.voltage[0] = 0;
Energy.active_power[0] = 0;
Energy.current[0] = 0;
@@ -110,7 +112,7 @@ bool Bl0940SerialInput(void) {
while (Bl0940Serial->available()) {
yield();
uint8_t serial_in_byte = Bl0940Serial->read();
- if (!Bl0940.received && (BL0940_PACKET_HEADER == serial_in_byte)) { // Packet header
+ if (!Bl0940.received && (BL0940_PACKET_HEADER == serial_in_byte)) {
Bl0940.received = true;
Bl0940.byte_counter = 0;
}
@@ -129,12 +131,13 @@ bool Bl0940SerialInput(void) {
Bl0940.received = false;
return true;
} else {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE));
+// AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE));
do { // Sync buffer with data (issue #1907 and #3425)
memmove(Bl0940.rx_buffer, Bl0940.rx_buffer +1, BL0940_BUFFER_SIZE -1);
Bl0940.byte_counter--;
} while ((Bl0940.byte_counter > 1) && (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0]));
if (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0]) {
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE));
Bl0940.received = false;
Bl0940.byte_counter = 0;
}
@@ -171,9 +174,9 @@ void Bl0940SnsInit(void) {
ClaimSerial();
}
if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) {
- Settings.energy_voltage_calibration = 33003;
- Settings.energy_current_calibration = 2243;
- Settings.energy_power_calibration = 1414;
+ Settings.energy_voltage_calibration = BL0940_UREF;
+ Settings.energy_current_calibration = BL0940_IREF;
+ Settings.energy_power_calibration = BL0940_PREF;
}
for (uint32_t i = 0; i < 5; i++) {
diff --git a/tasmota/xsns_45_vl53l0x.ino b/tasmota/xsns_45_vl53l0x.ino
index ef9de345c..f3a9bbe74 100644
--- a/tasmota/xsns_45_vl53l0x.ino
+++ b/tasmota/xsns_45_vl53l0x.ino
@@ -1,5 +1,5 @@
/*
- xsns_45_vl53l0x.ino - VL53L0X support for Tasmota
+ xsns_45_vl53l0x.ino - VL53L0X time of flight sensor support for Tasmota
Copyright (C) 2020 Theo Arends and Gerhard Mutz
@@ -19,23 +19,30 @@
#ifdef USE_I2C
#ifdef USE_VL53L0X
+/*********************************************************************************************\
+ * VL53L0x time of flight sensor
+ *
+ * I2C Addres: 0x29
+\*********************************************************************************************/
-#define XSNS_45 45
-#define XI2C_31 31 // See I2CDEVICES.md
+#define XSNS_45 45
+#define XI2C_31 31 // See I2CDEVICES.md
#include
#include "VL53L0X.h"
VL53L0X sensor;
-uint8_t vl53l0x_ready = 0;
-uint16_t vl53l0x_distance;
-uint16_t Vl53l0_buffer[5];
-uint8_t Vl53l0_index;
+struct {
+ uint16_t distance;
+ uint16_t distance_prev;
+ uint16_t buffer[5];
+ uint8_t ready = 0;
+ uint8_t index;
+} Vl53l0x;
/********************************************************************************************/
-void Vl53l0Detect(void)
-{
+void Vl53l0Detect(void) {
if (!I2cSetDevice(0x29)) { return; }
if (!sensor.init()) { return; }
@@ -49,9 +56,9 @@ void Vl53l0Detect(void)
// instead, provide a desired inter-measurement period in
// ms (e.g. sensor.startContinuous(100)).
sensor.startContinuous();
- vl53l0x_ready = 1;
+ Vl53l0x.ready = 1;
- Vl53l0_index=0;
+ Vl53l0x.index = 0;
}
#ifdef USE_WEBSERVER
@@ -61,50 +68,64 @@ const char HTTP_SNS_VL53L0X[] PROGMEM =
#define USE_VL_MEDIAN
-void Vl53l0Every_250MSecond(void)
-{
- uint16_t tbuff[5],tmp;
- uint8_t flag;
-
+void Vl53l0Every_250MSecond(void) {
// every 200 ms
uint16_t dist = sensor.readRangeContinuousMillimeters();
- if (dist==0 || dist>2000) {
- dist=9999;
+ if ((0 == dist) || (dist > 2000)) {
+ dist = 9999;
}
#ifdef USE_VL_MEDIAN
// store in ring buffer
- Vl53l0_buffer[Vl53l0_index]=dist;
- Vl53l0_index++;
- if (Vl53l0_index>=5) Vl53l0_index=0;
+ Vl53l0x.buffer[Vl53l0x.index] = dist;
+ Vl53l0x.index++;
+ if (Vl53l0x.index >= 5) {
+ Vl53l0x.index = 0;
+ }
// sort list and take median
- memmove(tbuff,Vl53l0_buffer,sizeof(tbuff));
- for (byte ocnt=0; ocnt<5; ocnt++) {
- flag=0;
- for (byte count=0; count<4; count++) {
- if (tbuff[count]>tbuff[count+1]) {
- tmp=tbuff[count];
- tbuff[count]=tbuff[count+1];
- tbuff[count+1]=tmp;
- flag=1;
+ uint16_t tbuff[5];
+ memmove(tbuff, Vl53l0x.buffer, sizeof(tbuff));
+ uint16_t tmp;
+ uint8_t flag;
+ for (uint32_t ocnt = 0; ocnt < 5; ocnt++) {
+ flag = 0;
+ for (uint32_t count = 0; count < 4; count++) {
+ if (tbuff[count] > tbuff[count +1]) {
+ tmp = tbuff[count];
+ tbuff[count] = tbuff[count +1];
+ tbuff[count +1] = tmp;
+ flag = 1;
}
}
- if (!flag) break;
+ if (!flag) { break; }
}
- vl53l0x_distance=tbuff[2];
+ Vl53l0x.distance = tbuff[2];
#else
- vl53l0x_distance=dist;
+ Vl53l0x.distance = dist;
#endif
}
-void Vl53l0Show(boolean json)
-{
+#ifdef USE_DOMOTICZ
+void Vl53l0Every_Second(void) {
+ if (abs(Vl53l0x.distance - Vl53l0x.distance_prev) > 8) {
+ Vl53l0x.distance_prev = Vl53l0x.distance;
+ DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x.distance);
+ }
+}
+#endif // USE_DOMOTICZ
+
+void Vl53l0Show(boolean json) {
if (json) {
- ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l0x_distance);
+ ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), Vl53l0x.distance);
+#ifdef USE_DOMOTICZ
+ if (0 == tele_period) {
+ DomoticzSensor(DZ_ILLUMINANCE, Vl53l0x.distance);
+ }
+#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
- WSContentSend_PD(HTTP_SNS_VL53L0X, vl53l0x_distance);
+ WSContentSend_PD(HTTP_SNS_VL53L0X, Vl53l0x.distance);
#endif
}
}
@@ -122,11 +143,16 @@ bool Xsns45(byte function)
if (FUNC_INIT == function) {
Vl53l0Detect();
}
- else if (vl53l0x_ready) {
+ else if (Vl53l0x.ready) {
switch (function) {
case FUNC_EVERY_250_MSECOND:
Vl53l0Every_250MSecond();
break;
+#ifdef USE_DOMOTICZ
+ case FUNC_EVERY_SECOND:
+ Vl53l0Every_Second();
+ break;
+#endif // USE_DOMOTICZ
case FUNC_JSON_APPEND:
Vl53l0Show(1);
break;
diff --git a/tools/unishox/clipboard-const-converter.py b/tools/unishox/clipboard-const-converter.py
new file mode 100644
index 000000000..ee180d8a9
--- /dev/null
+++ b/tools/unishox/clipboard-const-converter.py
@@ -0,0 +1,112 @@
+from tkinter import Tk
+import unishox
+
+# get text from clipboard expecting something like that:
+# const char HTTP_SCRIPT_WIFI[] PROGMEM =
+# "function c(l){" // comments
+# "eb('s1').value=l.innerText||l.textContent;" // comments
+# "eb('p1').focus();" // comments
+# // comments
+# "}";
+
+text = Tk().clipboard_get()
+# print(text)
+
+# parsing and cleaning
+text_list = text.splitlines()
+text = '' #just reuse the string
+const_name = '' #default if no name will be found
+
+line_number = 0
+for line in text_list:
+ pos = line.find("const char")
+ # print(pos, line)
+ if pos > -1:
+ line_list = line.rsplit(" ")
+ for el in line_list:
+ if el.find('[]') > -1:
+ const_name = el[:-2] #extract the "const char" variable name
+ line_list.pop(line_number)
+ else: # remove line comments
+ line_el = line.rsplit("//")
+ # print('Splitted line list by //' % line_el)
+ # print(line_el[0])
+ text = text + line_el[0]
+ line_number = line_number +1
+
+# print const_name
+# print text
+
+#remove unwanted quotation marks
+qm = []
+pos =0
+last_char = ""
+for char in text:
+ if char == "\"":
+ if last_char != "\\":
+ qm.append(pos) #find all quotation marks without preceding backslash
+ last_char = char
+ pos = pos + 1
+# print(qm)
+lastel = 0
+input = ""
+for pos in qm:
+ sub = text[lastel+1:pos:]
+ if not sub.isspace() and pos-lastel > 1:
+ # print(lastel, pos)
+ input = input + sub #only copy substrings that are not whitespace
+ # print(text[lastel+1:pos:])
+ lastel = pos
+
+print("####### Parsing intput:")
+print("Const char name: ",const_name)
+print('####### Cleaned input:')
+print(input)
+
+#construct output (taken from shadinger)
+input = input.replace("\\t", "\t")
+input = input.replace("\\n", "\n")
+input = input.replace("\\r", "\r")
+input = input.replace("\\f", "\f")
+input = input.replace("\\b", "\b")
+input = input.replace("\\\"", u"\u0022")
+
+in_bytes = bytearray(input, 'utf-8')
+in_len = len(in_bytes)
+out_bytes = bytearray(in_len * 2)
+
+UNISHOX = unishox.Unishox()
+out_len = UNISHOX.compress(in_bytes, len(in_bytes), out_bytes, len(out_bytes))
+print("####### Compression result:")
+print("Compressed from {i} to {o}, -{p:.1f}%".format(i=in_len, o=out_len, p=(100-out_len/in_len*100)))
+out_bytes = out_bytes[:out_len] # truncate to right size
+
+#PROGMEM is growing in steps 0,8,24,40,56,... bytes of data resulting in size of 0,16,32,48,64,... bytes
+for in_real in range(8,in_len+16,16):
+ if in_real>=in_len:
+ print("Old real PROGMEM-size:",in_real+8,"(unused bytes:",in_real-in_len,")")
+ break
+for out_real in range(8,out_len+16,16):
+ if out_real>=out_len:
+ print("New real PROGMEM-size:",out_real+8,"(unused bytes:",out_real-out_len,")")
+ break
+print("the optimal case would be raw bytes + 8, real difference: ", in_real - out_real, "bytes")
+# https://www.geeksforgeeks.org/break-list-chunks-size-n-python/
+def chunked(my_list, n):
+ return [my_list[i * n:(i + 1) * n] for i in range((len(my_list) + n - 1) // n )]
+
+# split in chunks of 20 characters
+chunks = chunked(out_bytes, 20)
+
+lines_raw = [ "\"\\x" + "\\x".join( [ '{:02X}'.format(b) for b in chunk ] ) + "\"" for chunk in chunks ]
+line_complete = "const char " + const_name + "_COMPRESSED" +"[] PROGMEM = " + ("\n" + " "*29).join(lines_raw) + ";"
+lines = "const size_t " + const_name +"_SIZE = {size};\n{lines}".format(size=in_len, lines=line_complete)
+
+print('####### Final output:')
+print(lines)
+
+definition = "#define " + const_name + " Decompress(" + const_name + "_COMPRESSED" + "," + const_name +"_SIZE" + ").c_str()"
+print(definition)
+
+
+# maybe add export to clipboard for later ...
\ No newline at end of file
diff --git a/tools/unishox/unishox.py b/tools/unishox/unishox.py
new file mode 100644
index 000000000..1cce14706
--- /dev/null
+++ b/tools/unishox/unishox.py
@@ -0,0 +1,520 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Python Class for compressing short strings.
+
+This class contains a highly modified and optimized version of Unishox
+for Tasmota converted in C ported to Pyhton3.
+
+It was basically developed to individually compress and decompress small strings
+(see https://github.com/siara-cc/Unishox)
+In general compression utilities such as zip, gzip do not compress short strings
+well and often expand them. They also use lots of memory which makes them unusable
+in constrained environments like Arduino.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+class Unishox:
+ """
+ This is a highly modified and optimized version of Unishox
+ for Tasmota, aimed at compressing `Rules` which are typically
+ short strings from 50 to 500 bytes.
+
+ @author Stephan Hadinger
+ @revised Norbert Richter
+ """
+
+ # pylint: disable=bad-continuation,bad-whitespace,line-too-long
+ #cl_95 = [0x4000 + 3, 0x3F80 + 11, 0x3D80 + 11, 0x3C80 + 10, 0x3BE0 + 12, 0x3E80 + 10, 0x3F40 + 11, 0x3EC0 + 10, 0x3BA0 + 11, 0x3BC0 + 11, 0x3D60 + 11, 0x3B60 + 11, 0x3A80 + 10, 0x3AC0 + 10, 0x3A00 + 9, 0x3B00 + 10, 0x38C0 + 10, 0x3900 + 10, 0x3940 + 11, 0x3960 + 11, 0x3980 + 11, 0x39A0 + 11, 0x39C0 + 11, 0x39E0 + 12, 0x39F0 + 12, 0x3880 + 10, 0x3CC0 + 10, 0x3C00 + 9, 0x3D00 + 10, 0x3E00 + 9, 0x3F00 + 10, 0x3B40 + 11, 0x3BF0 + 12, 0x2B00 + 8, 0x21C0 + 11, 0x20C0 + 10, 0x2100 + 10, 0x2600 + 7, 0x2300 + 11, 0x21E0 + 12, 0x2140 + 11, 0x2D00 + 8, 0x2358 + 13, 0x2340 + 12, 0x2080 + 10, 0x21A0 + 11, 0x2E00 + 8, 0x2C00 + 8, 0x2180 + 11, 0x2350 + 13, 0x2F80 + 9, 0x2F00 + 9, 0x2A00 + 8, 0x2160 + 11, 0x2330 + 12, 0x21F0 + 12, 0x2360 + 13, 0x2320 + 12, 0x2368 + 13, 0x3DE0 + 12, 0x3FA0 + 11, 0x3DF0 + 12, 0x3D40 + 11, 0x3F60 + 11, 0x3FF0 + 12, 0xB000 + 4, 0x1C00 + 7, 0x0C00 + 6, 0x1000 + 6, 0x6000 + 3, 0x3000 + 7, 0x1E00 + 8, 0x1400 + 7, 0xD000 + 4, 0x3580 + 9, 0x3400 + 8, 0x0800 + 6, 0x1A00 + 7, 0xE000 + 4, 0xC000 + 4, 0x1800 + 7, 0x3500 + 9, 0xF800 + 5, 0xF000 + 5, 0xA000 + 4, 0x1600 + 7, 0x3300 + 8, 0x1F00 + 8, 0x3600 + 9, 0x3200 + 8, 0x3680 + 9, 0x3DA0 + 11, 0x3FC0 + 11, 0x3DC0 + 11, 0x3FE0 + 12]
+ cl_95 = [0x4000 + 3, 0x3F80 + 11, 0x3D80 + 11, 0x3C80 + 10, 0x3BE0 + 12, 0x3E80 + 10, 0x3F40 + 11, 0x3EC0 + 10, 0x3BA0 + 11, 0x3BC0 + 11, 0x3D60 + 11, 0x3B60 + 11, 0x3A80 + 10, 0x3AC0 + 10, 0x3A00 + 9, 0x3B00 + 10, 0x38C0 + 10, 0x3900 + 10, 0x3940 + 11, 0x3960 + 11, 0x3980 + 11, 0x39A0 + 11, 0x39C0 + 11, 0x39E0 + 12, 0x39F0 + 12, 0x3880 + 10, 0x3CC0 + 10, 0x3C00 + 9, 0x3D00 + 10, 0x3E00 + 9, 0x3F00 + 10, 0x3B40 + 11, 0x3BF0 + 12, 0x2B00 + 8, 0x21C0 + 11, 0x20C0 + 10, 0x2100 + 10, 0x2600 + 7, 0x2300 + 11, 0x21E0 + 12, 0x2140 + 11, 0x2D00 + 8, 0x46B0 + 13, 0x2340 + 12, 0x2080 + 10, 0x21A0 + 11, 0x2E00 + 8, 0x2C00 + 8, 0x2180 + 11, 0x46A0 + 13, 0x2F80 + 9, 0x2F00 + 9, 0x2A00 + 8, 0x2160 + 11, 0x2330 + 12, 0x21F0 + 12, 0x46C0 + 13, 0x2320 + 12, 0x46D0 + 13, 0x3DE0 + 12, 0x3FA0 + 11, 0x3DF0 + 12, 0x3D40 + 11, 0x3F60 + 11, 0x3FF0 + 12, 0xB000 + 4, 0x1C00 + 7, 0x0C00 + 6, 0x1000 + 6, 0x6000 + 3, 0x3000 + 7, 0x1E00 + 8, 0x1400 + 7, 0xD000 + 4, 0x3580 + 9, 0x3400 + 8, 0x0800 + 6, 0x1A00 + 7, 0xE000 + 4, 0xC000 + 4, 0x1800 + 7, 0x3500 + 9, 0xF800 + 5, 0xF000 + 5, 0xA000 + 4, 0x1600 + 7, 0x3300 + 8, 0x1F00 + 8, 0x3600 + 9, 0x3200 + 8, 0x3680 + 9, 0x3DA0 + 11, 0x3FC0 + 11, 0x3DC0 + 11, 0x3FE0 + 12]
+
+ # enum {SHX_STATE_1 = 1, SHX_STATE_2}; // removed Unicode state
+ SHX_STATE_1 = 1
+ SHX_STATE_2 = 2
+
+ SHX_SET1 = 0
+ SHX_SET1A = 1
+ SHX_SET1B = 2
+ SHX_SET2 = 3
+
+ sets = [['\0', ' ', 'e', '\0', 't', 'a', 'o', 'i', 'n', 's', 'r'],
+ ['\0', 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'],
+ ['f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', '\0', '\0', '\0'],
+ ['\0', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'],
+ ['.', ',', '-', '/', '?', '+', ' ', '(', ')', '$', '@'],
+ [';', '#', ':', '<', '^', '*', '"', '{', '}', '[', ']'],
+ ['=', '%', '\'', '>', '&', '_', '!', '\\', '|', '~', '`']]
+
+ us_vcode = [2 + (0 << 3), 3 + (3 << 3), 3 + (1 << 3), 4 + (6 << 3), 0,
+ # 5, 6, 7, 8, 9, 10
+ 4 + (4 << 3), 3 + (2 << 3), 4 + (8 << 3), 0, 0, 0,
+ # 11, 12, 13, 14, 15
+ 4 + (7 << 3), 0, 4 + (5 << 3), 0, 5 + (9 << 3),
+ # 16, 17, 18, 19, 20, 21, 22, 23
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ # 24, 25, 26, 27, 28, 29, 30, 31
+ 0, 0, 0, 0, 0, 0, 0, 5 + (10 << 3) ]
+ # 0, 1, 2, 3, 4, 5, 6, 7,
+ us_hcode = [1 + (1 << 3), 2 + (0 << 3), 0, 3 + (2 << 3), 0, 0, 0, 5 + (3 << 3),
+ # 8, 9, 10, 11, 12, 13, 14, 15,
+ 0, 0, 0, 0, 0, 0, 0, 5 + (5 << 3),
+ # 16, 17, 18, 19, 20, 21, 22, 23
+ 0, 0, 0, 0, 0, 0, 0, 5 + (4 << 3),
+ # 24, 25, 26, 27, 28, 29, 30, 31
+ 0, 0, 0, 0, 0, 0, 0, 5 + (6 << 3) ]
+ # pylint: enable=bad-continuation,bad-whitespace
+
+ ESCAPE_MARKER = 0x2A
+
+ TERM_CODE = 0x37C0
+ # TERM_CODE_LEN = 10
+ DICT_CODE = 0x0000
+ DICT_CODE_LEN = 5
+ #DICT_OTHER_CODE = 0x0000
+ #DICT_OTHER_CODE_LEN = 6
+ RPT_CODE_TASMOTA = 0x3780
+ RPT_CODE_TASMOTA_LEN = 10
+ BACK2_STATE1_CODE = 0x2000
+ BACK2_STATE1_CODE_LEN = 4
+ #BACK_FROM_UNI_CODE = 0xFE00
+ #BACK_FROM_UNI_CODE_LEN = 8
+ LF_CODE = 0x3700
+ LF_CODE_LEN = 9
+ TAB_CODE = 0x2400
+ TAB_CODE_LEN = 7
+ ALL_UPPER_CODE = 0x2200
+ ALL_UPPER_CODE_LEN = 8
+ SW2_STATE2_CODE = 0x3800
+ SW2_STATE2_CODE_LEN = 7
+ ST2_SPC_CODE = 0x3B80
+ ST2_SPC_CODE_LEN = 11
+ BIN_CODE_TASMOTA = 0x8000
+ BIN_CODE_TASMOTA_LEN = 3
+
+ NICE_LEN = 5
+
+ mask = [0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF]
+
+ # pylint: disable=missing-function-docstring,invalid-name
+
+ # Input
+ # out = bytearray
+ def append_bits(self, out, ol, code, clen, state):
+ #print("Append bits {ol} {code} {clen} {state}".format(ol=ol, code=code, clen=clen, state=state))
+ if state == self.SHX_STATE_2:
+ # remove change state prefix
+ if (code >> 9) == 0x1C:
+ code <<= 7
+ clen -= 7
+ while clen > 0:
+ cur_bit = ol % 8
+ blen = 8 if (clen > 8) else clen
+ a_byte = (code >> 8) & self.mask[blen - 1]
+ #print("append_bits a_byte {ab} blen {blen}".format(ab=a_byte,blen=blen))
+ a_byte >>= cur_bit
+ if blen + cur_bit > 8:
+ blen = (8 - cur_bit)
+ if cur_bit == 0:
+ out[ol // 8] = a_byte
+ else:
+ out[ol // 8] |= a_byte
+ code <<= blen
+ ol += blen
+ if 0 == ol % 8: # pylint: disable=misplaced-comparison-constant
+ # we completed a full byte
+ last_c = out[(ol // 8) - 1]
+ if last_c in (0, self.ESCAPE_MARKER):
+ out[ol // 8] = 1 + last_c # increment to 0x01 or 0x2B
+ out[(ol // 8) -1] = self.ESCAPE_MARKER # replace old value with marker
+ ol += 8 # add one full byte
+ clen -= blen
+ return ol
+
+ codes = [0x82, 0xC3, 0xE5, 0xED, 0xF5] # pylint: disable=bad-whitespace
+ bit_len = [ 5, 7, 9, 12, 16] # pylint: disable=bad-whitespace
+
+ def encodeCount(self, out, ol, count):
+ #print("encodeCount ol = {ol}, count = {count}".format(ol=ol, count=count))
+ till = 0
+ base = 0
+ for i in range(len(self.bit_len)):
+ bit_len_i = self.bit_len[i]
+ till += (1 << bit_len_i)
+ if count < till:
+ codes_i = self.codes[i]
+ ol = self.append_bits(out, ol, (codes_i & 0xF8) << 8, codes_i & 0x07, 1)
+ #print("encodeCount append_bits ol = {ol}, code = {code}, len = {len}".format(ol=ol,code=(codes_i & 0xF8) << 8,len=codes_i & 0x07))
+ ol = self.append_bits(out, ol, (count - base) << (16 - bit_len_i), bit_len_i, 1)
+ #print("encodeCount append_bits ol = {ol}, code = {code}, len = {len}".format(ol=ol,code=(count - base) << (16 - bit_len_i),len=bit_len_i))
+ return ol
+ base = till
+ return ol
+
+ # Returns (int, ol, state, is_all_upper)
+ def matchOccurance(self, inn, len_, l_, out, ol, state, is_all_upper):
+ # int j, k;
+ longest_dist = 0
+ longest_len = 0
+ #for (j = l_ - self.NICE_LEN; j >= 0; j--) {
+ j = l_ - self.NICE_LEN
+ while j >= 0:
+ k = l_
+ #for (k = l_; k < len && j + k - l_ < l_; k++) {
+ while k < len_ and j + k - l_ < l_:
+ if inn[k] != inn[j + k - l_]:
+ break
+ k += 1
+ if k - l_ > self.NICE_LEN - 1:
+ match_len = k - l_ - self.NICE_LEN
+ match_dist = l_ - j - self.NICE_LEN + 1
+ if match_len > longest_len:
+ longest_len = match_len
+ longest_dist = match_dist
+ j -= 1
+
+ if longest_len:
+ #print("longest_len {ll}".format(ll=longest_len))
+ #ol_save = ol
+ if state == self.SHX_STATE_2 or is_all_upper:
+ is_all_upper = 0
+ state = self.SHX_STATE_1
+ ol = self.append_bits(out, ol, self.BACK2_STATE1_CODE, self.BACK2_STATE1_CODE_LEN, state)
+
+ ol = self.append_bits(out, ol, self.DICT_CODE, self.DICT_CODE_LEN, 1)
+ ol = self.encodeCount(out, ol, longest_len)
+ ol = self.encodeCount(out, ol, longest_dist)
+ #print("longest_len {ll} longest_dist {ld} ol {ols}-{ol}".format(ll=longest_len, ld=longest_dist, ol=ol, ols=ol_save))
+ l_ += longest_len + self.NICE_LEN
+ l_ -= 1
+
+ return l_, ol, state, is_all_upper
+ return -l_, ol, state, is_all_upper
+
+
+ def compress(self, inn, len_, out, len_out):
+ ol = 0
+ state = self.SHX_STATE_1
+ is_all_upper = 0
+ l = 0
+ while l < len_:
+ # for (l=0; l 0:
+ #print("matchOccurance l = {l} l_old = {lo}".format(l=l,lo=l_old))
+ l += 1 # for loop
+ continue
+
+ l = -l
+
+ if state == self.SHX_STATE_2: # if Set2
+ if ord(' ') <= c_in <= ord('@') or ord('[') <= c_in <= ord('`') or ord('{') <= c_in <= ord('~'):
+ pass
+ else:
+ state = self.SHX_STATE_1 # back to Set1 and lower case
+ ol = self.append_bits(out, ol, self.BACK2_STATE1_CODE, self.BACK2_STATE1_CODE_LEN, state)
+
+ is_upper = 0
+ if ord('A') <= c_in <= ord('Z'):
+ is_upper = 1
+ else:
+ if is_all_upper:
+ is_all_upper = 0
+ ol = self.append_bits(out, ol, self.BACK2_STATE1_CODE, self.BACK2_STATE1_CODE_LEN, state)
+
+ if 32 <= c_in <= 126:
+ if is_upper and not is_all_upper:
+ ll = l+5
+ # for (ll=l+5; ll>=l && ll ord('Z'):
+ break
+
+ ll -= 1
+
+ if ll == l-1:
+ ol = self.append_bits(out, ol, self.ALL_UPPER_CODE, self.ALL_UPPER_CODE_LEN, state) # CapsLock
+ is_all_upper = 1
+
+ if state == self.SHX_STATE_1 and ord('0') <= c_in <= ord('9'):
+ ol = self.append_bits(out, ol, self.SW2_STATE2_CODE, self.SW2_STATE2_CODE_LEN, state) # Switch to sticky Set2
+ state = self.SHX_STATE_2
+
+ c_in -= 32
+ if is_all_upper and is_upper:
+ c_in += 32
+ if c_in == 0 and state == self.SHX_STATE_2:
+ ol = self.append_bits(out, ol, self.ST2_SPC_CODE, self.ST2_SPC_CODE_LEN, state) # space from Set2 ionstead of Set1
+ else:
+ # ol = self.append_bits(out, ol, pgm_read_word(&c_95[c_in]), pgm_read_byte(&l_95[c_in]), state); // original version with c/l in split arrays
+ cl = self.cl_95[c_in]
+ cl_code = cl & 0xFFF0
+ cl_len = cl & 0x000F
+ if cl_len == 13:
+ cl_code = cl_code >> 1
+ ol = self.append_bits(out, ol, cl_code, cl_len, state)
+
+ elif c_in == 10:
+ ol = self.append_bits(out, ol, self.LF_CODE, self.LF_CODE_LEN, state) # LF
+ elif c_in == '\t':
+ ol = self.append_bits(out, ol, self.TAB_CODE, self.TAB_CODE_LEN, state) # TAB
+ else:
+ ol = self.append_bits(out, ol, self.BIN_CODE_TASMOTA, self.BIN_CODE_TASMOTA_LEN, state) # Binary, we reuse the Unicode marker which 3 bits instead of 9
+ ol = self.encodeCount(out, ol, (255 - c_in) & 0xFF)
+
+
+ # check that we have some headroom in the output buffer
+ if ol // 8 >= len_out - 4:
+ return -1 # we risk overflow and crash
+
+ l += 1
+
+ bits = ol % 8
+ if bits:
+ ol = self.append_bits(out, ol, self.TERM_CODE, 8 - bits, 1) # 0011 0111 1100 0000 TERM = 0011 0111 11
+ return (ol + 7) // 8
+ # return ol // 8 + 1 if (ol%8) else 0
+
+
+ def getBitVal(self, inn, bit_no, count):
+ c_in = inn[bit_no >> 3]
+ if bit_no >> 3 and self.ESCAPE_MARKER == inn[(bit_no >> 3) - 1]:
+ c_in -= 1
+ r = 1 << count if (c_in & (0x80 >> (bit_no % 8))) else 0
+ #print("getBitVal r={r}".format(r=r))
+ return r
+
+ # Returns:
+ # 0..11
+ # or -1 if end of stream
+ def getCodeIdx(self, code_type, inn, len_, bit_no_p):
+ code = 0
+ count = 0
+ while count < 5:
+ if bit_no_p >= len_:
+ return -1, bit_no_p
+ # detect marker
+ if self.ESCAPE_MARKER == inn[bit_no_p >> 3]:
+ bit_no_p += 8 # skip marker
+
+ if bit_no_p >= len_:
+ return -1, bit_no_p
+
+ code += self.getBitVal(inn, bit_no_p, count)
+ bit_no_p += 1
+ count += 1
+ code_type_code = code_type[code]
+ if code_type_code and (code_type_code & 0x07) == count:
+ #print("getCodeIdx = {r}".format(r=code_type_code >> 3))
+ return code_type_code >> 3, bit_no_p
+
+ #print("getCodeIdx not found = {r}".format(r=1))
+ return 1, bit_no_p
+
+ def getNumFromBits(self, inn, bit_no, count):
+ ret = 0
+ while count:
+ count -= 1
+ if self.ESCAPE_MARKER == inn[bit_no >> 3]:
+ bit_no += 8 # skip marker
+ ret += self.getBitVal(inn, bit_no, count)
+ bit_no += 1
+ return ret
+
+ def readCount(self, inn, bit_no_p, len_):
+ (idx, bit_no_p) = self.getCodeIdx(self.us_hcode, inn, len_, bit_no_p)
+ if idx >= 1:
+ idx -= 1 # we skip v = 1 (code '0') since we no more accept 2 bits encoding
+ if idx >= 5 or idx < 0:
+ return 0, bit_no_p # unsupported or end of stream
+ till = 0
+ bit_len_idx = 0
+ base = 0
+ #for (uint32_t i = 0; i <= idx; i++) {
+ i = 0
+ while i <= idx:
+ # for i in range(idx):
+ base = till
+ bit_len_idx = self.bit_len[i]
+ till += (1 << bit_len_idx)
+ i += 1
+
+ count = self.getNumFromBits(inn, bit_no_p, bit_len_idx) + base
+ #print("readCount getNumFromBits = {count} ({bl})".format(count=count,bl=bit_len_idx))
+
+ bit_no_p += bit_len_idx
+ return count, bit_no_p
+
+ def decodeRepeat(self, inn, len_, out, ol, bit_no):
+ #print("decodeRepeat Enter")
+ (dict_len, bit_no) = self.readCount(inn, bit_no, len_)
+ dict_len += self.NICE_LEN
+ (dist, bit_no) = self.readCount(inn, bit_no, len_)
+ dist += self.NICE_LEN - 1
+ #memcpy(out + ol, out + ol - dist, dict_len);
+ i = 0
+ while i < dict_len:
+ #for i in range(dict_len):
+ out[ol + i] = out[ol - dist + i]
+ i += 1
+ ol += dict_len
+
+ return ol, bit_no
+
+ def decompress(self, inn, len_, out, len_out):
+ ol = 0
+ bit_no = 0
+ dstate = self.SHX_SET1
+ is_all_upper = 0
+
+ len_ <<= 3 # *8, len_ in bits
+ out[ol] = 0
+ while bit_no < len_:
+ c = 0
+ is_upper = is_all_upper
+ (v, bit_no) = self.getCodeIdx(self.us_vcode, inn, len_, bit_no) # read vCode
+ #print("bit_no {b}. v = {v}".format(b=bit_no,v=v))
+ if v < 0:
+ break # end of stream
+ h = dstate # Set1 or Set2
+ if v == 0: # Switch which is common to Set1 and Set2, first entry
+ (h, bit_no) = self.getCodeIdx(self.us_hcode, inn, len_, bit_no) # read hCode
+ #print("bit_no {b}. h = {h}".format(b=bit_no,h=h))
+ if h < 0:
+ break # end of stream
+ if h == self.SHX_SET1: # target is Set1
+ if dstate == self.SHX_SET1: # Switch from Set1 to Set1 us UpperCase
+ if is_all_upper: # if CapsLock, then back to LowerCase
+ is_upper = 0
+ is_all_upper = 0
+ continue
+
+ (v, bit_no) = self.getCodeIdx(self.us_vcode, inn, len_, bit_no) # read again vCode
+ if v < 0:
+ break # end of stream
+ if v == 0:
+ (h, bit_no) = self.getCodeIdx(self.us_hcode, inn, len_, bit_no) # read second hCode
+ if h < 0:
+ break # end of stream
+ if h == self.SHX_SET1: # If double Switch Set1, the CapsLock
+ is_all_upper = 1
+ continue
+
+ is_upper = 1 # anyways, still uppercase
+ else:
+ dstate = self.SHX_SET1 # if Set was not Set1, switch to Set1
+ continue
+
+ elif h == self.SHX_SET2: # If Set2, switch dstate to Set2
+ if dstate == self.SHX_SET1:
+ dstate = self.SHX_SET2
+ continue
+
+ if h != self.SHX_SET1: # all other Sets (why not else)
+ (v, bit_no) = self.getCodeIdx(self.us_vcode, inn, len_, bit_no) # we changed set, now read vCode for char
+ if v < 0:
+ break # end of stream
+
+ if v == 0 and h == self.SHX_SET1A:
+ #print("v = 0, h = self.SHX_SET1A")
+ if is_upper:
+ (temp, bit_no) = self.readCount(inn, bit_no, len_)
+ out[ol] = 255 - temp # binary
+ ol += 1
+ else:
+ (ol, bit_no) = self.decodeRepeat(inn, len_, out, ol, bit_no) # dist
+ continue
+
+ if h == self.SHX_SET1 and v == 3:
+ # was Unicode, will do Binary instead
+ (temp, bit_no) = self.readCount(inn, bit_no, len_)
+ out[ol] = 255 - temp # binary
+ ol += 1
+ continue
+
+ if h < 7 and v < 11:
+ #print("h {h} v {v}".format(h=h,v=v))
+ c = ord(self.sets[h][v])
+ if ord('a') <= c <= ord('z'):
+ if is_upper:
+ c -= 32 # go to UpperCase for letters
+ else: # handle all other cases
+ if is_upper and dstate == self.SHX_SET1 and v == 1:
+ c = ord('\t') # If UpperCase Space, change to TAB
+ if h == self.SHX_SET1B:
+ if 8 == v: # was LF or RPT, now only LF # pylint: disable=misplaced-comparison-constant
+ out[ol] = ord('\n')
+ ol += 1
+ continue
+
+ if 9 == v: # was CRLF, now RPT # pylint: disable=misplaced-comparison-constant
+ (count, bit_no) = self.readCount(inn, bit_no, len_)
+ count += 4
+ if ol + count >= len_out:
+ return -1 # overflow
+
+ rpt_c = out[ol - 1]
+ while count:
+ count -= 1
+ out[ol] = rpt_c
+ ol += 1
+ continue
+
+ if 10 == v: # pylint: disable=misplaced-comparison-constant
+ break # TERM, stop decoding
+
+ out[ol] = c
+ ol += 1
+
+ if ol >= len_out:
+ return -1 # overflow
+
+ return ol
+
+ # pylint: enable=missing-function-docstring
+
+
+if __name__ == "__main__":
+ # pylint: disable=line-too-long
+ UNISHOX = Unishox()
+ BYTES_ = bytearray(2048)
+ INN = bytearray(b'ON Switch1#State==1 DO Add1 1 ENDON ON Var1#State==0 DO ShutterStop1 ENDON ON Var1#State==1 DO ShutterClose1 ENDON ON Var1#State>=2 DO Var1 0 ENDON ON Shutter1#Close DO Var1 0 ENDON ON Switch2#State==1 DO Add2 1 ENDON ON Var2#State==0 DO ShutterStop1 ENDON ON Var2#State==1 DO ShutterOpen1 ENDON ON Var2#State>=2 DO Var2 0 ENDON ON Shutter1#Open DO Var2 0 ENDON')
+ LEN_ = UNISHOX.compress(INN, len(INN), BYTES_, len(BYTES_))
+ print("Compressed from {fromm} to {to} ({p}%)".format(fromm=len(INN), to=LEN_, p=(100-LEN_/len(INN)*100)))
+
+ OUT = bytearray(2048)
+ LEN_ = UNISHOX.decompress(BYTES_, LEN_, OUT, len(OUT))
+ print(str(OUT, 'utf-8').split('\x00')[0])
diff --git a/tools/unishox/unishox.pyc b/tools/unishox/unishox.pyc
new file mode 100644
index 000000000..d2c127f09
Binary files /dev/null and b/tools/unishox/unishox.pyc differ