Merge branch 'development' into HP303B

This commit is contained in:
Robert Jaakke 2020-06-08 20:37:47 +02:00
commit d2815c5322
9 changed files with 1109 additions and 92 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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 =
"<!DOCTYPE html><html lang=\"" D_HTML_LANGUAGE "\" class=\"\">"
"<head>"
@ -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
"</script>";
#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 =
"<style>"
"div,fieldset,input,select{padding:5px;font-size:1em;}"
@ -358,6 +588,25 @@ const char HTTP_HEAD_STYLE1[] PROGMEM =
"textarea{resize:vertical;width:98%%;height:318px;padding:5px;overflow:auto;background:#%06x;color:#%06x;}" // COLOR_CONSOLE, COLOR_CONSOLE_TEXT
"body{text-align:center;font-family:verdana,sans-serif;background:#%06x;}" // COLOR_BACKGROUND
"td{padding:0px;}";
#endif //USE_UNISHOX_COMPRESSION
#ifdef USE_UNISHOX_COMPRESSION
const size_t HTTP_HEAD_STYLE2_SIZE = 478;
const char HTTP_HEAD_STYLE2_COMPRESSED[] PROGMEM = "\x1C\x2E\xAB\x38\xF6\x8E\xCF\x88\xFE\x79\x9C\x67\x82\x04\x18\xA7\x5F\xEC\x4D\x17"
"\xE3\xCC\xE3\x3A\x59\x7D\x8D\x3C\x0E\xB0\xCD\x07\xBF\x82\xF8\x43\xCC\xF2\x3E\x8E"
"\x3E\x23\x61\xE0\x3C\x0B\x3E\x08\x52\x02\xDE\x67\x58\xA7\xA3\xC2\xA8\xF3\x39\x47"
"\x4C\x2F\xB1\xA7\x83\x19\xD4\x75\xFB\x4D\xAC\xF3\x39\x0E\x94\x5F\x63\x4F\x03\xFA"
"\x25\x0A\x3C\xCE\x46\xCF\xA3\xE8\xF0\x75\x90\xFB\x1C\x69\xB4\x75\xD7\xEF\xBD\xB5"
"\xB9\xC7\x58\x82\xFF\x75\xB9\xC7\x99\xC6\x74\xC2\xF1\xE0\x15\x2A\x2B\x86\x2F\xFE"
"\xCF\x9E\x63\x33\x7A\x9F\xCF\x07\xB8\x10\x78\x18\x3C\xC5\x61\x9B\xF9\xED\x04\xCE"
"\x2A\x01\x0F\x71\xD0\x77\xD8\x80\xA7\x50\x15\xB1\x21\xEF\xF0\x29\xD4\x05\x4C\x4A"
"\xCF\x68\x23\xF0\xDF\x4C\xD9\x47\x58\x8C\x3C\x04\x2E\x06\xBB\x39\x9E\x0F\x71\xD0"
"\x61\xED\x30\x16\x5D\x1E\x61\x33\x14\x08\x38\x05\x85\xA3\xDC\x08\x33\x0F\x71\xD0"
"\xD4\x08\x56\xFF\xA3\xC2\x81\x22\xE0\x20\xCD\x3D\xC7\x4F\x82\x17\x20\x60\x8D\xC7"
"\xD3\x1A\x78\x19\x62\x09\xBC\x3C\x79\x9C\xA2\x18\x6C\x3C\x0D\xBF\x8F\x6F\x1E\x67"
"\x38\x86\x1B\x11\xCA\x21\x86\xC3\xC1\xEE";
#define HTTP_HEAD_STYLE2 Decompress(HTTP_HEAD_STYLE2_COMPRESSED,HTTP_HEAD_STYLE2_SIZE).c_str()
#else
const char HTTP_HEAD_STYLE2[] PROGMEM =
"button{border:0;border-radius:0.3rem;background:#%06x;color:#%06x;line-height:2.4rem;font-size:1.2rem;width:100%%;-webkit-transition-duration:0.4s;transition-duration:0.4s;cursor:pointer;}" // COLOR_BUTTON, COLOR_BUTTON_TEXT
"button:hover{background:#%06x;}" // COLOR_BUTTON_HOVER
@ -369,6 +618,8 @@ const char HTTP_HEAD_STYLE2[] PROGMEM =
".p{float:left;text-align:left;}"
".q{float:right;text-align:right;}"
".r{border-radius:0.3em;padding:2px;margin:6px 2px;}";
#endif //USE_UNISHOX_COMPRESSION
const char HTTP_HEAD_STYLE3[] PROGMEM =
"</style>"
@ -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
}
}

View File

@ -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<tsiz) {
if (uplsize+csiz<tsiz) {
memcpy(script_ex_ptr,upload.buf,csiz);
script_ex_ptr+=csiz;
uplsize+=csiz;
} else {
csiz=tsiz-uplsize;
memcpy(script_ex_ptr,upload.buf,csiz);
script_ex_ptr+=csiz;
uplsize+=csiz;
}
//AddLog_P2(LOG_LEVEL_INFO, PSTR("HTP: write %d - %d"),csiz,uplsize);
}
//if (upload_file) upload_file.write(upload.buf,upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END) {
AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload close"));
//if (upload_file) upload_file.close();
if (Web.upload_error) {
AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error"));
} else {
*script_ex_ptr=0;
bitWrite(Settings.rule_enabled,0,sc_state);
SaveScript();
SaveScriptEnd();
}
} else {
Web.upload_error=1;
WSSend(500, CT_PLAIN, F("500: couldn't create file"));
}
}
void ScriptExecuteUploadSuccess(void) {
WSSend(200, CT_PLAIN, F("transfer OK"));
}
#ifdef USE_SCRIPT_FATFS
@ -3912,6 +3995,17 @@ void Script_FileUploadConfiguration(void)
}
}
void ScriptFileUploadSuccess(void) {
WSContentStart_P(S_INFORMATION);
WSContentSendStyle();
WSContentSend_P(PSTR("<div style='text-align:center;'><b>" D_UPLOAD " <font color='#"));
WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "</font></b><br/>"), WebColor(COL_TEXT_SUCCESS));
WSContentSend_P(PSTR("</div><br/>"));
WSContentSend_P(PSTR("<p><form action='%s' method='get'><button>%s</button></form></p>"),"/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("<div style='text-align:center;'><b>" D_UPLOAD " <font color='#"));
WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "</font></b><br/>"), WebColor(COL_TEXT_SUCCESS));
WSContentSend_P(PSTR("</div><br/>"));
WSContentSend_P(PSTR("<p><form action='%s' method='get'><button>%s</button></form></p>"),"/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 (time<esp32_tasks[1].task_timer) {delay(time); }
//if (time<=esp32_tasks[0].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); }
delay(esp32_tasks[0].task_timer);
Run_Scripter(">t1",3,0);
if (bitRead(Settings.rule_enabled, 0)) {
Run_Scripter(">t1",3,0);
}
}
}
@ -5752,7 +5842,9 @@ void script_task2(void *arg) {
//if (time<esp32_tasks[1].task_timer) {delay(time); }
//if (time<=esp32_tasks[1].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); }
delay(esp32_tasks[1].task_timer);
Run_Scripter(">t2",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);

View File

@ -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 <TasmotaSerial.h>
@ -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++) {

View File

@ -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 <Wire.h>
#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;

View File

@ -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 = '<var>' #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 ...

520
tools/unishox/unishox.py Normal file
View File

@ -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<len_; l++) {
c_in = inn[l]
if l and l < len_ - 4:
if c_in == inn[l - 1] and c_in == inn[l + 1] and c_in == inn[l + 2] and c_in == inn[l + 3]:
rpt_count = l + 4
while rpt_count < len_ and inn[rpt_count] == c_in:
rpt_count += 1
rpt_count -= l
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) # back to lower case and Set1
ol = self.append_bits(out, ol, self.RPT_CODE_TASMOTA, self.RPT_CODE_TASMOTA_LEN, 1) # reusing CRLF for RPT
ol = self.encodeCount(out, ol, rpt_count - 4)
l += rpt_count
#l -= 1
continue
if l < (len_ - self.NICE_LEN + 1):
#l_old = l
(l, ol, state, is_all_upper) = self.matchOccurance(inn, len_, l, out, ol, state, is_all_upper)
if 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<len_; ll--) {
while l <= ll < len_:
if inn[ll] < ord('A') or inn[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])

BIN
tools/unishox/unishox.pyc Normal file

Binary file not shown.