diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 7119fef..302bf87 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -31,7 +31,7 @@ jobs: with: python-version: '3.9' - name: Install PlatformIO Core - run: pip install --upgrade platformio + run: pip install --upgrade platformio==6.1.11 - name: Build PlatformIO Project run: pio run diff --git a/.gitignore b/.gitignore index 471812f..7ee3f93 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ commit test.html gzipped -webh \ No newline at end of file +webh +*.code-workspace +logs +todo \ No newline at end of file diff --git a/bin/UZG-01.bin b/bin/UZG-01.bin index 0636f91..86d7fce 100644 Binary files a/bin/UZG-01.bin and b/bin/UZG-01.bin differ diff --git a/bin/UZG-01_v0.1.5.full.bin b/bin/UZG-01_v0.1.5.full.bin deleted file mode 100644 index 97b9c36..0000000 Binary files a/bin/UZG-01_v0.1.5.full.bin and /dev/null differ diff --git a/bin/UZG-01_v0.1.9.full.bin b/bin/UZG-01_v0.1.9.full.bin new file mode 100644 index 0000000..b45041d Binary files /dev/null and b/bin/UZG-01_v0.1.9.full.bin differ diff --git a/manifest.json b/manifest.json index 661b125..9fa88ab 100644 --- a/manifest.json +++ b/manifest.json @@ -1,12 +1,12 @@ { "name": "UZG-FW", - "version": "v0.1.5", + "version": "v0.1.9", "builds": [ { "chipFamily": "ESP32", "improv": false, "parts": [ - { "path": "bin/UZG-01_v0.1.5.full.bin", "offset": 0 } + { "path": "bin/UZG-01_v0.1.9.full.bin", "offset": 0 } ] } ] diff --git a/platformio.ini b/platformio.ini index a217f8e..fb6b2f6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,47 +8,78 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html +;It was difficult to find working platform for https get function on solo board. Here are some logs: +;https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5/platform-espressif32-2.0.5.zip ;- old build. - unkown board +;https://github.com/tasmota/platform-espressif32/releases/download/2023.01.00/platform-espressif32.zip ; fuck yeaar. it works on solo +;https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip ; fuck yeaar. it also works on solo +;https://github.com/tasmota/platform-espressif32/releases/download/2023.08.00/platform-espressif32.zip ; bitch. did't work -1 +;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.01/platform-espressif32.zip ;- build but -1 +;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32.zip ;- last builds version but -1 +;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.05/platform-espressif32.zip ;- WiFiClientSecure.h: No such file or directory +;https://github.com/tasmota/platform-espressif32/releases/download/2023.10.10/platform-espressif32.zip ; - logs2.txt +;espressif32 @ ^6.4.0 ;- unkown board +;espressif32 @ 5.1.0 ; - works but no solo + [platformio] -default_envs = prod-v01 +default_envs = prod-solo [env] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.08.00/platform-espressif32.zip framework = arduino lib_deps = - bblanchon/ArduinoJson@>=6.21.3 + bblanchon/ArduinoJson@6.21.3 rlogiacco/CircularBuffer@>=1.3.3 - plerup/EspSoftwareSerial@>=8.1.0 + plerup/EspSoftwareSerial@8.1.0 marian-craciunescu/ESP32Ping@>=1.7 sstaub/Ticker@>=4.4.0 -board = esp32dev -monitor_filters = esp32_exception_decoder + knolleary/PubSubClient@^2.8 +monitor_filters = esp32_exception_decoder, default, log2file monitor_speed = 115200 upload_speed = 460800 - -[env:prod-v01] -board = esp32-solo1 -build_flags = -DFRAMEWORK_ARDUINO_SOLO1 -board_build.f_cpu = 160000000L extra_scripts = pre:tools/version_increment_pre.py pre:tools/webfilesbuilder/build_html.py post:tools/build.py +build_flags = + -DBUILD_ENV_NAME=$PIOENV + + +[env:prod-solo] +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip +board = esp32-solo1 +build_flags = + ${env.build_flags} + -DFRAMEWORK_ARDUINO_SOLO1 +board_build.f_cpu = 160000000L +extra_scripts = + ${env.extra_scripts} + +[env:debug-solo] +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip +board = esp32-solo1 +build_flags = + -DDEBUG + ${env.build_flags} + -DFRAMEWORK_ARDUINO_SOLO1 +board_build.f_cpu = 160000000L +extra_scripts = + ${env.extra_scripts} + [env:prod] +platform = espressif32 @ 6.4.0 +board = esp32dev build_flags = ${env.build_flags} - -D=${PIOENV} extra_scripts = - pre:tools/version_increment_pre.py - pre:tools/webfilesbuilder/build_html.py - post:tools/build.py + ${env.extra_scripts} + [env:debug] +platform = espressif32 @ 6.4.0 +board = esp32dev build_flags = - ${env.build_flags} - -D=${PIOENV} -DDEBUG + ${env.build_flags} extra_scripts = - pre:tools/version_increment_pre.py - pre:tools/webfilesbuilder/build_html.py - post:tools/debug_build.py + ${env.extra_scripts} + diff --git a/src/config.h b/src/config.h index b230e19..000adf4 100644 --- a/src/config.h +++ b/src/config.h @@ -5,8 +5,8 @@ #include #include "version.h" -#define DEBUG -//ESP32 PINS TO CONTROL LAN8720 +// #define DEBUG +// ESP32 PINS TO CONTROL LAN8720 #define ETH_CLK_MODE_1 ETH_CLOCK_GPIO17_OUT #define ETH_POWER_PIN_ALTERNATIVE_1 5 #define ETH_POWER_PIN_1 -1 @@ -14,7 +14,7 @@ #define ETH_ADDR_1 0 #define ETH_MDC_PIN_1 23 #define ETH_MDIO_PIN_1 18 -//ESP32 PINS TO CONTROL CC2652P +// ESP32 PINS TO CONTROL CC2652P #define CC2652P_RST 16 #define CC2652P_FLSH 32 #define CC2652P_RXD 36 @@ -26,13 +26,24 @@ #define TCP_LISTEN_PORT 9999 #define FORMAT_LITTLEFS_IF_FAILED true -const int16_t overseerInterval = 10 * 1000; //check lan or wifi connection every 5sec -const uint8_t overseerMaxRetry = 3; //5x12 = 60sec delay for AP start -const uint8_t LED_RED = 12; -const uint8_t LED_BLUE = 14; + +// CC2652 settings +#define BSL_PIN 15 // CC2652 pin number +#define BSL_LEVEL 0 //0-LOW 1-HIGH + + +const int16_t overseerInterval = 10 * 1000; // check lan or wifi connection every 5sec +const uint8_t overseerMaxRetry = 3; // 5x12 = 60sec delay for AP start +const uint8_t LED_USB = 12; // RED +const uint8_t LED_PWR = 14; // BLUE const uint8_t MAX_SOCKET_CLIENTS = 5; -enum COORDINATOR_MODE_t : uint8_t {COORDINATOR_MODE_LAN, COORDINATOR_MODE_WIFI, COORDINATOR_MODE_USB}; +enum COORDINATOR_MODE_t : uint8_t +{ + COORDINATOR_MODE_LAN, + COORDINATOR_MODE_WIFI, + COORDINATOR_MODE_USB +}; // struct JsonConsts_t{ // char* str; @@ -41,7 +52,8 @@ enum COORDINATOR_MODE_t : uint8_t {COORDINATOR_MODE_LAN, COORDINATOR_MODE_WIFI, // "sadasd" // }; -struct ConfigSettingsStruct{ +struct ConfigSettingsStruct +{ char ssid[50]; char password[50]; char ipAddressWiFi[18]; @@ -65,17 +77,31 @@ struct ConfigSettingsStruct{ bool webAuth; char webUser[50]; char webPass[50]; - bool disableLedBlue; - bool disableLedYellow; - //bool disablePingCtrl; + bool disableLedUSB; + bool disableLedPwr; + // bool disablePingCtrl; bool disableLeds; COORDINATOR_MODE_t coordinator_mode; - COORDINATOR_MODE_t prevCoordinator_mode;//for button + COORDINATOR_MODE_t prevCoordinator_mode; // for button bool keepWeb; bool apStarted; bool wifiWebSetupInProgress; bool fwEnabled; IPAddress fwIp; + bool mqttEnable; + char mqttServer[50]; + IPAddress mqttServerIP; + int mqttPort; + char mqttUser[50]; + char mqttPass[50]; + char mqttTopic[50]; + // bool mqttRetain; + int mqttInterval; + bool mqttDiscovery; + unsigned long mqttReconnectTime; + unsigned long mqttHeartbeatTime; + bool zbLedState; + bool zbFlashing; }; struct InfosStruct @@ -85,13 +111,23 @@ struct InfosStruct char flash[8]; }; +struct zbVerStruct +{ + uint32_t zbRev; + uint8_t maintrel; + uint8_t minorrel; + uint8_t majorrel; + uint8_t product; + uint8_t transportrev; +}; + typedef CircularBuffer LogConsoleType; -//#define WL_MAC_ADDR_LENGTH 6 +// #define WL_MAC_ADDR_LENGTH 6 #ifdef DEBUG -#define DEBUG_PRINT(x) Serial.print(x) -#define DEBUG_PRINTLN(x) Serial.println(x) +#define DEBUG_PRINT(x) Serial.print(String(x)) +#define DEBUG_PRINTLN(x) Serial.println(String(x)) #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) diff --git a/src/etc.cpp b/src/etc.cpp index 3756dff..05e3c03 100644 --- a/src/etc.cpp +++ b/src/etc.cpp @@ -4,13 +4,13 @@ #include #include "FS.h" #include - +#include #include "config.h" #include "log.h" #include "web.h" extern struct ConfigSettingsStruct ConfigSettings; -extern const char* deviceModel; +extern const char *deviceModel; void getReadableTime(String &readableTime, unsigned long beginTime) { @@ -50,17 +50,20 @@ void getReadableTime(String &readableTime, unsigned long beginTime) readableTime += String(seconds) + ""; } -float readtemp(bool clear){ - if (clear == true) { +float readtemp(bool clear) +{ + if (clear == true) + { return (temprature_sens_read() - 32) / 1.8; } - else { + else + { return (temprature_sens_read() - 32) / 1.8 - ConfigSettings.tempOffset; } } float getCPUtemp(bool clear) -{ +{ DEBUG_PRINTLN(F("getCPUtemp")); float CPUtemp = 0.0; if (WiFi.getMode() == WIFI_MODE_NULL || WiFi.getMode() == WIFI_OFF) @@ -70,12 +73,30 @@ float getCPUtemp(bool clear) CPUtemp = readtemp(clear); WiFi.disconnect(); WiFi.mode(WIFI_OFF); // disable wifi - }else{ + } + else + { CPUtemp = readtemp(clear); } return CPUtemp; } +void zigbeeRouterRejoin() +{ + printLogMsg("Zigbee RST pin ON"); + DEBUG_PRINTLN(F("Zigbee RST pin ON")); + digitalWrite(CC2652P_FLSH, 0); + delay(250); + + printLogMsg("Zigbee RST pin OFF"); + DEBUG_PRINTLN(F("Zigbee RST pin OFF")); + digitalWrite(CC2652P_FLSH, 1); + delay(500); + + printLogMsg("Router reconnect"); + DEBUG_PRINTLN(F("Router Reconnect")); +} + void zigbeeEnableBSL() { printLogMsg("Zigbee BSL pin ON"); @@ -111,8 +132,8 @@ void zigbeeRestart() DEBUG_PRINTLN(F("Zigbee RST pin OFF")); digitalWrite(CC2652P_RST, 1); delay(2000); - printLogMsg("Zigbee Reset is done"); - DEBUG_PRINTLN(F("Zigbee Reset is done")); + printLogMsg("Zigbee restart was done"); + DEBUG_PRINTLN(F("Zigbee restart was done")); } void adapterModeUSB() @@ -120,7 +141,7 @@ void adapterModeUSB() printLogMsg("Switched UZG-01 to USB mode"); DEBUG_PRINTLN(F("Switched UZG-01 to USB mode")); digitalWrite(MODE_SWITCH, 1); - digitalWrite(LED_RED, 1); + digitalWrite(LED_USB, 1); } void adapterModeLAN() @@ -128,36 +149,65 @@ void adapterModeLAN() printLogMsg("Switched UZG-01 to LAN mode"); DEBUG_PRINTLN(F("Switched UZG-01 to LAN mode")); digitalWrite(MODE_SWITCH, 0); - digitalWrite(LED_RED, 0); + digitalWrite(LED_USB, 0); } -void ledYellowToggle() +void ledPowerToggle() { printLogMsg("BLUE LED has been toggled"); DEBUG_PRINTLN(F("BLUE LED has been toggled")); - digitalWrite(LED_BLUE, !digitalRead(LED_BLUE)); + digitalWrite(LED_PWR, !digitalRead(LED_PWR)); } -void ledBlueToggle() +void ledUSBToggle() { printLogMsg("RED LED has been toggled"); DEBUG_PRINTLN(F("RED LED has been toggled")); - digitalWrite(LED_RED, !digitalRead(LED_RED)); + digitalWrite(LED_USB, !digitalRead(LED_USB)); } -void getDeviceID(char * arr){ +void getDeviceID(char *arr) +{ uint64_t mac = ESP.getEfuseMac(); uint8_t a; uint8_t b; - a ^= mac >> 8*0; - a ^= mac >> 8*1; - a ^= mac >> 8*2; - a ^= mac >> 8*3; - b ^= mac >> 8*4; - b ^= mac >> 8*5; - b ^= mac >> 8*6; - b ^= mac >> 8*7; - sprintf(arr, "%s--%3d%3d", deviceModel, a, b); + a ^= mac >> 8 * 0; + a ^= mac >> 8 * 1; + a ^= mac >> 8 * 2; + a ^= mac >> 8 * 3; + b ^= mac >> 8 * 4; + b ^= mac >> 8 * 5; + b ^= mac >> 8 * 6; + b ^= mac >> 8 * 7; + + char buf[20]; + + if (a < 16) + { + sprintf(buf, "0%x", a); + } + else + { + sprintf(buf, "%x", a); + } + + if (b < 16) + { + sprintf(buf, "%s0%x", buf, b); + } + else + { + sprintf(buf, "%s%x", buf, b); + } + + for (uint8_t cnt = 0; cnt < strlen(buf); cnt++) + { + buf[cnt] = toupper(buf[cnt]); + } + + // char buf[20]; + sprintf(arr, "%s-%s", deviceModel, buf); + // arr = buf; } // void writeDefultConfig(const char *path, String StringConfig) @@ -182,15 +232,19 @@ void getDeviceID(char * arr){ // configFile.close(); // } -void writeDefultConfig(const char *path, JsonDocument& doc){ +void writeDefultConfig(const char *path, DynamicJsonDocument &doc) +{ DEBUG_PRINTLN(path); DEBUG_PRINTLN(F("Write defaults")); File configFile = LittleFS.open(path, FILE_WRITE); - if (!configFile){ + if (!configFile) + { DEBUG_PRINTLN(F("Failed Write")); - //return false; - }else{ + // return false; + } + else + { serializeJson(doc, configFile); } configFile.close(); @@ -220,25 +274,28 @@ String hexToDec(String hexString) return String(decValue); } -void resetSettings(){ +void resetSettings() +{ DEBUG_PRINTLN(F("[resetSettings] Start")); - digitalWrite(LED_RED, 1); - digitalWrite(LED_BLUE, 0); - for (uint8_t i = 0; i < 15; i++){ + digitalWrite(LED_USB, 1); + digitalWrite(LED_PWR, 0); + for (uint8_t i = 0; i < 15; i++) + { delay(200); - digitalWrite(LED_RED, !digitalRead(LED_RED)); - digitalWrite(LED_BLUE, !digitalRead(LED_BLUE)); + digitalWrite(LED_USB, !digitalRead(LED_USB)); + digitalWrite(LED_PWR, !digitalRead(LED_PWR)); } DEBUG_PRINTLN(F("[resetSettings] Led blinking done")); - if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, "/lfs2", 10)){ + if (!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED, "/lfs2", 10)) + { DEBUG_PRINTLN(F("Error with LITTLEFS")); } - LittleFS.remove("/config/configSerial.json"); //todo move 2 define or global const - LittleFS.remove("/config/configSecurity.json"); //todo move 2 define or global const - LittleFS.remove("/config/configGeneral.json"); //todo move 2 define or global const - LittleFS.remove("/config/configEther.json"); //todo move 2 define or global const - LittleFS.remove("/config/configWifi.json"); //todo move 2 define or global const - LittleFS.remove("/config/system.json"); //todo move 2 define or global const + LittleFS.remove("/config/configSerial.json"); // todo move 2 define or global const + LittleFS.remove("/config/configSecurity.json"); // todo move 2 define or global const + LittleFS.remove("/config/configGeneral.json"); // todo move 2 define or global const + LittleFS.remove("/config/configEther.json"); // todo move 2 define or global const + LittleFS.remove("/config/configWifi.json"); // todo move 2 define or global const + LittleFS.remove("/config/system.json"); // todo move 2 define or global const DEBUG_PRINTLN(F("[resetSettings] Config del done")); ESP.restart(); } \ No newline at end of file diff --git a/src/etc.h b/src/etc.h index 9fa77ec..34893ba 100644 --- a/src/etc.h +++ b/src/etc.h @@ -9,20 +9,26 @@ uint8_t temprature_sens_read(); #endif uint8_t temprature_sens_read(); -float getCPUtemp(bool clear = false); +#define STRINGIFY(s) STRINGIFY1(s) // Don’t ask why. It has to do with the inner workings of the preprocessor. +#define STRINGIFY1(s) #s // https://community.platformio.org/t/how-to-put-a-string-in-a-define-in-build-flag-into-a-libary-json-file/13480/6 + + + +float getCPUtemp(bool clear = false); +void zigbeeRouterRejoin(); void zigbeeEnableBSL(); void zigbeeRestart(); void adapterModeUSB(); void adapterModeLAN(); -void ledYellowToggle(); -void ledBlueToggle(); +void ledPowerToggle(); +void ledUSBToggle(); void getDeviceID(char * arr); -void writeDefultConfig(const char *path, JsonDocument& doc); +void writeDefultConfig(const char *path, DynamicJsonDocument& doc); void resetSettings(); diff --git a/src/intelhex.cpp b/src/intelhex.cpp new file mode 100644 index 0000000..f08f5d9 --- /dev/null +++ b/src/intelhex.cpp @@ -0,0 +1,212 @@ +#include "intelhex.h" +#include "config.h" + +/* + * IntelHex Arduino Library + * + * Description: + * This library is designed to read and parse Intel Hex format files from the LittleFS file system on ESP8266 and ESP32. + * + * License: + * MIT License + * + * Author: + * OpenAI's ChatGPT with contributions from xyzroe + */ +/* + +If you want library to control LittleFS you need to uncomment some lines. + + +TO-DO List + +1. Line length check +2. Error if not IntelHEX line after start till end + +*/ + +IntelHex::IntelHex(const char *filename) : _filename(filename) {} + +IntelHex::~IntelHex() +{ + if (_file) + { + _file.close(); + } +} + +bool IntelHex::open() +{ + // if (!LittleFS.begin()) { + // Serial.println("Could not mount file system"); + // return false; + // } + _file = LittleFS.open(_filename, "r"); + return _file; +} + +void IntelHex::close() +{ + _file.close(); + // LittleFS.end(); +} + +bool IntelHex::parse(void (*preCallback)(), void (*parseCallback)(uint32_t address, uint8_t len, uint8_t *data), void (*postCallback)()) +{ + if (!open()) + { + return false; + } + + preCallback(); + + bool status = true; + + while (status == true && _file.available() && _file_last_line == false) + { + status = _munchLine(parseCallback); + } + + if (_file_last_line) + { + DEBUG_PRINTLN("HEX file last line found"); + _file_parsed = true; + } + else + { + DEBUG_PRINTLN("HEX file last line NOT found"); + status = false; + } + + postCallback(); + + close(); + return status; +} + +bool IntelHex::_munchLine(void (*parseCallback)(uint32_t address, uint8_t len, uint8_t *data)) +{ + String line = _file.readStringUntil('\n'); + line.trim(); + + // DEBUG_PRINTLN("Parsing line: " + line); // Print each processed line + // delay(1); + + if (line.length() == 0 || line[0] != ':') + { + return true; // Continue parsing + } + + uint8_t sum = 0; + for (int i = 1; i < line.length(); i += 2) + { + sum += strtol(line.substring(i, i + 2).c_str(), nullptr, 16); + } + + if (sum != 0) + { + DEBUG_PRINTLN("Checksum line error"); + return false; + } + + if (line == ":00000001FF") + { // look like Intel HEX file end line + DEBUG_PRINTLN("File last line found"); + _file_last_line = true; + return true; + } + + uint8_t len = strtol(line.substring(1, 3).c_str(), nullptr, 16); + uint16_t offset_low = strtol(line.substring(3, 7).c_str(), nullptr, 16); + uint8_t recordType = strtol(line.substring(7, 9).c_str(), nullptr, 16); + + // DEBUG_PRINTLN("Record Type: " + String(recordType, HEX) + ", Offset Low: " + String(offset_low, HEX) + ", Length: " + String(len, HEX)); // Print record info + + uint8_t data[255]; + for (uint8_t i = 0; i < len; i++) + { + data[i] = strtol(line.substring(9 + i * 2, 11 + i * 2).c_str(), nullptr, 16); + } + + uint32_t address = offset_low; + // https://jimmywongiot.com/2021/04/20/format-of-intelhex/ + if (recordType == 4) + { + _offset_high = ((uint32_t)strtol(line.substring(9, 13).c_str(), nullptr, 16)) << 16; // because 4 + } + else if (recordType == 2) + { + _offset_high = ((uint32_t)strtol(line.substring(9, 13).c_str(), nullptr, 16)) << 4; // because 2 + } + + address += _offset_high; + + if (!_bsl_valid) + { + _bsl_valid = _checkBSLconfig(address, len, data); + } + parseCallback(address, len, data); + + return true; +} + +bool IntelHex::_checkBSLconfig(uint32_t address, uint8_t len, uint8_t *data) +{ + // Check if CCFG is within the buffer + //DEBUG_PRINTLN(" "); + //DEBUG_PRINT(ELEMENTCOUNT(CCFG_ADDRESS)); + //DEBUG_PRINT(" "); + + // DEBUG_PRINTLN(ESP.getFreeHeap() / 1024); + delay(5); + for (int i = 0; i < ELEMENTCOUNT(CCFG_ADDRESS); i++) + { + //DEBUG_PRINT(CCFG_ADDRESS[i]); + //DEBUG_PRINT(" "); + if (address <= CCFG_ADDRESS[i] && address + len > CCFG_ADDRESS[i] + 4) + { + DEBUG_PRINTLN(" "); + DEBUG_PRINTLN("CCFG_ADDRESS[" + String(i) + "] in range"); + + if (data[CCFG_ADDRESS[i] - address + 3] == BOOTLOADER_ENABLE) + { + DEBUG_PRINTLN("Bootloader enabled"); + _bsl_bootloader_enbl = true; + } + + if (data[CCFG_ADDRESS[i] - address + 0] == BL_ENABLE) + { + DEBUG_PRINTLN("'failure analysis' enabled"); + _bsl_bl_enbl = true; + } + + if (data[CCFG_ADDRESS[i] - address + 2] == BL_LEVEL_LOW) + { + DEBUG_PRINTLN("BSL low level"); + _bsl_level = 1; + } + else if (data[CCFG_ADDRESS[i] - address + 2] == BL_LEVEL_HIGH) + { + DEBUG_PRINTLN("BSL high level"); + _bsl_level = 2; + } + else + { + DEBUG_PRINTLN("BSL level UNKNOWN. Error!"); + _bsl_level = 0; + } + + // Pin in HEX converts to DEC number + _bsl_pin = data[CCFG_ADDRESS[i] - address + 1]; + DEBUG_PRINTLN("BSL pin - " + String(_bsl_pin)); + + if (_bsl_bootloader_enbl && _bsl_bl_enbl && (_bsl_level > 0) && (_bsl_pin > 0)) + { + DEBUG_PRINTLN("BSL valid!"); + _bsl_addr = i; + return true; + } + } + } + return false; +} \ No newline at end of file diff --git a/src/intelhex.h b/src/intelhex.h new file mode 100644 index 0000000..beea94d --- /dev/null +++ b/src/intelhex.h @@ -0,0 +1,70 @@ +#ifndef INTELHEX_H +#define INTELHEX_H + +#include "Arduino.h" +#include "LittleFS.h" + +#define ELEMENTCOUNT(x) (sizeof(x) / sizeof(x[0])) + +class IntelHex +{ +public: + IntelHex(const char *filename); + ~IntelHex(); + + bool parse(void (*preCallback)(), void (*parseCallback)(uint32_t address, uint8_t len, uint8_t *data), void (*postCallback)()); + + void setFileValidated(bool value) { _file_validated = value; } + + bool fileParsed() const { return _file_parsed; } + bool fileValidated() const { return _file_validated; } + + bool bslActive() const { return _bsl_valid; } + int bslPin() const { return _bsl_pin; } + bool bslLevel() const { return _bsl_level - 1; } // 0 error, 1 low, 2 high + bool bslAddr() const { return _bsl_addr; } // 0 - all seriers, 1 - P/R 7 series + +private: + const char *_filename; + File _file; + bool _file_parsed = false; + bool _file_validated = false; + bool _file_last_line = false; + uint32_t _offset_high; + + bool _bsl_bootloader_enbl = false; + bool _bsl_bl_enbl = false; + int _bsl_level = 0; + int _bsl_pin = 0; + bool _bsl_valid = false; + int _bsl_addr = 0; + + // const uint32_t CCFG_ADDRESS = 0x057FD8; // all others + const uint32_t ALL_CHIP_ADDRESS = 0x057FD8; + const uint32_t P7_CHIP_ADDRESS = 0x0AFFD8; + const uint32_t CCFG_ADDRESS[2] = {ALL_CHIP_ADDRESS, P7_CHIP_ADDRESS}; + + #define ALL_CHIP_ID 0 + #define P7_CHIP_ID 1 + + // const uint32_t CCFG_ADDRESS = 0x0AFFD8; //CC2652R7 and CC1352P7 + + /* + Parsing line: :020000025000AC ordinary so 0x057FD8 + Parsing line: :02000002A0005C but in 2652R7 so maybe 0x0AFFD8 + */ + + const uint32_t BOOTLOADER_ENABLE = 0xC5; //(Bootloader enable. SET_CCFG_BL_CONFIG_BOOTLOADER_ENABLE in CC13xx/CC26xxware) + + const uint32_t BL_LEVEL_LOW = 0xFE; //(Active low. SET_CCFG_BL_CONFIG_BL_LEVEL in CC13xx/CC26xxware) + const uint32_t BL_LEVEL_HIGH = 0xFF; // ? NEED to check! + + const uint32_t BL_ENABLE = 0xC5; // (Enable "failure analysis". SET_CCFG_BL_CONFIG_BL_ENABLE in CC13xx/CC26xxware) + + bool open(); + void close(); + bool _munchLine(void (*parseCallback)(uint32_t address, uint8_t len, uint8_t *data)); + bool _checkBSLconfig(uint32_t address, uint8_t len, uint8_t *data); +}; + +#endif diff --git a/src/intelhex2.cpp.bak b/src/intelhex2.cpp.bak new file mode 100644 index 0000000..f7f473c --- /dev/null +++ b/src/intelhex2.cpp.bak @@ -0,0 +1,91 @@ +#include "intelhex.h" + +#ifdef DEBUG +#define DEBUG_PRINT(x) Serial.print(x) +#define DEBUG_PRINTLN(x) Serial.println(x) +#else +#define DEBUG_PRINT(x) +#define DEBUG_PRINTLN(x) +#endif + +IntelHex::IntelHex(const char* filename) : _filename(filename) {} + +IntelHex::~IntelHex() { + if (_file) { + _file.close(); + } +} + +bool IntelHex::open() { + if (!LittleFS.begin()) { + DEBUG_PRINTLN("Could not mount file system"); + return false; + } + _file = LittleFS.open(_filename, "r"); + return _file; +} + +void IntelHex::close() { + DEBUG_PRINT("Try to close file..."); + _file.close(); + //LittleFS.end(); + DEBUG_PRINTLN(" OK"); +} + +bool IntelHex::parse(void (*preCallback)(), void (*parseCallback)(uint32_t address, uint8_t len, uint8_t* data), void (*postCallback)()) { + if (!open()) { + return false; + } + + preCallback(); + + bool status = true; + while (status && _file.available()) { + status = _munchLine(parseCallback); + } + + postCallback(); + + file_parsed = true; // Set file_parsed to true after parsing + close(); + return status; +} + +bool IntelHex::_munchLine(void (*parseCallback)(uint32_t address, uint8_t len, uint8_t* data)) { + String line = _file.readStringUntil('\n'); + line.trim(); + + //DEBUG_PRINT("Parsing line: " + line); + + if (line.length() == 0 || line[0] != ':') { + return true; // Continue parsing + } + + uint8_t sum = 0; + for (int i = 1; i < line.length(); i += 2) { + sum += strtol(line.substring(i, i + 2).c_str(), nullptr, 16); + } + + if (sum != 0) { + DEBUG_PRINTLN("Checksum error"); + file_validated = false; // Set file_validated to false on checksum error + return false; + } + + //file_validated = true; // Set file_validated to true if checksum passes + + uint8_t len = strtol(line.substring(1, 3).c_str(), nullptr, 16); + uint16_t address = strtol(line.substring(3, 7).c_str(), nullptr, 16); + uint8_t recordType = strtol(line.substring(7, 9).c_str(), nullptr, 16); + + //DEBUG_PRINTLN("Record Type: " + String(recordType, HEX) + ", Address: " + String(address, HEX) + ", Length: " + String(len, HEX)); + + uint8_t data[255]; + for (uint8_t i = 0; i < len; i++) { + data[i] = strtol(line.substring(9 + i * 2, 11 + i * 2).c_str(), nullptr, 16); + } + + parseCallback(address, len, data); + + return true; +} diff --git a/src/main.cpp b/src/main.cpp index 75283d9..90d7407 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include -#include +//#include +//#include #include #include #include "FS.h" @@ -8,6 +9,8 @@ #include "web.h" #include "log.h" #include "etc.h" +#include "mqtt.h" +#include "zb.h" #include #include "version.h" #include "Ticker.h" @@ -23,6 +26,7 @@ #define BUFFER_SIZE 256 ConfigSettingsStruct ConfigSettings; +zbVerStruct zbVer; InfosStruct Infos; //volatile bool btnFlag = false; @@ -37,6 +41,7 @@ const char* configFileEther = "/config/configEther.json"; const char* configFileGeneral = "/config/configGeneral.json"; const char* configFileSecurity = "/config/configSecurity.json"; const char* configFileSerial = "/config/configSerial.json"; +const char* configFileMqtt = "/config/configMqtt.json"; const char* deviceModel = "UZG-01"; void mDNS_start(); @@ -370,8 +375,8 @@ bool loadConfigGeneral(){ const char* hostname = "hostname"; const char* disableLeds = "disableLeds"; const char* refreshLogs = "refreshLogs"; - const char* disableLedYellow = "disableLedYellow"; - const char* disableLedBlue = "disableLedBlue"; + const char* disableLedPwr = "disableLedPwr"; + const char* disableLedUSB = "disableLedUSB"; const char* prevCoordMode = "prevCoordMode"; const char* keepWeb = "keepWeb"; File configFile = LittleFS.open(configFileGeneral, FILE_READ); @@ -380,13 +385,13 @@ bool loadConfigGeneral(){ //String deviceID = deviceModel; //getDeviceID(deviceID); DEBUG_PRINTLN("RESET ConfigGeneral"); - //String StringConfig = "{\"hostname\":\"" + deviceID + "\",\"disableLeds\": false,\"refreshLogs\":1000,\"usbMode\":0,\"disableLedYellow\":0,\"disableLedBlue\":0,\""+ coordMode +"\":0}\""+ prevCoordMode +"\":0, \"keepWeb\": 0}"; + //String StringConfig = "{\"hostname\":\"" + deviceID + "\",\"disableLeds\": false,\"refreshLogs\":1000,\"usbMode\":0,\"disableLedPwr\":0,\"disableLedUSB\":0,\""+ coordMode +"\":0}\""+ prevCoordMode +"\":0, \"keepWeb\": 0}"; DynamicJsonDocument doc(1024); doc[hostname] = deviceModel; doc[disableLeds] = 0; doc[refreshLogs] = 1000; - doc[disableLedYellow] = 0; - doc[disableLedBlue] = 0; + doc[disableLedPwr] = 0; + doc[disableLedUSB] = 0; doc[coordMode] = 0; doc[prevCoordMode] = 0; doc[keepWeb] = 0; @@ -422,10 +427,10 @@ bool loadConfigGeneral(){ ConfigSettings.prevCoordinator_mode = static_cast((uint8_t)doc[prevCoordMode]); DEBUG_PRINTLN(F("[loadConfigGeneral] 'static_cast' res is:")); DEBUG_PRINTLN(String(ConfigSettings.coordinator_mode)); - ConfigSettings.disableLedYellow = (uint8_t)doc[disableLedYellow]; - DEBUG_PRINTLN(F("[loadConfigGeneral] disableLedYellow")); - ConfigSettings.disableLedBlue = (uint8_t)doc[disableLedBlue]; - DEBUG_PRINTLN(F("[loadConfigGeneral] disableLedBlue")); + ConfigSettings.disableLedPwr = (uint8_t)doc[disableLedPwr]; + DEBUG_PRINTLN(F("[loadConfigGeneral] disableLedPwr")); + ConfigSettings.disableLedUSB = (uint8_t)doc[disableLedUSB]; + DEBUG_PRINTLN(F("[loadConfigGeneral] disableLedUSB")); ConfigSettings.disableLeds = (uint8_t)doc[disableLeds]; DEBUG_PRINTLN(F("[loadConfigGeneral] disableLeds")); ConfigSettings.keepWeb = (uint8_t)doc[keepWeb]; @@ -515,6 +520,64 @@ bool loadConfigSerial(){ return true; } +bool loadConfigMqtt() +{ + const char* enable = "enable"; + const char* server = "server"; + const char* port = "port"; + const char* user = "user"; + const char* pass = "pass"; + const char* topic = "topic"; + const char* interval = "interval"; + const char* discovery = "discovery"; + + File configFile = LittleFS.open(configFileMqtt, FILE_READ); + if (!configFile) + { + char deviceIdArr[20]; + getDeviceID(deviceIdArr); + + DynamicJsonDocument doc(1024); + doc[enable] = 0; + doc[server] = ""; + doc[port] = 1883; + doc[user] = "mqttuser"; + doc[pass] = ""; + doc[topic] = String(deviceIdArr); + doc[interval] = 60; + doc[discovery] = 0; + writeDefultConfig(configFileMqtt, doc); + } + + configFile = LittleFS.open(configFileMqtt, FILE_READ); + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, configFile); + + if (error) + { + DEBUG_PRINTLN(F("deserializeJson() failed: ")); + DEBUG_PRINTLN(error.f_str()); + + configFile.close(); + LittleFS.remove(configFileMqtt); + return false; + } + + ConfigSettings.mqttEnable = (int)doc[enable]; + strlcpy(ConfigSettings.mqttServer, doc[server] | "", sizeof(ConfigSettings.mqttServer)); + ConfigSettings.mqttServerIP = parse_ip_address(ConfigSettings.mqttServer); + ConfigSettings.mqttPort = (int)doc[port]; + strlcpy(ConfigSettings.mqttUser, doc[user] | "", sizeof(ConfigSettings.mqttUser)); + strlcpy(ConfigSettings.mqttPass, doc[pass] | "", sizeof(ConfigSettings.mqttPass)); + strlcpy(ConfigSettings.mqttTopic, doc[topic] | "", sizeof(ConfigSettings.mqttTopic)); + ConfigSettings.mqttInterval = (int)doc[interval]; + ConfigSettings.mqttDiscovery = (int)doc[discovery]; + + configFile.close(); + return true; + +} + void startAP(const bool start){ if (ConfigSettings.apStarted){ if (!start){ @@ -642,28 +705,28 @@ void setLedsDisable(bool mode, bool setup){ deserializeJson(doc, configFile); configFile.close(); doc["disableLeds"] = mode; - doc["disableLedYellow"] = mode; - doc["disableLedBlue"] = mode; + doc["disableLedPwr"] = mode; + doc["disableLedUSB"] = mode; configFile = LittleFS.open(path, FILE_WRITE); serializeJson(doc, configFile); configFile.close(); ConfigSettings.disableLeds = mode; - ConfigSettings.disableLedYellow = mode; - ConfigSettings.disableLedBlue = mode; + ConfigSettings.disableLedPwr = mode; + ConfigSettings.disableLedUSB = mode; } if(mode){ - digitalWrite(LED_RED, !mode); - digitalWrite(LED_BLUE, !mode); + digitalWrite(LED_USB, !mode); + digitalWrite(LED_PWR, !mode); }else{ - if(!ConfigSettings.disableLedYellow){ - digitalWrite(LED_BLUE, !mode); + if(!ConfigSettings.disableLedPwr){ + digitalWrite(LED_PWR, !mode); }else{ - digitalWrite(LED_BLUE, 0); + digitalWrite(LED_PWR, 0); } - if(ConfigSettings.coordinator_mode == COORDINATOR_MODE_USB && !ConfigSettings.disableLedBlue){ - digitalWrite(LED_RED, !mode); + if(ConfigSettings.coordinator_mode == COORDINATOR_MODE_USB && !ConfigSettings.disableLedUSB){ + digitalWrite(LED_USB, !mode); }else{ - digitalWrite(LED_RED, 0); + digitalWrite(LED_USB, 0); } } DEBUG_PRINTLN(F("[setLedsDisable] done")); @@ -716,7 +779,7 @@ void toggleUsbMode(){ configFile = LittleFS.open(path, FILE_WRITE); serializeJson(doc, configFile); configFile.close(); - digitalWrite(LED_RED, ConfigSettings.coordinator_mode == COORDINATOR_MODE_USB ? 1 : 0); + digitalWrite(LED_USB, ConfigSettings.coordinator_mode == COORDINATOR_MODE_USB ? 1 : 0); ESP.restart(); } @@ -767,11 +830,11 @@ void setupCoordinatorMode(){ // serial.write(cmd, size); // } -void clearS2Buffer(){ - while (Serial2.available()){//clear buffer - Serial2.read(); - } -} +//void clearS2Buffer(){ +// while (Serial2.available()){//clear buffer +// Serial2.read(); +// } +//} void setup(){ Serial.begin(115200);//todo ifdef DEBUG @@ -782,15 +845,16 @@ void setup(){ pinMode(CC2652P_FLSH, OUTPUT); digitalWrite(CC2652P_RST, 1); digitalWrite(CC2652P_FLSH, 1); - pinMode(LED_BLUE, OUTPUT); - pinMode(LED_RED, OUTPUT); + pinMode(LED_PWR, OUTPUT); + pinMode(LED_USB, OUTPUT); pinMode(BTN, INPUT); pinMode(MODE_SWITCH, OUTPUT); digitalWrite(MODE_SWITCH, 0);//enable zigbee serial - digitalWrite(LED_BLUE, 1); - digitalWrite(LED_RED, 1); + digitalWrite(LED_PWR, 1); + digitalWrite(LED_USB, 1); //hard reset + #if BUILD_ENV_NAME != debug if(!digitalRead(BTN)){ DEBUG_PRINTLN(F("[hard reset] Entering hard reset mode")); uint8_t counter = 0; @@ -805,60 +869,13 @@ void setup(){ } DEBUG_PRINTLN(F("[hard reset] Btn up, exit")); } + #endif //-------------------- //zig connection & leds testing - const byte cmdLed0 = 0x27; - const byte cmdLed1 = 0x0A; - const byte cmdLedIndex = 0x01;//for led 1 - const byte cmdLedStateOff = 0x00; - const byte cmdLedStateOn = 0x01; - const byte cmdFrameStart = 0xFE; - const byte cmdLedLen = 0x02; - const byte zigLed1Off[] = {cmdFrameStart, cmdLedLen, cmdLed0, cmdLed1, cmdLedIndex, cmdLedStateOff, 0x2E}; //resp FE 01 67 0A 00 6C - const byte zigLed1On[] = {cmdFrameStart, cmdLedLen, cmdLed0, cmdLed1, cmdLedIndex, cmdLedStateOn, 0x2F}; - const byte cmdLedResp[] = {0xFE, 0x01, 0x67, 0x0A, 0x00, 0x6C}; Serial2.begin(115200, SERIAL_8N1, CC2652P_RXD, CC2652P_TXD); //start zigbee serial - bool respOk = false; - for (uint8_t i = 0; i < 12; i++){//wait for zigbee start - if(respOk) break; - clearS2Buffer(); - Serial2.write(zigLed1On, sizeof(zigLed1On)); - Serial2.flush(); - delay(400); - for (uint8_t i = 0; i < 5; i++){ - if (Serial2.read() != 0xFE){//check for packet start - Serial2.read();//skip - }else{ - for (uint8_t i = 1; i < 4; i++){ - if(Serial2.read() != cmdLedResp[i]){//check if resp ok - respOk = false; - break; - }else{ - respOk = true; - } - } - } - } - digitalWrite(LED_RED, !digitalRead(LED_RED));//blue led flashing mean wait for zigbee resp - } - delay(500); - if(!respOk){ - digitalWrite(LED_BLUE, 1); - digitalWrite(LED_RED, 1); - for (uint8_t i = 0; i < 5; i++){//indicate wrong resp - digitalWrite(LED_BLUE, !digitalRead(LED_BLUE)); - digitalWrite(LED_RED, !digitalRead(LED_RED)); - delay(1000); - } - printLogMsg("[ZBCHK] Wrong answer"); - }else{ - Serial2.write(zigLed1Off, sizeof(zigLed1Off)); - Serial2.flush(); - printLogMsg("[ZBCHK] connection established"); - } - digitalWrite(LED_BLUE, 0); - digitalWrite(LED_RED, 0); + zbCheck(); + getZbVer(); //----------------- attachInterrupt(digitalPinToInterrupt(BTN), btnInterrupt, FALLING); @@ -905,7 +922,7 @@ void setup(){ DEBUG_PRINTLN(F("Config serial load OK")); } - if ((!loadConfigWifi()) || (!loadConfigEther()) || (!loadConfigGeneral()) || (!loadConfigSecurity())) + if ((!loadConfigWifi()) || (!loadConfigEther()) || (!loadConfigGeneral()) || (!loadConfigSecurity()) || (!loadConfigMqtt())) { DEBUG_PRINTLN(F("Error load config files")); ESP.restart(); @@ -920,8 +937,22 @@ void setup(){ setupCoordinatorMode(); ConfigSettings.connectedClients = 0; + if (ConfigSettings.mqttEnable) + { + mqttConnectSetup(); + } + + DEBUG_PRINTLN(millis()); + Serial2.updateBaudRate(ConfigSettings.serialSpeed);//set actual speed printLogMsg("Setup done"); + + char deviceIdArr[20]; + getDeviceID(deviceIdArr); + + DEBUG_PRINTLN(String(deviceIdArr)); + printLogMsg(String(deviceIdArr)); + } WiFiClient client[10]; @@ -937,6 +968,7 @@ void socketClientConnected(int client) ConfigSettings.socketTime = millis(); DEBUG_PRINT(F("Socket time ")); DEBUG_PRINTLN(ConfigSettings.socketTime); + mqttPublishIo("socket", "ON"); } ConfigSettings.connectedSocket[client] = true; ConfigSettings.connectedClients++; @@ -956,6 +988,7 @@ void socketClientDisconnected(int client) ConfigSettings.socketTime = millis(); DEBUG_PRINT(F("Socket time ")); DEBUG_PRINTLN(ConfigSettings.socketTime); + mqttPublishIo("socket", "OFF"); } } } @@ -1123,8 +1156,16 @@ void loop(void){ printSendSocket(serial_bytes_read, serial_buf); serial_bytes_read = 0; } + + + if (ConfigSettings.mqttEnable) + { + mqttLoop(); } + } + + if (WiFi.getMode() == WIFI_MODE_AP || WiFi.getMode() == WIFI_MODE_APSTA) { dnsServer.processNextRequest(); diff --git a/src/mqtt.cpp b/src/mqtt.cpp new file mode 100644 index 0000000..a014a69 --- /dev/null +++ b/src/mqtt.cpp @@ -0,0 +1,457 @@ +#include +#include +#include +#include "WiFi.h" +#include +#include "FS.h" +#include "FS.h" +#include +#include "web.h" +#include "config.h" +#include "log.h" +#include "etc.h" +#include +#include "mqtt.h" + +extern struct ConfigSettingsStruct ConfigSettings; +extern struct zbVerStruct zbVer; + +WiFiClient clientMqtt; + +PubSubClient clientPubSub(clientMqtt); + +void mqttConnectSetup() +{ + clientPubSub.setServer(ConfigSettings.mqttServerIP, ConfigSettings.mqttPort); + clientPubSub.setCallback(mqttCallback); +} + +void mqttReconnect() +{ + DEBUG_PRINT(F("Attempting MQTT connection...")); + + byte willQoS = 0; + String willTopic = String(ConfigSettings.mqttTopic) + "/avty"; + const char *willMessage = "offline"; + boolean willRetain = false; + char deviceIdArr[20]; + getDeviceID(deviceIdArr); + + if (clientPubSub.connect(String(deviceIdArr).c_str(), ConfigSettings.mqttUser, ConfigSettings.mqttPass, willTopic.c_str(), willQoS, willRetain, willMessage)) + { + ConfigSettings.mqttReconnectTime = 0; + mqttOnConnect(); + } + else + { + DEBUG_PRINT(F("failed, rc=")); + DEBUG_PRINT(clientPubSub.state()); + DEBUG_PRINT(F(" try again in ")); + DEBUG_PRINT(ConfigSettings.mqttInterval); + DEBUG_PRINTLN(F(" seconds")); + + ConfigSettings.mqttReconnectTime = millis() + ConfigSettings.mqttInterval * 1000; + } +} + +void mqttOnConnect() +{ + DEBUG_PRINTLN(F("connected")); + mqttSubscribe("cmd"); + DEBUG_PRINTLN(F("mqtt Subscribed")); + if (ConfigSettings.mqttDiscovery) + { + mqttPublishDiscovery(); + DEBUG_PRINTLN(F("mqtt Published Discovery")); + } + + mqttPublishIo("rst_esp", "OFF"); + mqttPublishIo("rst_zig", "OFF"); + mqttPublishIo("enbl_bsl", "OFF"); + mqttPublishIo("socket", "OFF"); + DEBUG_PRINTLN(F("mqtt Published IOs")); + mqttPublishAvty(); + DEBUG_PRINTLN(F("mqtt Published Avty")); + if (ConfigSettings.mqttInterval > 0) + { + mqttPublishState(); + DEBUG_PRINTLN(F("mqtt Published State")); + } +} + +void mqttPublishMsg(String topic, String msg, bool retain) +{ + clientPubSub.beginPublish(topic.c_str(), msg.length(), retain); + clientPubSub.print(msg.c_str()); + clientPubSub.endPublish(); +} + +void mqttPublishAvty() +{ + String topic(ConfigSettings.mqttTopic); + topic = topic + "/avty"; + String mqttBuffer = "online"; + clientPubSub.publish(topic.c_str(), mqttBuffer.c_str(), true); +} + +void mqttPublishState() +{ + String topic(ConfigSettings.mqttTopic); + topic = topic + "/state"; + DynamicJsonDocument root(1024); + String readableTime; + getReadableTime(readableTime, 0); + root["uptime"] = readableTime; + + float CPUtemp = getCPUtemp(); + root["temperature"] = String(CPUtemp); + + root["connections"] = ConfigSettings.connectedClients; + + if (ConfigSettings.connectedEther) + { + if (ConfigSettings.dhcp) + { + root["ip"] = ETH.localIP().toString(); + } + else + { + root["ip"] = ConfigSettings.ipAddress; + } + } + else + { + if (ConfigSettings.dhcpWiFi) + { + root["ip"] = WiFi.localIP().toString(); + } + else + { + root["ip"] = ConfigSettings.ipAddressWiFi; + } + } + //if (ConfigSettings.emergencyWifi) + //{ + // root["emergencyMode"] = "ON"; + //} + //else + //{ + // root["emergencyMode"] = "OFF"; + //} + + switch (ConfigSettings.coordinator_mode) { + case COORDINATOR_MODE_USB: + root["mode"] = "Zigbee-to-USB"; + break; + case COORDINATOR_MODE_WIFI: + root["mode"] = "Zigbee-to-WiFi"; + break; + case COORDINATOR_MODE_LAN: + root["mode"] = "Zigbee-to-Ethernet"; + break; + default: + break; + } + root["zbfw"] = String(zbVer.zbRev); + root["hostname"] = ConfigSettings.hostname; + String mqttBuffer; + serializeJson(root, mqttBuffer); + DEBUG_PRINTLN(mqttBuffer); + clientPubSub.publish(topic.c_str(), mqttBuffer.c_str(), true); + ConfigSettings.mqttHeartbeatTime = millis() + (ConfigSettings.mqttInterval * 1000); +} + +void mqttPublishIo(String const &io, String const &state) +{ + if (clientPubSub.connected()) + { + String topic(ConfigSettings.mqttTopic); + topic = topic + "/io/" + io; + clientPubSub.publish(topic.c_str(), state.c_str(), true); + } +} + +void mqttCallback(char *topic, byte *payload, unsigned int length) +{ + char jjson[length + 1]; + memcpy(jjson, payload, length); + jjson[length + 1] = '\0'; + + DynamicJsonDocument jsonBuffer(1024); + + deserializeJson(jsonBuffer, jjson); + + const char *command = jsonBuffer["cmd"]; + + DEBUG_PRINT(F("mqtt Callback - ")); + DEBUG_PRINTLN(jjson); + + if (command) { + DEBUG_PRINT(F("mqtt cmd - ")); + DEBUG_PRINTLN(command); + if (strcmp(command, "rst_esp") == 0) + { + printLogMsg("ESP restart MQTT"); + ESP.restart(); + } + + if (strcmp(command, "rst_zig") == 0) + { + printLogMsg("Zigbee restart MQTT"); + zigbeeRestart(); + } + + if (strcmp(command, "enbl_bsl") == 0) + { + printLogMsg("Zigbee BSL enable MQTT"); + zigbeeEnableBSL(); + } + } + return; +} + +void mqttSubscribe(String topic) +{ + String mtopic(ConfigSettings.mqttTopic); + mtopic = mtopic + "/" + topic; + clientPubSub.subscribe(mtopic.c_str()); +} + +void mqttLoop() +{ + if (!clientPubSub.connected()) + { + if (ConfigSettings.mqttReconnectTime == 0) + { + //mqttReconnect(); + DEBUG_PRINTLN(F("mqttReconnect in 5 seconds")); + ConfigSettings.mqttReconnectTime = millis() + 5000; + } + else + { + if (ConfigSettings.mqttReconnectTime <= millis()) + { + mqttReconnect(); + } + } + } + else + { + clientPubSub.loop(); + if (ConfigSettings.mqttInterval > 0) + { + if (ConfigSettings.mqttHeartbeatTime <= millis()) + { + mqttPublishState(); + } + } + } +} + +void mqttPublishDiscovery() +{ + String mtopic(ConfigSettings.mqttTopic); + String deviceName = mtopic; //ConfigSettings.hostname; + + String topic; + DynamicJsonDocument buffJson(2048); + String mqttBuffer; + + DynamicJsonDocument via(1024); + char deviceIdArr[20]; + getDeviceID(deviceIdArr); + via["ids"] = String(deviceIdArr); + + //int lastAutoMsg = 9; + //if (ConfigSettings.board == 2) lastAutoMsg--; + + for (int i = 0; i <= 99; i++) + { + switch (i) + { + case 0: + { + DynamicJsonDocument dev(1024); + char deviceIdArr[20]; + getDeviceID(deviceIdArr); + + dev["ids"] = String(deviceIdArr); + dev["name"] = ConfigSettings.hostname; + dev["mf"] = "Zig Star"; + //dev["mdl"] = ConfigSettings.boardName; + + char verArr[25]; + const char * env = STRINGIFY(BUILD_ENV_NAME); + sprintf(verArr, "%s (%s)", VERSION, env); + dev["sw"] = String(verArr); + + topic = "homeassistant/button/" + deviceName + "/rst_esp/config"; + buffJson["name"] = "Restart ESP"; + buffJson["uniq_id"] = deviceName + "/rst_esp"; + buffJson["stat_t"] = mtopic + "/io/rst_esp"; + buffJson["cmd_t"] = mtopic + "/cmd"; + buffJson["icon"] = "mdi:restore-alert"; + buffJson["payload_press"] = "{cmd:\"rst_esp\"}"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["dev"] = dev; + break; + } + case 1: + { + topic = "homeassistant/button/" + deviceName + "/rst_zig/config"; + buffJson["name"] = "Restart Zigbee"; + buffJson["uniq_id"] = deviceName + "/rst_zig"; + buffJson["stat_t"] = mtopic + "/io/rst_zig"; + buffJson["cmd_t"] = mtopic + "/cmd"; + buffJson["icon"] = "mdi:restart"; + buffJson["payload_press"] = "{cmd:\"rst_zig\"}"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["dev"] = via; + break; + } + case 2: + { + topic = "homeassistant/button/" + deviceName + "/enbl_bsl/config"; + buffJson["name"] = "Enable BSL"; + buffJson["uniq_id"] = deviceName + "/enbl_bsl"; + buffJson["stat_t"] = mtopic + "/io/enbl_bsl"; + buffJson["cmd_t"] = mtopic + "/cmd"; + buffJson["icon"] = "mdi:flash"; + buffJson["payload_press"] = "{cmd:\"enbl_bsl\"}"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["dev"] = via; + break; + } + case 3: + { + topic = "homeassistant/binary_sensor/" + deviceName + "/socket/config"; + buffJson["name"] = "Socket"; + buffJson["uniq_id"] = deviceName + "/socket"; + buffJson["stat_t"] = mtopic + "/io/socket"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["dev_cla"] = "connectivity"; + buffJson["dev"] = via; + break; + } + //case 4: + //{ + // topic = "homeassistant/binary_sensor/" + deviceName + "/emrgncMd/config"; + // buffJson["name"] = "Emergency mode"; + // buffJson["uniq_id"] = deviceName + "/emrgncMd"; + // buffJson["stat_t"] = mtopic + "/state"; + // buffJson["avty_t"] = mtopic + "/avty"; + // buffJson["val_tpl"] = "{{ value_json.emergencyMode }}"; + // buffJson["json_attr_t"] = mtopic + "/state"; + // buffJson["dev_cla"] = "power"; + // buffJson["icon"] = "mdi:access-point-network"; + // buffJson["dev"] = via; + // break; + //} + case 4: + { + topic = "homeassistant/sensor/" + deviceName + "/uptime/config"; + buffJson["name"] = "Uptime"; + buffJson["uniq_id"] = deviceName + "/uptime"; + buffJson["stat_t"] = mtopic + "/state"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["val_tpl"] = "{{ value_json.uptime }}"; + buffJson["json_attr_t"] = mtopic + "/state"; + buffJson["icon"] = "mdi:clock"; + buffJson["dev"] = via; + break; + } + case 5: + { + topic = "homeassistant/sensor/" + deviceName + "/ip/config"; + buffJson["name"] = "IP"; + buffJson["uniq_id"] = deviceName + "/ip"; + buffJson["stat_t"] = mtopic + "/state"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["val_tpl"] = "{{ value_json.ip }}"; + buffJson["json_attr_t"] = mtopic + "/state"; + buffJson["icon"] = "mdi:check-network"; + buffJson["dev"] = via; + break; + } + case 6: + { + topic = "homeassistant/sensor/" + deviceName + "/temperature/config"; + buffJson["name"] = "ESP temperature"; + buffJson["uniq_id"] = deviceName + "/temperature"; + buffJson["stat_t"] = mtopic + "/state"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["val_tpl"] = "{{ value_json.temperature }}"; + buffJson["json_attr_t"] = mtopic + "/state"; + buffJson["icon"] = "mdi:coolant-temperature"; + buffJson["dev"] = via; + buffJson["dev_cla"] = "temperature"; + buffJson["stat_cla"] = "measurement"; + buffJson["unit_of_meas"] = "°C"; + break; + } + case 7: + { + topic = "homeassistant/sensor/" + deviceName + "/hostname/config"; + buffJson["name"] = "Hostname"; + buffJson["uniq_id"] = deviceName + "/hostname"; + buffJson["stat_t"] = mtopic + "/state"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["val_tpl"] = "{{ value_json.hostname }}"; + buffJson["json_attr_t"] = mtopic + "/state"; + buffJson["icon"] = "mdi:account-network"; + buffJson["dev"] = via; + break; + } + case 8: + { + topic = "homeassistant/sensor/" + deviceName + "/connections/config"; + buffJson["name"] = "Socket connections"; + buffJson["uniq_id"] = deviceName + "/connections"; + buffJson["stat_t"] = mtopic + "/state"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["val_tpl"] = "{{ value_json.connections }}"; + buffJson["json_attr_t"] = mtopic + "/state"; + buffJson["icon"] = "mdi:check-network-outline"; + buffJson["dev"] = via; + break; + } + case 9: + { + topic = "homeassistant/sensor/" + deviceName + "/mode/config"; + buffJson["name"] = "Mode"; + buffJson["uniq_id"] = deviceName + "/mode"; + buffJson["stat_t"] = mtopic + "/state"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["val_tpl"] = "{{ value_json.mode }}"; + buffJson["json_attr_t"] = mtopic + "/state"; + buffJson["icon"] = "mdi:access-point-network"; + buffJson["dev"] = via; + break; + } + case 10: + { + topic = "homeassistant/sensor/" + deviceName + "/zbfw/config"; + buffJson["name"] = "Zigbee fw rev"; + buffJson["uniq_id"] = deviceName + "/zbfw"; + buffJson["stat_t"] = mtopic + "/state"; + buffJson["avty_t"] = mtopic + "/avty"; + buffJson["val_tpl"] = "{{ value_json.zbfw }}"; + buffJson["json_attr_t"] = mtopic + "/state"; + buffJson["icon"] = "mdi:access-point-network"; + buffJson["dev"] = via; + break; + } + default: + topic = "error"; + break; + } + if (topic != "error") { + serializeJson(buffJson, mqttBuffer); + //DEBUG_PRINTLN(mqttBuffer); + mqttPublishMsg(topic, mqttBuffer, true); + buffJson.clear(); + mqttBuffer = ""; + } + else { i = 100; } + } +} \ No newline at end of file diff --git a/src/mqtt.h b/src/mqtt.h new file mode 100644 index 0000000..bbe2ebd --- /dev/null +++ b/src/mqtt.h @@ -0,0 +1,12 @@ + +void mqttConnectSetup(); +void mqttReconnect(); +void mqttCallback(char *topic, byte *payload, unsigned int length); +void mqttLoop(); +void mqttPublishState(); +void mqttOnConnect(); +void mqttPublishAvty(); +void mqttPublishDiscovery(); +void mqttPublishMsg(String topic, String msg, bool retain); +void mqttPublishIo(String const &io, String const &state); +void mqttSubscribe(String topic); \ No newline at end of file diff --git a/src/version.h b/src/version.h index 717c448..c7600d4 100644 --- a/src/version.h +++ b/src/version.h @@ -1,9 +1,9 @@ // AUTO GENERATED FILE, DO NOT EDIT #ifndef VERSION - #define VERSION "0.1.5" + #define VERSION "0.1.9" #endif #ifndef BUILD_TIMESTAMP - #define BUILD_TIMESTAMP "2023-09-06 12:01:02.723464" + #define BUILD_TIMESTAMP "2024-01-13 16:19:12.097501" #endif \ No newline at end of file diff --git a/src/web.cpp b/src/web.cpp index c76dea0..ad0bcd3 100644 --- a/src/web.cpp +++ b/src/web.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,8 @@ #include "config.h" #include "etc.h" #include "log.h" +#include "zb.h" +#include "webh/PAGE_MQTT.html.gz.h" #include "webh/PAGE_ABOUT.html.gz.h" #include "webh/PAGE_ETHERNET.html.gz.h" #include "webh/PAGE_GENERAL.html.gz.h" @@ -30,17 +33,30 @@ #include "webh/icons.svg.gz.h" #include "webh/logo.png.gz.h" #include "Ticker.h" +// #include "git_ca_cert.h" + +// #define HTTP_DOWNLOAD_UNIT_SIZE 3000 + +// #define HTTP_UPLOAD_BUFLEN 3000 + +// #define HTTP_MAX_DATA_WAIT 10000 // ms to wait for the client to send the request +// #define HTTP_MAX_POST_WAIT 10000 // ms to wait for POST data to arrive +// #define HTTP_MAX_SEND_WAIT 10000 // ms to wait for data chunk to be ACKed +// #define HTTP_MAX_CLOSE_WAIT 4000 // ms to wait for the client to close the connection extern struct ConfigSettingsStruct ConfigSettings; +extern struct zbVerStruct zbVer; + bool wifiWebSetupInProgress = false; extern const char *coordMode; -extern const char* configFileSystem; -extern const char* configFileWifi; -extern const char* configFileEther; -extern const char* configFileGeneral; -extern const char* configFileSecurity; -extern const char* configFileSerial; -extern const char* deviceModel; +extern const char *configFileSystem; +extern const char *configFileWifi; +extern const char *configFileEther; +extern const char *configFileMqtt; +extern const char *configFileGeneral; +extern const char *configFileSecurity; +extern const char *configFileSerial; +extern const char *deviceModel; const char *contTypeTextHtml = "text/html"; const char *contTypeTextJs = "text/javascript"; const char *contTypeTextCss = "text/css"; @@ -49,896 +65,1447 @@ const char *respHeaderName = "respValuesArr"; const char *contTypeJson = "application/json"; const char *contTypeText = "text/plain"; -enum API_PAGE_t : uint8_t { API_PAGE_ROOT, - API_PAGE_GENERAL, - API_PAGE_ETHERNET, - API_PAGE_WIFI, - API_PAGE_ZHA_Z2M, - API_PAGE_SECURITY, - API_PAGE_SYSTOOLS, - API_PAGE_ABOUT }; +const char *tempFile = "/config/fw.hex"; + +bool opened = false; +File fwFile; + +extern bool loadConfigMqtt(); + +enum API_PAGE_t : uint8_t +{ + API_PAGE_ROOT, + API_PAGE_GENERAL, + API_PAGE_ETHERNET, + API_PAGE_WIFI, + API_PAGE_ZHA_Z2M, + API_PAGE_SECURITY, + API_PAGE_SYSTOOLS, + API_PAGE_ABOUT, + API_PAGE_MQTT +}; WebServer serverWeb(80); -HTTPClient clientWeb; +// HTTPClient clientWeb; +WiFiClient eventsClient; -void webServerHandleClient() { +void webServerHandleClient() +{ serverWeb.handleClient(); } -void initWebServer() { - serverWeb.on("/js/bootstrap.min.js", []() { sendGzip(contTypeTextJs, bootstrap_min_js_gz, bootstrap_min_js_gz_len); }); - serverWeb.on("/js/masonry.js", []() { sendGzip(contTypeTextJs, masonry_js_gz, masonry_js_gz_len); }); - serverWeb.on("/js/functions.js", []() { sendGzip(contTypeTextJs, functions_js_gz, functions_js_gz_len); }); - serverWeb.on("/js/jquery-min.js", []() { sendGzip(contTypeTextJs, jquery_min_js_gz, jquery_min_js_gz_len); }); - serverWeb.on("/css/required.css", []() { sendGzip(contTypeTextJs, required_css_gz, required_css_gz_len); }); - serverWeb.on("/favicon.ico", []() { sendGzip("img/png", favicon_ico_gz, favicon_ico_gz_len); }); - serverWeb.on("/logo.png", []() { sendGzip("img/png", logo_png_gz, logo_png_gz_len); }); - serverWeb.on("/icons.svg", []() { sendGzip("image/svg+xml", icons_svg_gz, icons_svg_gz_len); }); - serverWeb.onNotFound([]() { serverWeb.send(HTTP_CODE_OK, contTypeText, F("URL NOT FOUND")); }); // handleNotFound); - serverWeb.on("/", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); - serverWeb.on("/general", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); - serverWeb.on("/security", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); - serverWeb.on("/wifi", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); - serverWeb.on("/ethernet", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); - serverWeb.on("/zha-z2m", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); - serverWeb.on("/about", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); - serverWeb.on("/sys-tools", []() { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); +void initWebServer() +{ + serverWeb.on("/js/bootstrap.min.js", []() + { sendGzip(contTypeTextJs, bootstrap_min_js_gz, bootstrap_min_js_gz_len); }); + serverWeb.on("/js/masonry.js", []() + { sendGzip(contTypeTextJs, masonry_js_gz, masonry_js_gz_len); }); + serverWeb.on("/js/functions.js", []() + { sendGzip(contTypeTextJs, functions_js_gz, functions_js_gz_len); }); + serverWeb.on("/js/jquery-min.js", []() + { sendGzip(contTypeTextJs, jquery_min_js_gz, jquery_min_js_gz_len); }); + serverWeb.on("/css/required.css", []() + { sendGzip(contTypeTextJs, required_css_gz, required_css_gz_len); }); + serverWeb.on("/favicon.ico", []() + { sendGzip("img/png", favicon_ico_gz, favicon_ico_gz_len); }); + serverWeb.on("/logo.png", []() + { sendGzip("img/png", logo_png_gz, logo_png_gz_len); }); + serverWeb.on("/icons.svg", []() + { sendGzip("image/svg+xml", icons_svg_gz, icons_svg_gz_len); }); + serverWeb.onNotFound([]() + // { serverWeb.send(HTTP_CODE_OK, contTypeText, F("URL NOT FOUND")); }); // handleNotFound); + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/general", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/security", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/wifi", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/ethernet", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/zha-z2m", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/about", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/sys-tools", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); + serverWeb.on("/mqtt", []() + { sendGzip(contTypeTextHtml, PAGE_LOADER_html_gz, PAGE_LOADER_html_gz_len); }); serverWeb.on("/saveParams", HTTP_POST, handleSaveParams); serverWeb.on("/cmdZigRST", handleZigbeeRestart); serverWeb.on("/cmdZigBSL", handleZigbeeBSL); serverWeb.on("/saveFile", handleSavefile); - serverWeb.on("/switch/firmware_update/toggle", handleZigbeeBSL); // for cc-2538.py ESPHome edition back compatibility | will be disabled on 1.0.0 + serverWeb.on("/switch/firmware_update/toggle", handleZigbeeBSL); // for cc-2538.py ESPHome edition back compatibility | will be disabled someday serverWeb.on("/api", handleApi); - serverWeb.on("/logout", []() { + serverWeb.on("/status", handleStatus); + serverWeb.on("/logout", []() + { serverWeb.sendHeader(F("Content-Encoding"), F("gzip")); - serverWeb.send_P(401, contTypeTextHtml, (const char *)PAGE_LOGOUT_html_gz, PAGE_LOGOUT_html_gz_len); - }); + serverWeb.send_P(401, contTypeTextHtml, (const char *)PAGE_LOGOUT_html_gz, PAGE_LOGOUT_html_gz_len); }); - /*handling uploading firmware file */ + serverWeb.on("/events", handleEvents); + /*handling uploading esp32 firmware file */ serverWeb.on( - "/update", HTTP_POST, []() { + "/update", HTTP_POST, []() + { serverWeb.sendHeader("Connection", "close"); - serverWeb.send(HTTP_CODE_OK, contTypeText, (Update.hasError()) ? "FAIL" : "OK"); - ESP.restart(); }, - []() { - HTTPUpload &upload = serverWeb.upload(); - if (upload.status == UPLOAD_FILE_START) { - if(!checkAuth()) return; - Serial.printf("Update: %s\n", upload.filename.c_str()); - if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { // start with max available size - Update.printError(Serial); - } - } else if (upload.status == UPLOAD_FILE_WRITE) { - /* flashing firmware to ESP*/ - if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { - Update.printError(Serial); - } - } else if (upload.status == UPLOAD_FILE_END) { - if (Update.end(true)) { // true to set the size to the current progress - Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); - serverWeb.send(HTTP_CODE_OK, contTypeText, (Update.hasError()) ? "FAIL" : "OK"); - } else { - Update.printError(Serial); - } + serverWeb.send(HTTP_CODE_OK, contTypeText, (Update.hasError()) ? "FAIL" : "OK"); }, + []() + { + HTTPUpload &upload = serverWeb.upload(); + static long contentLength = 0; + if (upload.status == UPLOAD_FILE_START) + { + if (!checkAuth()) + return; + + Serial.println("hostHeader: " + serverWeb.hostHeader()); + // Serial.println("header Content-Length: " + serverWeb.header("Content-Length")); + contentLength = serverWeb.header("Content-Length").toInt(); + Serial.println("contentLength: " + String(contentLength)); + + DEBUG_PRINTLN("Update ESP from file " + String(upload.filename.c_str()) + " size: " + String(upload.totalSize)); + DEBUG_PRINTLN("upload.currentSize " + String(upload.currentSize)); + if (!Update.begin(contentLength)) + { // start with max available size + Update.printError(Serial); + } + Update.onProgress(progressFunc); + } + else if (upload.status == UPLOAD_FILE_WRITE) + { + /* flashing firmware to ESP*/ + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) + { + Update.printError(Serial); + } + // DEBUG_PRINT("."); + // DEBUG_PRINT(upload.currentSize); + } + else if (upload.status == UPLOAD_FILE_END) + { + // DEBUG_PRINTLN("Finish..."); + if (Update.end(true)) + { // true to set the size to the current progress + // DEBUG_PRINTLN(""); + // DEBUG_PRINTLN("Update Success: " + upload.totalSize); + DEBUG_PRINTLN("Update success. Rebooting..."); + ESP.restart(); + // serverWeb.send(HTTP_CODE_OK, contTypeText, (Update.hasError()) ? "FAIL" : "OK"); } + else + { + DEBUG_PRINTLN("Update error: "); + Update.printError(Serial); + } + } + }); + /*handling uploading zigbee firmware file */ + serverWeb.on( + "/updateZB", HTTP_POST, []() + { + serverWeb.sendHeader("Connection", "close"); + // serverWeb.send(HTTP_CODE_OK, contTypeText, (Update.hasError()) ? "FAIL" : "OK"); + serverWeb.send(HTTP_CODE_OK, contTypeText, "Upload OK. Try to flash..."); + // ESP.restart(); flash zigbee here + }, + []() + { + HTTPUpload &upload = serverWeb.upload(); + if (opened == false) + { + // LittleFS.end(); + opened = true; + DEBUG_PRINTLN("Try to removed file " + String(tempFile)); + if (LittleFS.remove(tempFile)) + { + DEBUG_PRINTLN(F("Removed file - OK")); + } + else + { + DEBUG_PRINTLN(F("Error while removing file")); + } + delay(250); + fwFile = LittleFS.open(tempFile, FILE_WRITE); + + if (!fwFile) + { + DEBUG_PRINTLN(F("- failed to open file for writing")); + return; + } + } + + if (upload.status == UPLOAD_FILE_START) + { + if (!checkAuth()) + return; + DEBUG_PRINTLN("Upload zigbee fw file: " + String(upload.filename.c_str())); + printLogMsg("[ZB_FW] upload:" + String(upload.filename.c_str())); + // DEBUG_PRINTLN("size: " + String(String(upload.totalSize).c_str())); + // printLogMsg("size:" + String(String(upload.totalSize).c_str())); + } + else if (upload.status == UPLOAD_FILE_WRITE) + { + // DEBUG_PRINT("."); +/* //String temp; + size_t size = upload.totalSize + if (size) + { + // read up to 128 byte + int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); + */ + + //for (int i = 0; i < sizeof(upload.buf); i++) + //{ + // char a = upload.buf[i]; + // Serial.write(a); + //} + //DEBUG_PRINTLN(temp); + + fwFile.write(upload.buf, sizeof(upload.buf)); + } + else if (upload.status == UPLOAD_FILE_END) + { + // DEBUG_PRINTLN(F("Finish!")); + delay(500); + fwFile.close(); + delay(500); + DEBUG_PRINTLN(F("UPLOAD_FILE_END")); + printLogMsg("[ZB_FW] upload finish!"); + opened = false; + + DEBUG_PRINTLN("Total file size: " + String(upload.totalSize)); + + checkFwHex(tempFile); + } }); + + const char *headerkeys[] = {"Content-Length"}; + size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *); + serverWeb.collectHeaders(headerkeys, headerkeyssize); serverWeb.begin(); DEBUG_PRINTLN(F("webserver setup done")); } -void sendGzip(const char *contentType, const uint8_t content[], uint16_t contentLen) { +void handleEvents() +{ + eventsClient = serverWeb.client(); + if (eventsClient) + { // send events header + eventsClient.println("HTTP/1.1 200 OK"); + eventsClient.println("Content-Type: text/event-stream;"); + eventsClient.println("Connection: close"); + eventsClient.println("Access-Control-Allow-Origin: *"); + eventsClient.println("Cache-Control: no-cache"); + eventsClient.println(); + eventsClient.flush(); + } +} + +void sendEvent(const char *event, const uint8_t evsz, const String data) +{ + if (eventsClient) + { + char evnmArr[10 + evsz]; + sprintf(evnmArr, "event: %s\n", event); + eventsClient.print(evnmArr); + eventsClient.print(String("data: ") + data + "\n\n"); + // eventsClient.println(); + eventsClient.flush(); + } +} + +void sendGzip(const char *contentType, const uint8_t content[], uint16_t contentLen) +{ serverWeb.sendHeader(F("Content-Encoding"), F("gzip")); serverWeb.send_P(HTTP_CODE_OK, contentType, (const char *)content, contentLen); } -void hex2bin(uint8_t *out, const char *in){ - //uint8_t sz = 0; - while (*in) { - while (*in == ' ') in++; // skip spaces - if (!*in) break; - uint8_t c = *in>='a' ? *in-'a'+10 : *in>='A' ? *in-'A'+10 : *in-'0'; +void hex2bin(uint8_t *out, const char *in) +{ + // uint8_t sz = 0; + while (*in) + { + while (*in == ' ') + in++; // skip spaces + if (!*in) + break; + uint8_t c = *in >= 'a' ? *in - 'a' + 10 : *in >= 'A' ? *in - 'A' + 10 + : *in - '0'; in++; c <<= 4; - if (!*in) break; - c |= *in>='a' ? *in-'a'+10 : *in>='A' ? *in-'A'+10 : *in-'0'; + if (!*in) + break; + c |= *in >= 'a' ? *in - 'a' + 10 : *in >= 'A' ? *in - 'A' + 10 + : *in - '0'; in++; *out++ = c; - //sz++; + // sz++; } } -void handleApi() { // http://192.168.0.116/api?action=0&page=0 - enum API_ACTION_t : uint8_t { API_GET_PAGE, - API_GET_PARAM, - API_STARTWIFISCAN, - API_WIFISCANSTATUS, - API_GET_FILELIST, - API_GET_FILE, - API_SEND_HEX, - API_WIFICONNECTSTAT, - API_CMD, - API_GET_LOG }; +void handleApi() +{ // http://192.168.0.116/api?action=0&page=0 + enum API_ACTION_t : uint8_t + { + API_GET_PAGE, + API_GET_PARAM, + API_STARTWIFISCAN, + API_WIFISCANSTATUS, + API_GET_FILELIST, + API_GET_FILE, + API_SEND_HEX, + API_WIFICONNECTSTAT, + API_CMD, + API_GET_LOG, + API_FLASH_ZB + }; const char *action = "action"; const char *page = "page"; const char *Authentication = "Authentication"; - const char* param = "param"; - const char* wrongArgs = "wrong args"; - const char* ok = "ok"; + const char *param = "param"; + const char *wrongArgs = "wrong args"; + const char *ok = "ok"; - if(ConfigSettings.webAuth){ - if(!checkAuth()){//Authentication + if (ConfigSettings.webAuth) + { + if (!checkAuth()) + { // Authentication serverWeb.sendHeader(Authentication, "fail"); serverWeb.send(401, contTypeText, F("wrong login or password")); return; - }else{ + } + else + { serverWeb.sendHeader(Authentication, ok); } } - if (serverWeb.argName(0) != action) { - DEBUG_PRINTLN(F("[handleApi] wrong arg 'action'")); + if (serverWeb.argName(0) != action) + { + DEBUG_PRINT(F("[handleApi] wrong arg 'action' ")); DEBUG_PRINTLN(serverWeb.argName(0)); serverWeb.send(500, contTypeText, wrongArgs); - } else { + } + else + { const uint8_t action = serverWeb.arg(action).toInt(); - DEBUG_PRINTLN(F("[handleApi] arg 0 is:")); - DEBUG_PRINTLN(action); - switch (action) { - case API_GET_LOG:{ - String result; - result = logPrint(); - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - } - break; - case API_CMD:{ - enum CMD_t : uint8_t {//cmd list for buttons starts from 0 - CMD_ZB_ROUTER_RECON, - CMD_ZB_RST, - CMD_ZB_BSL, - CMD_ESP_RES, - CMD_ADAP_LAN, - CMD_ADAP_USB, - CMD_LEDY_TOG, - CMD_LEDB_TOG, - CMD_CLEAR_LOG - }; - String result = wrongArgs; - const char* argCmd = "cmd"; - if(serverWeb.hasArg(argCmd)){ - result = "ok"; - switch (serverWeb.arg(argCmd).toInt()){ - case CMD_CLEAR_LOG: - logClear(); - break; - case CMD_ZB_ROUTER_RECON: - digitalWrite(CC2652P_FLSH, 0); - delay(250); - digitalWrite(CC2652P_FLSH, 1); - printLogMsg("Router reconnect"); - DEBUG_PRINTLN(F("Router Reconnect")); - break; - case CMD_ZB_RST: - zigbeeRestart(); - break; - case CMD_ZB_BSL: - zigbeeEnableBSL(); - break; - case CMD_ESP_RES: - serverWeb.send(HTTP_CODE_OK, contTypeText, result); - ESP.restart(); - break; - case CMD_ADAP_LAN: - adapterModeLAN(); - break; - case CMD_ADAP_USB: - adapterModeUSB(); - break; - case CMD_LEDY_TOG: - ledYellowToggle(); - break; - case CMD_LEDB_TOG: - ledBlueToggle(); - break; - - default: - break; + //DEBUG_PRINT(F("[handleApi] arg 0 is: ")); + //DEBUG_PRINTLN(action); + switch (action) + { + case API_FLASH_ZB: + { + ConfigSettings.zbFlashing = 1; + const char *fwurlArg = "fwurl"; + const uint8_t eventLen = 11; + const char *tagZB_FW_info = "ZB_FW_info"; + const char *tagZB_FW_err = "ZB_FW_err"; + const char *tagZB_FW_progress = "ZB_FW_prgs"; + if (serverWeb.hasArg(fwurlArg)) + { + String fwUrl = serverWeb.arg(fwurlArg); + serverWeb.send(HTTP_CODE_OK, contTypeText, ok); + uint8_t evWaitCount = 0; + while (!eventsClient.connected() && evWaitCount < 200) + { // wait for events + webServerHandleClient(); + delay(25); + evWaitCount++; + } + // sendEvent(tag, eventLen, String("FW Url: ") + fwUrl); + setClock(); + HTTPClient https; + WiFiClientSecure client; + client.setInsecure(); + https.begin(client, fwUrl); // https://raw.githubusercontent.com/Tarik2142/devHost/main/coordinator_20211217.bin + https.addHeader("Content-Type", "application/octet-stream"); + const int16_t httpsCode = https.GET(); + // sendEvent(tag, eventLen, String("REQ result: ") + httpsCode); + if (httpsCode == HTTP_CODE_OK) + { + const uint32_t fwSize = https.getSize(); + DEBUG_PRINTLN(F("[start] Downloading firmware...")); + sendEvent(tagZB_FW_info, eventLen, "[start]"); + sendEvent(tagZB_FW_info, eventLen, "Downloading firmware..."); + const char *tempFile2 = "/config/coordinator.bin"; + LittleFS.remove(tempFile2); + File fwFile = LittleFS.open(tempFile2, "w", 1); + uint8_t buff[4]; + uint32_t downloaded = 0; + while (client.readBytes(buff, sizeof(buff))) + { + downloaded += fwFile.write(buff, sizeof(buff)); + if (!(downloaded % 8192)) + { + const uint8_t d = ((float)downloaded / fwSize) * 100; + sendEvent(tagZB_FW_progress, eventLen, String(d)); + } } - serverWeb.send(HTTP_CODE_OK, contTypeText, result); + fwFile.close(); + // in development } - } - break; - case API_WIFICONNECTSTAT:{ - String result; - StaticJsonDocument<70> doc; - const char* connected = "connected"; - if(WiFi.status() == WL_CONNECTED){ - doc[connected] = true; - doc["ip"] = WiFi.localIP().toString(); - }else{ - doc[connected] = false; + else + { + DEBUG_PRINTLN("REQ error: http_code " + String(httpsCode)); + serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, String(httpsCode)); + sendEvent(tagZB_FW_err, eventLen, "REQ error: http_code " + String(httpsCode)); } - serializeJson(doc, result); - serverWeb.send(HTTP_CODE_OK, contTypeJson, result); } - break; - case API_SEND_HEX:{ - String result = wrongArgs; - const char* argSize = "size"; - const char* argHex = "hex"; - if(serverWeb.hasArg(argHex) && serverWeb.hasArg(argSize)){ - result = ok; - uint8_t size = serverWeb.arg(argSize).toInt(); - byte resp[size]; - hex2bin(resp, serverWeb.arg(argHex).c_str()); - Serial2.write(resp, size); + else + { + serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, "missing arg 1"); + } + ConfigSettings.zbFlashing = 0; + } + break; + case API_GET_LOG: + { + String result; + result = logPrint(); + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + } + break; + case API_CMD: + { + enum CMD_t : uint8_t + { // cmd list for buttons starts from 0 + CMD_ZB_ROUTER_RECON, + CMD_ZB_RST, + CMD_ZB_BSL, + CMD_ESP_RES, + CMD_ADAP_LAN, + CMD_ADAP_USB, + CMD_LED_PWR_TOG, + CMD_LED_USB_TOG, + CMD_CLEAR_LOG, + CMD_ESP_UPD_GIT, + CMD_ZB_CHK_REV, + CMD_ZB_CHK_CON, + CMD_ZB_LED_TOG + }; + String result = wrongArgs; + const char *argCmd = "cmd"; + if (serverWeb.hasArg(argCmd)) + { + result = "ok"; + switch (serverWeb.arg(argCmd).toInt()) + { + case CMD_CLEAR_LOG: + logClear(); + break; + case CMD_ZB_ROUTER_RECON: + zigbeeRouterRejoin(); + break; + case CMD_ZB_RST: + zigbeeRestart(); + break; + case CMD_ZB_BSL: + zigbeeEnableBSL(); + break; + case CMD_ESP_RES: + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + ESP.restart(); + break; + case CMD_ADAP_LAN: + adapterModeLAN(); + break; + case CMD_ADAP_USB: + adapterModeUSB(); + break; + case CMD_LED_PWR_TOG: + ledPowerToggle(); + break; + case CMD_LED_USB_TOG: + ledUSBToggle(); + break; + case CMD_ESP_UPD_GIT: + getEspUpdate(UPD_FILE); + // downloadURLAndUpdateESP(UPD_FILE); + break; + case CMD_ZB_CHK_REV: + getZbVer(); + break; + case CMD_ZB_CHK_CON: + zbCheck(); + break; + case CMD_ZB_LED_TOG: + zbLedToggle(); + break; + default: + break; } serverWeb.send(HTTP_CODE_OK, contTypeText, result); } - break; - case API_GET_FILE:{ - String result = wrongArgs; - const char* argFilename = "filename"; - if(serverWeb.hasArg(argFilename)){ - String filename = "/config/" + serverWeb.arg(argFilename); - File file = LittleFS.open(filename, "r"); - if (!file) return; - result = ""; - while (file.available()) { - result += (char)file.read(); - } - file.close(); + } + break; + case API_WIFICONNECTSTAT: + { + String result; + StaticJsonDocument<70> doc; + const char *connected = "connected"; + if (WiFi.status() == WL_CONNECTED) + { + doc[connected] = true; + doc["ip"] = WiFi.localIP().toString(); + } + else + { + doc[connected] = false; + } + serializeJson(doc, result); + serverWeb.send(HTTP_CODE_OK, contTypeJson, result); + } + break; + case API_SEND_HEX: + { + String result = wrongArgs; + const char *argSize = "size"; + const char *argHex = "hex"; + DEBUG_PRINTLN(F("[send_hex]")); + if (serverWeb.hasArg(argHex) && serverWeb.hasArg(argSize)) + { + result = ok; + DEBUG_PRINTLN(F("try...")); + DEBUG_PRINTLN(serverWeb.arg(argHex).c_str()); + uint8_t size = serverWeb.arg(argSize).toInt(); + byte resp[size]; + hex2bin(resp, serverWeb.arg(argHex).c_str()); + + // Serial2.write(resp, size); + } + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + } + break; + case API_GET_FILE: + { + String result = wrongArgs; + const char *argFilename = "filename"; + if (serverWeb.hasArg(argFilename)) + { + String filename = "/config/" + serverWeb.arg(argFilename); + File file = LittleFS.open(filename, "r"); + if (!file) + return; + result = ""; + while (file.available()) + { + result += (char)file.read(); } - serverWeb.send(HTTP_CODE_OK, contTypeText, result); + file.close(); } - break; - case API_GET_PARAM:{ - String resp = wrongArgs; - if(serverWeb.hasArg(param)){ - if (serverWeb.arg(param) == "refreshLogs"){ - resp = (String)ConfigSettings.refreshLogs; - }else if(serverWeb.arg(param) == "coordMode"){ - if(wifiWebSetupInProgress){ - resp = "1"; - }else{ - resp = (String)ConfigSettings.coordinator_mode; - } - } - } - serverWeb.send(HTTP_CODE_OK, contTypeText, resp); + serverWeb.send(HTTP_CODE_OK, contTypeText, result); + } + break; + case API_GET_PARAM: + { + String resp = wrongArgs; + if (serverWeb.hasArg(param)) + { + if (serverWeb.arg(param) == "refreshLogs") + { + resp = (String)ConfigSettings.refreshLogs; } - break; - case API_STARTWIFISCAN: - if (WiFi.getMode() == WIFI_OFF) { // enable wifi for scan - WiFi.mode(WIFI_STA); + else if (serverWeb.arg(param) == "coordMode") + { + if (wifiWebSetupInProgress) + { + resp = "1"; } - // } else if (WiFi.getMode() == WIFI_AP) { // enable sta for scan - // WiFi.mode(WIFI_AP_STA); - // } - WiFi.scanNetworks(true); - serverWeb.send(HTTP_CODE_OK, contTypeTextHtml, ok); - break; - case API_WIFISCANSTATUS: { - static uint8_t timeout = 0; - DynamicJsonDocument doc(1024); - String result = ""; - int16_t scanRes = WiFi.scanComplete(); - const char* scanDone = "scanDone"; - doc[scanDone] = false; - if (scanRes == -2) { - WiFi.scanNetworks(true); - } else if (scanRes > 0) { - doc[scanDone] = true; - JsonArray wifi = doc.createNestedArray("wifi"); - for (int i = 0; i < scanRes; ++i) { - JsonObject wifi_0 = wifi.createNestedObject(); - wifi_0["ssid"] = WiFi.SSID(i); - wifi_0["rssi"] = WiFi.RSSI(i); - wifi_0["channel"] = WiFi.channel(i); - wifi_0["secure"] = WiFi.encryptionType(i); + else + { + resp = (String)ConfigSettings.coordinator_mode; } - WiFi.scanDelete(); - //if (ConfigSettings.coordinator_mode == COORDINATOR_MODE_LAN) WiFi.mode(WIFI_OFF); no more wifi scan in lan mode - } - if (timeout < 10) { - timeout++; - } else { - doc[scanDone] = true; - WiFi.scanDelete(); - timeout = 0; - //if (ConfigSettings.coordinator_mode == COORDINATOR_MODE_LAN) WiFi.mode(WIFI_OFF); } - - serializeJson(doc, result); - serverWeb.send(HTTP_CODE_OK, contTypeJson, result); - break; - } - case API_GET_PAGE: - if (!serverWeb.arg(page).length() > 0) { - DEBUG_PRINTLN(F("[handleApi] wrong arg 'page'")); - DEBUG_PRINTLN(serverWeb.argName(1)); - serverWeb.send(500, contTypeText, wrongArgs); - return; + else if (serverWeb.arg(param) == "zbRev") + { + resp = zbVer.zbRev > 0 ? (String)zbVer.zbRev : "Unknown"; } - switch (serverWeb.arg(page).toInt()) { - case API_PAGE_ROOT: - handleRoot(); - sendGzip(contTypeTextHtml, PAGE_ROOT_html_gz, PAGE_ROOT_html_gz_len); - break; - case API_PAGE_GENERAL: - handleGeneral(); - sendGzip(contTypeTextHtml, PAGE_GENERAL_html_gz, PAGE_GENERAL_html_gz_len); - break; - case API_PAGE_ETHERNET: - handleEther(); - sendGzip(contTypeTextHtml, PAGE_ETHERNET_html_gz, PAGE_ETHERNET_html_gz_len); - break; - case API_PAGE_WIFI: - handleWifi(); - sendGzip(contTypeTextHtml, PAGE_WIFI_html_gz, PAGE_WIFI_html_gz_len); - break; - case API_PAGE_ZHA_Z2M: - handleSerial(); - sendGzip(contTypeTextHtml, PAGE_SERIAL_html_gz, PAGE_SERIAL_html_gz_len); - break; - case API_PAGE_SECURITY: - handleSecurity(); - sendGzip(contTypeTextHtml, PAGE_SECURITY_html_gz, PAGE_SECURITY_html_gz_len); - break; - case API_PAGE_SYSTOOLS: - handleSysTools(); - sendGzip(contTypeTextHtml, PAGE_SYSTOOLS_html_gz, PAGE_SYSTOOLS_html_gz_len); - break; - case API_PAGE_ABOUT: - //handleAbout(); - sendGzip(contTypeTextHtml, PAGE_ABOUT_html_gz, PAGE_ABOUT_html_gz_len); - break; - - default: - break; + else if (serverWeb.arg(param) == "espVer") + { + resp = VERSION; } - break; - case API_GET_FILELIST:{ - String fileList = ""; - DynamicJsonDocument doc(512); - JsonArray files = doc.createNestedArray("files"); - File root = LittleFS.open("/config"); - File file = root.openNextFile(); - while (file) { - JsonObject jsonfile = files.createNestedObject(); - jsonfile["filename"] = String(file.name()); - jsonfile["size"] = file.size(); - file = root.openNextFile(); + } + serverWeb.send(HTTP_CODE_OK, contTypeText, resp); + } + break; + case API_STARTWIFISCAN: + if (WiFi.getMode() == WIFI_OFF) + { // enable wifi for scan + WiFi.mode(WIFI_STA); + } + // } else if (WiFi.getMode() == WIFI_AP) { // enable sta for scan + // WiFi.mode(WIFI_AP_STA); + // } + WiFi.scanNetworks(true); + serverWeb.send(HTTP_CODE_OK, contTypeTextHtml, ok); + break; + case API_WIFISCANSTATUS: + { + static uint8_t timeout = 0; + DynamicJsonDocument doc(1024); + String result = ""; + int16_t scanRes = WiFi.scanComplete(); + const char *scanDone = "scanDone"; + doc[scanDone] = false; + if (scanRes == -2) + { + WiFi.scanNetworks(true); + } + else if (scanRes > 0) + { + doc[scanDone] = true; + JsonArray wifi = doc.createNestedArray("wifi"); + for (int i = 0; i < scanRes; ++i) + { + JsonObject wifi_0 = wifi.createNestedObject(); + wifi_0["ssid"] = WiFi.SSID(i); + wifi_0["rssi"] = WiFi.RSSI(i); + wifi_0["channel"] = WiFi.channel(i); + wifi_0["secure"] = WiFi.encryptionType(i); } - serializeJson(doc, fileList); - serverWeb.send(HTTP_CODE_OK, contTypeJson, fileList); - break; + WiFi.scanDelete(); + // if (ConfigSettings.coordinator_mode == COORDINATOR_MODE_LAN) WiFi.mode(WIFI_OFF); no more wifi scan in lan mode + } + if (timeout < 10) + { + timeout++; + } + else + { + doc[scanDone] = true; + WiFi.scanDelete(); + timeout = 0; + // if (ConfigSettings.coordinator_mode == COORDINATOR_MODE_LAN) WiFi.mode(WIFI_OFF); } + serializeJson(doc, result); + serverWeb.send(HTTP_CODE_OK, contTypeJson, result); + break; + } + case API_GET_PAGE: + if (!serverWeb.arg(page).length() > 0) + { + DEBUG_PRINTLN(F("[handleApi] wrong arg 'page'")); + DEBUG_PRINTLN(serverWeb.argName(1)); + serverWeb.send(500, contTypeText, wrongArgs); + return; + } + switch (serverWeb.arg(page).toInt()) + { + case API_PAGE_ROOT: + handleRoot(); + sendGzip(contTypeTextHtml, PAGE_ROOT_html_gz, PAGE_ROOT_html_gz_len); + break; + case API_PAGE_GENERAL: + handleGeneral(); + sendGzip(contTypeTextHtml, PAGE_GENERAL_html_gz, PAGE_GENERAL_html_gz_len); + break; + case API_PAGE_ETHERNET: + handleEther(); + sendGzip(contTypeTextHtml, PAGE_ETHERNET_html_gz, PAGE_ETHERNET_html_gz_len); + break; + case API_PAGE_WIFI: + handleWifi(); + sendGzip(contTypeTextHtml, PAGE_WIFI_html_gz, PAGE_WIFI_html_gz_len); + break; + case API_PAGE_ZHA_Z2M: + handleSerial(); + sendGzip(contTypeTextHtml, PAGE_SERIAL_html_gz, PAGE_SERIAL_html_gz_len); + break; + case API_PAGE_SECURITY: + handleSecurity(); + sendGzip(contTypeTextHtml, PAGE_SECURITY_html_gz, PAGE_SECURITY_html_gz_len); + break; + case API_PAGE_SYSTOOLS: + handleSysTools(); + sendGzip(contTypeTextHtml, PAGE_SYSTOOLS_html_gz, PAGE_SYSTOOLS_html_gz_len); + break; + case API_PAGE_ABOUT: + // handleAbout(); + sendGzip(contTypeTextHtml, PAGE_ABOUT_html_gz, PAGE_ABOUT_html_gz_len); + break; + case API_PAGE_MQTT: + handleMqtt(); + sendGzip(contTypeTextHtml, PAGE_MQTT_html_gz, PAGE_MQTT_html_gz_len); + break; default: - DEBUG_PRINTLN(F("[handleApi] switch (action) err")); break; + } + break; + case API_GET_FILELIST: + { + String fileList = ""; + DynamicJsonDocument doc(512); + JsonArray files = doc.createNestedArray("files"); + File root = LittleFS.open("/config"); + File file = root.openNextFile(); + while (file) + { + JsonObject jsonfile = files.createNestedObject(); + jsonfile["filename"] = String(file.name()); + jsonfile["size"] = file.size(); + file = root.openNextFile(); + } + serializeJson(doc, fileList); + serverWeb.send(HTTP_CODE_OK, contTypeJson, fileList); + break; + } + + default: + DEBUG_PRINTLN(F("[handleApi] switch (action) err")); + break; } } } -void handleSaveParams(){ +void handleSaveParams() +{ String result; DynamicJsonDocument doc(512); - const char* pageId = "pageId"; - const char* on = "on"; + const char *pageId = "pageId"; + const char *on = "on"; File configFile; const uint8_t one = 1; const uint8_t zero = 0; - if (serverWeb.hasArg(pageId)){ - switch (serverWeb.arg(pageId).toInt()){ - case API_PAGE_GENERAL:{ - configFile = LittleFS.open(configFileGeneral, FILE_READ); - deserializeJson(doc, configFile); - configFile.close(); - - if (serverWeb.hasArg(coordMode)) { - const uint8_t mode = serverWeb.arg(coordMode).toInt(); - if(mode <= 2 && mode >= zero){ - //ConfigSettings.coordinator_mode = static_cast(mode); - if(mode == 1) wifiWebSetupInProgress = true; - doc[coordMode] = static_cast(mode); - } - } - const char* keepWeb = "keepWeb"; - if (serverWeb.arg(keepWeb) == on) { - doc[keepWeb] = one; - } else { - doc[keepWeb] = zero; - } - const char* disableLedYellow = "disableLedYellow"; - if (serverWeb.arg(disableLedYellow) == on) { - doc[disableLedYellow] = one; - } else { - doc[disableLedYellow] = zero; - } - const char* disableLedBlue = "disableLedBlue"; - if (serverWeb.arg(disableLedBlue) == on) { - doc[disableLedBlue] = one; - } else { - doc[disableLedBlue] = zero; + if (serverWeb.hasArg(pageId)) + { + switch (serverWeb.arg(pageId).toInt()) + { + case API_PAGE_GENERAL: + { + configFile = LittleFS.open(configFileGeneral, FILE_READ); + deserializeJson(doc, configFile); + configFile.close(); + + if (serverWeb.hasArg(coordMode)) + { + const uint8_t mode = serverWeb.arg(coordMode).toInt(); + if (mode <= 2 && mode >= zero) + { + // ConfigSettings.coordinator_mode = static_cast(mode); + if (mode == 1) + wifiWebSetupInProgress = true; + doc[coordMode] = static_cast(mode); } - configFile = LittleFS.open(configFileGeneral, FILE_WRITE); - serializeJson(doc, configFile); - configFile.close(); } - break; - case API_PAGE_ETHERNET:{ - configFile = LittleFS.open(configFileEther, FILE_READ); - deserializeJson(doc, configFile); - configFile.close(); - doc["ip"] = serverWeb.arg("ipAddress"); - doc["mask"] = serverWeb.arg("ipMask"); - doc["gw"] = serverWeb.arg("ipGW"); - const char* dhcp = "dhcp"; - if (serverWeb.arg(dhcp) == "on") { - doc[dhcp] = one; - } else { - doc[dhcp] = zero; - } - // const char* disablePingCtrl = "disablePingCtrl"; - // if (serverWeb.arg(disablePingCtrl) == on) { - // doc[disablePingCtrl] = one; - // } else { - // doc[disablePingCtrl] = zero; - // } - configFile = LittleFS.open(configFileEther, FILE_WRITE); - serializeJson(doc, configFile); - configFile.close(); + const char *keepWeb = "keepWeb"; + if (serverWeb.arg(keepWeb) == on) + { + doc[keepWeb] = one; } - break; - case API_PAGE_WIFI:{ - configFile = LittleFS.open(configFileWifi, FILE_READ); - deserializeJson(doc, configFile); - configFile.close(); - doc["ssid"] = serverWeb.arg("WIFISSID"); - doc["pass"] = serverWeb.arg("WIFIpassword"); - const char* dhcpWiFi = "dhcpWiFi"; - if (serverWeb.arg(dhcpWiFi) == on) { - doc[dhcpWiFi] = 1; - } else { - doc[dhcpWiFi] = 0; - } - doc["ip"] = serverWeb.arg("ipAddress"); - doc["mask"] = serverWeb.arg("ipMask"); - doc["gw"] = serverWeb.arg("ipGW"); - configFile = LittleFS.open(configFileWifi, FILE_WRITE); - serializeJson(doc, configFile); - configFile.close(); - WiFi.persistent(false); - if (ConfigSettings.apStarted){ - WiFi.mode(WIFI_AP_STA); - }else{ - WiFi.mode(WIFI_STA); - } - WiFi.begin(serverWeb.arg("WIFISSID").c_str(), serverWeb.arg("WIFIpassword").c_str()); + else + { + doc[keepWeb] = zero; } - break; - case API_PAGE_ZHA_Z2M:{ - configFile = LittleFS.open(configFileSerial, FILE_READ); - deserializeJson(doc, configFile); - configFile.close(); - const char* baud = "baud"; - if (serverWeb.hasArg(baud)){ - doc[baud] = serverWeb.arg(baud); - }else{ - doc[baud] = 115200; - } - const char* port = "port"; - if (serverWeb.hasArg(baud)){ - doc[port] = serverWeb.arg(port); - }else{ - doc[port] = 6638; - } - configFile = LittleFS.open(configFileSerial, FILE_WRITE); - serializeJson(doc, configFile); - configFile.close(); + const char *disableLedPwr = "disableLedPwr"; + if (serverWeb.arg(disableLedPwr) == on) + { + doc[disableLedPwr] = one; } - break; - case API_PAGE_SECURITY:{ - configFile = LittleFS.open(configFileSecurity, FILE_READ); - deserializeJson(doc, configFile); - configFile.close(); - const char* disableWeb = "disableWeb"; - if (serverWeb.arg(disableWeb) == on) { - doc[disableWeb] = 1; - } else { - doc[disableWeb] = 0; - } - const char* webAuth = "webAuth"; - if (serverWeb.arg(webAuth) == on) { - doc[webAuth] = 1; - } else { - doc[webAuth] = 0; - } - const char* webUser = "webUser"; - if (serverWeb.arg(webUser) != "") { - doc[webUser] = serverWeb.arg(webUser); - } else { - doc[webUser] = "admin"; - } - const char* fwEnabled = "fwEnabled"; - if (serverWeb.arg(fwEnabled) == on) { - doc[fwEnabled] = 1; - } else { - doc[fwEnabled] = 0; - } - const char* fwIp = "fwIp"; - doc[fwIp] = serverWeb.arg(fwIp); - doc["webPass"] = serverWeb.arg("webPass"); + else + { + doc[disableLedPwr] = zero; + } + const char *disableLedUSB = "disableLedUSB"; + if (serverWeb.arg(disableLedUSB) == on) + { + doc[disableLedUSB] = one; + } + else + { + doc[disableLedUSB] = zero; + } + configFile = LittleFS.open(configFileGeneral, FILE_WRITE); + serializeJson(doc, configFile); + configFile.close(); + } + break; + case API_PAGE_ETHERNET: + { + configFile = LittleFS.open(configFileEther, FILE_READ); + deserializeJson(doc, configFile); + configFile.close(); + doc["ip"] = serverWeb.arg("ipAddress"); + doc["mask"] = serverWeb.arg("ipMask"); + doc["gw"] = serverWeb.arg("ipGW"); + const char *dhcp = "dhcp"; + if (serverWeb.arg(dhcp) == "on") + { + doc[dhcp] = one; + } + else + { + doc[dhcp] = zero; + } + // const char* disablePingCtrl = "disablePingCtrl"; + // if (serverWeb.arg(disablePingCtrl) == on) { + // doc[disablePingCtrl] = one; + // } else { + // doc[disablePingCtrl] = zero; + // } + configFile = LittleFS.open(configFileEther, FILE_WRITE); + serializeJson(doc, configFile); + configFile.close(); + } + case API_PAGE_MQTT: + { + configFile = LittleFS.open(configFileMqtt, FILE_READ); + deserializeJson(doc, configFile); + configFile.close(); + doc["server"] = serverWeb.arg("MqttServer"); + doc["port"] = serverWeb.arg("MqttPort"); + doc["user"] = serverWeb.arg("MqttUser"); + doc["pass"] = serverWeb.arg("MqttPass"); + doc["topic"] = serverWeb.arg("MqttTopic"); + doc["interval"] = serverWeb.arg("MqttInterval"); - configFile = LittleFS.open(configFileSecurity, FILE_WRITE); - serializeJson(doc, configFile); - configFile.close(); + const char *enable = "enable"; + if (serverWeb.arg("MqttEnable") == "on") + { + doc[enable] = one; } - break; - case API_PAGE_SYSTOOLS:{ - const char *refreshLogs = "refreshLogs"; - const char *hostname = "hostname"; - configFile = LittleFS.open(configFileGeneral, FILE_READ); - deserializeJson(doc, configFile); - configFile.close(); - if (serverWeb.hasArg(refreshLogs)){ - ConfigSettings.refreshLogs = serverWeb.arg(refreshLogs).toInt(); - doc[refreshLogs] = ConfigSettings.refreshLogs; - } - if (serverWeb.hasArg(hostname)){ - doc[hostname] = serverWeb.arg(hostname); - strlcpy(ConfigSettings.hostname, serverWeb.arg(hostname).c_str(), sizeof(ConfigSettings.hostname)); - } - configFile = LittleFS.open(configFileGeneral, FILE_WRITE); - serializeJson(doc, configFile); - configFile.close(); + else + { + doc[enable] = zero; } + + const char *discovery = "discovery"; + if (serverWeb.arg("MqttDiscovery") == "on") + { + doc[discovery] = one; + } + else + { + doc[discovery] = zero; + } + + // const char* disablePingCtrl = "disablePingCtrl"; + // if (serverWeb.arg(disablePingCtrl) == on) { + // doc[disablePingCtrl] = one; + // } else { + // doc[disablePingCtrl] = zero; + // } + configFile = LittleFS.open(configFileMqtt, FILE_WRITE); + serializeJson(doc, configFile); + configFile.close(); + loadConfigMqtt(); + } + break; + case API_PAGE_WIFI: + { + configFile = LittleFS.open(configFileWifi, FILE_READ); + deserializeJson(doc, configFile); + configFile.close(); + doc["ssid"] = serverWeb.arg("WIFISSID"); + doc["pass"] = serverWeb.arg("WIFIpassword"); + const char *dhcpWiFi = "dhcpWiFi"; + if (serverWeb.arg(dhcpWiFi) == on) + { + doc[dhcpWiFi] = 1; + } + else + { + doc[dhcpWiFi] = 0; + } + doc["ip"] = serverWeb.arg("ipAddress"); + doc["mask"] = serverWeb.arg("ipMask"); + doc["gw"] = serverWeb.arg("ipGW"); + configFile = LittleFS.open(configFileWifi, FILE_WRITE); + serializeJson(doc, configFile); + configFile.close(); + WiFi.persistent(false); + if (ConfigSettings.apStarted) + { + WiFi.mode(WIFI_AP_STA); + } + else + { + WiFi.mode(WIFI_STA); + } + WiFi.begin(serverWeb.arg("WIFISSID").c_str(), serverWeb.arg("WIFIpassword").c_str()); + } + break; + case API_PAGE_ZHA_Z2M: + { + configFile = LittleFS.open(configFileSerial, FILE_READ); + deserializeJson(doc, configFile); + configFile.close(); + const char *baud = "baud"; + if (serverWeb.hasArg(baud)) + { + doc[baud] = serverWeb.arg(baud); + } + else + { + doc[baud] = 115200; + } + const char *port = "port"; + if (serverWeb.hasArg(baud)) + { + doc[port] = serverWeb.arg(port); + } + else + { + doc[port] = 6638; + } + configFile = LittleFS.open(configFileSerial, FILE_WRITE); + serializeJson(doc, configFile); + configFile.close(); + } + break; + case API_PAGE_SECURITY: + { + configFile = LittleFS.open(configFileSecurity, FILE_READ); + deserializeJson(doc, configFile); + configFile.close(); + const char *disableWeb = "disableWeb"; + if (serverWeb.arg(disableWeb) == on) + { + doc[disableWeb] = 1; + } + else + { + doc[disableWeb] = 0; + } + const char *webAuth = "webAuth"; + if (serverWeb.arg(webAuth) == on) + { + doc[webAuth] = 1; + } + else + { + doc[webAuth] = 0; + } + const char *webUser = "webUser"; + if (serverWeb.arg(webUser) != "") + { + doc[webUser] = serverWeb.arg(webUser); + } + else + { + doc[webUser] = "admin"; + } + const char *fwEnabled = "fwEnabled"; + if (serverWeb.arg(fwEnabled) == on) + { + doc[fwEnabled] = 1; + } + else + { + doc[fwEnabled] = 0; + } + const char *fwIp = "fwIp"; + doc[fwIp] = serverWeb.arg(fwIp); + doc["webPass"] = serverWeb.arg("webPass"); + + configFile = LittleFS.open(configFileSecurity, FILE_WRITE); + serializeJson(doc, configFile); + configFile.close(); + } + break; + case API_PAGE_SYSTOOLS: + { + const char *refreshLogs = "refreshLogs"; + const char *hostname = "hostname"; + configFile = LittleFS.open(configFileGeneral, FILE_READ); + deserializeJson(doc, configFile); + configFile.close(); + if (serverWeb.hasArg(refreshLogs)) + { + ConfigSettings.refreshLogs = serverWeb.arg(refreshLogs).toInt(); + doc[refreshLogs] = ConfigSettings.refreshLogs; + } + if (serverWeb.hasArg(hostname)) + { + doc[hostname] = serverWeb.arg(hostname); + strlcpy(ConfigSettings.hostname, serverWeb.arg(hostname).c_str(), sizeof(ConfigSettings.hostname)); + } + configFile = LittleFS.open(configFileGeneral, FILE_WRITE); + serializeJson(doc, configFile); + configFile.close(); + } + break; + + default: break; - - default: - break; - } - serverWeb.send(HTTP_CODE_OK, contTypeText, "ok"); - }else{ + } + serverWeb.send(HTTP_CODE_OK, contTypeText, "ok"); + } + else + { serverWeb.send(500, contTypeText, "bad args"); } } -bool checkAuth() { - if(!ConfigSettings.webAuth) return true; +bool checkAuth() +{ + if (!ConfigSettings.webAuth) + return true; const char *www_realm = "Login Required"; - if (!serverWeb.authenticate(ConfigSettings.webUser, ConfigSettings.webPass)) { + if (!serverWeb.authenticate(ConfigSettings.webUser, ConfigSettings.webPass)) + { serverWeb.requestAuthentication(DIGEST_AUTH, www_realm, "Authentication failed"); return false; - } else { + } + else + { return true; } } -void handleGeneral() { - DynamicJsonDocument doc(1024); - String result; +void handleGeneral() +{ + DynamicJsonDocument doc(1024); + String result; - //doc["pageName"] = "General"; - // DEBUG_PRINTLN(ConfigSettings.usbMode); - switch (ConfigSettings.coordinator_mode) { - case COORDINATOR_MODE_USB: - doc["checkedUsbMode"] = checked; - break; - case COORDINATOR_MODE_WIFI: - doc["checkedWifiMode"] = checked; - break; - case COORDINATOR_MODE_LAN: - doc["checkedLanMode"] = checked; - break; + // doc["pageName"] = "General"; + // DEBUG_PRINTLN(ConfigSettings.usbMode); + switch (ConfigSettings.coordinator_mode) + { + case COORDINATOR_MODE_USB: + doc["checkedUsbMode"] = checked; + break; + case COORDINATOR_MODE_WIFI: + doc["checkedWifiMode"] = checked; + break; + case COORDINATOR_MODE_LAN: + doc["checkedLanMode"] = checked; + break; - default: - break; - } - // DEBUG_PRINTLN(ConfigSettings.disableLedYellow); - if (ConfigSettings.keepWeb) { - doc["keepWeb"] = checked; - } - if (ConfigSettings.disableLedYellow) { - doc["checkedDisableLedYellow"] = checked; - } - // DEBUG_PRINTLN(ConfigSettings.disableLedBlue); - if (ConfigSettings.disableLedBlue) { - doc["checkedDisableLedBlue"] = checked; - } - serializeJson(doc, result); - serverWeb.sendHeader(respHeaderName, result); + default: + break; + } + // DEBUG_PRINTLN(ConfigSettings.disableLedPwr); + if (ConfigSettings.keepWeb) + { + doc["keepWeb"] = checked; + } + if (ConfigSettings.disableLedPwr) + { + doc["checkedDisableLedPwr"] = checked; + } + // DEBUG_PRINTLN(ConfigSettings.disableLedUSB); + if (ConfigSettings.disableLedUSB) + { + doc["checkedDisableLedUSB"] = checked; + } + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); } -void handleSecurity() { - String result; - DynamicJsonDocument doc(1024); +void handleSecurity() +{ + String result; + DynamicJsonDocument doc(1024); - if (ConfigSettings.disableWeb) { - doc["disableWeb"] = checked; - } + if (ConfigSettings.disableWeb) + { + doc["disableWeb"] = checked; + } - if (ConfigSettings.webAuth) { - doc["webAuth"] = checked; - } - doc["webUser"] = (String)ConfigSettings.webUser; - doc["webPass"] = (String)ConfigSettings.webPass; - if (ConfigSettings.fwEnabled) { - doc["fwEnabled"] = checked; - } - doc["fwIp"] = ConfigSettings.fwIp.toString(); + if (ConfigSettings.webAuth) + { + doc["webAuth"] = checked; + } + doc["webUser"] = (String)ConfigSettings.webUser; + doc["webPass"] = (String)ConfigSettings.webPass; + if (ConfigSettings.fwEnabled) + { + doc["fwEnabled"] = checked; + } + doc["fwIp"] = ConfigSettings.fwIp.toString(); - serializeJson(doc, result); - serverWeb.sendHeader(respHeaderName, result); + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); } -void handleWifi() { - String result; - DynamicJsonDocument doc(1024); +void handleWifi() +{ + String result; + DynamicJsonDocument doc(1024); - //doc["pageName"] = "Config WiFi"; - doc["ssid"] = String(ConfigSettings.ssid); - doc["passWifi"] = String(ConfigSettings.password); - if (ConfigSettings.dhcpWiFi) { - doc["dchp"] = checked; - } - doc["ip"] = ConfigSettings.ipAddressWiFi; - doc["mask"] = ConfigSettings.ipMaskWiFi; - doc["gw"] = ConfigSettings.ipGWWiFi; + // doc["pageName"] = "Config WiFi"; + doc["ssid"] = String(ConfigSettings.ssid); + doc["passWifi"] = String(ConfigSettings.password); + if (ConfigSettings.dhcpWiFi) + { + doc["dchp"] = checked; + } + doc["ip"] = ConfigSettings.ipAddressWiFi; + doc["mask"] = ConfigSettings.ipMaskWiFi; + doc["gw"] = ConfigSettings.ipGWWiFi; - serializeJson(doc, result); - serverWeb.sendHeader(respHeaderName, result); + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); } -void handleSerial() { - String result; - DynamicJsonDocument doc(1024); - - if (ConfigSettings.serialSpeed == 9600) { - doc["9600"] = checked; - } else if (ConfigSettings.serialSpeed == 19200) { - doc["19200"] = checked; - } else if (ConfigSettings.serialSpeed == 38400) { - doc["8400"] = checked; - } else if (ConfigSettings.serialSpeed == 57600) { - doc["57600"] = checked; - } else if (ConfigSettings.serialSpeed == 115200) { - doc["115200"] = checked; - } else { - doc["115200"] = checked; - } - doc["socketPort"] = String(ConfigSettings.socketPort); +void handleSerial() +{ + String result; + DynamicJsonDocument doc(1024); - serializeJson(doc, result); - serverWeb.sendHeader(respHeaderName, result); + if (ConfigSettings.serialSpeed == 9600) + { + doc["9600"] = checked; + } + else if (ConfigSettings.serialSpeed == 19200) + { + doc["19200"] = checked; + } + else if (ConfigSettings.serialSpeed == 38400) + { + doc["8400"] = checked; + } + else if (ConfigSettings.serialSpeed == 57600) + { + doc["57600"] = checked; + } + else if (ConfigSettings.serialSpeed == 115200) + { + doc["115200"] = checked; + } + else + { + doc["115200"] = checked; + } + doc["socketPort"] = String(ConfigSettings.socketPort); + + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); } -void handleEther() { - String result; - DynamicJsonDocument doc(1024); +void handleEther() +{ + String result; + DynamicJsonDocument doc(1024); - //doc["pageName"] = "Config Ethernet"; + // doc["pageName"] = "Config Ethernet"; - if (ConfigSettings.dhcp) { - doc["modeEther"] = checked; - } - doc["ipEther"] = ConfigSettings.ipAddress; - doc["maskEther"] = ConfigSettings.ipMask; - doc["GWEther"] = ConfigSettings.ipGW; + if (ConfigSettings.dhcp) + { + doc["modeEther"] = checked; + } + doc["ipEther"] = ConfigSettings.ipAddress; + doc["maskEther"] = ConfigSettings.ipMask; + doc["GWEther"] = ConfigSettings.ipGW; - // if (ConfigSettings.disablePingCtrl) { - // doc["disablePingCtrl"] = checked; - // } + // if (ConfigSettings.disablePingCtrl) { + // doc["disablePingCtrl"] = checked; + // } - serializeJson(doc, result); - serverWeb.sendHeader(respHeaderName, result); + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); } -void handleRoot() { - String result; - DynamicJsonDocument doc(1024); - - char verArr[25]; - sprintf(verArr, "%s (%s)", VERSION, BUILD_TIMESTAMP); - doc["VERSION"] = verArr; - - String readableTime; - getReadableTime(readableTime, ConfigSettings.socketTime); - const char* connectedSocketStatus = "connectedSocketStatus"; - const char* connectedSocket = "connectedSocket"; - const char* notConnected = "Not connected"; - const char* yes = "Yes"; - const char* no = "No"; - const char* on = "On"; - const char* off = "Off"; - - if (ConfigSettings.connectedClients > 0) { - if (ConfigSettings.connectedClients > 1) { - doc[connectedSocketStatus] = "Yes, " + String(ConfigSettings.connectedClients) + "connection"; - doc[connectedSocket] = readableTime; - } else { - doc[connectedSocketStatus] = "Yes, " + String(ConfigSettings.connectedClients) + " connections"; - doc[connectedSocket] = readableTime; - } - } else { - doc[connectedSocketStatus] = no; - doc[connectedSocket] = notConnected; - } - const char* operationalMode = "operationalMode"; - switch (ConfigSettings.coordinator_mode) { - case COORDINATOR_MODE_USB: - doc[operationalMode] = "Zigbee-to-USB"; - break; - case COORDINATOR_MODE_WIFI: - doc[operationalMode] = "Zigbee-to-WiFi"; - break; - case COORDINATOR_MODE_LAN: - doc[operationalMode] = "Zigbee-to-Ethernet"; - break; +void handleMqtt() +{ + String result; + DynamicJsonDocument doc(1024); - default: - break; - } + if (ConfigSettings.mqttEnable) + { + doc["enableMqtt"] = checked; + } + doc["serverMqtt"] = ConfigSettings.mqttServer; + doc["portMqtt"] = ConfigSettings.mqttPort; + doc["userMqtt"] = ConfigSettings.mqttUser; + doc["passMqtt"] = ConfigSettings.mqttPass; + doc["topicMqtt"] = ConfigSettings.mqttTopic; + doc["intervalMqtt"] = ConfigSettings.mqttInterval; - // ETHERNET TAB - const char* ethDhcp = "ethDhcp"; - if (ConfigSettings.dhcp) { - doc[ethDhcp] = on; - } else { - doc[ethDhcp] = off; - } + if (ConfigSettings.mqttDiscovery) + { + doc["discoveryMqtt"] = checked; + } - const char* connectedEther = "connectedEther"; - const char* ethConnection = "ethConnection"; - const char* ethMac = "ethMac"; - const char* ethSpd = "ethSpd"; - const char* ethIp = "ethIp"; - const char* etchMask = "etchMask"; - const char* ethGate = "ethGate"; - if (ConfigSettings.connectedEther) { - doc[connectedEther] = yes; - doc[ethConnection] = "Connected"; - doc[ethMac] = ETH.macAddress(); - doc[ethSpd] = String(ETH.linkSpeed()) + String(" Mbps"); - doc[ethIp] = ETH.localIP().toString(); - doc[etchMask] = ETH.subnetMask().toString(); - doc[ethGate] = ETH.gatewayIP().toString(); - } else { - doc[connectedEther] = no; - doc[ethConnection] = notConnected; - doc[ethMac] = notConnected; - doc[ethSpd] = notConnected; - doc[ethIp] = notConnected; - doc[etchMask] = notConnected; - doc[ethGate] = notConnected; - } + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); +} - getReadableTime(readableTime, 0); - doc["uptime"] = readableTime; +DynamicJsonDocument getRootData() { + DynamicJsonDocument doc(1024); - float CPUtemp = getCPUtemp(); - doc["deviceTemp"] = String(CPUtemp); - doc["hwRev"] = deviceModel; - doc["espModel"] = String(ESP.getChipModel()); - doc["espCores"] = String(ESP.getChipCores()); - doc["espFreq"] = String(ESP.getCpuFreqMHz()); - doc["espHeapFree"] = String(ESP.getFreeHeap() / 1024); - doc["espHeapSize"] = String(ESP.getHeapSize() / 1024); + char verArr[25]; + const char *env = STRINGIFY(BUILD_ENV_NAME); - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); + sprintf(verArr, "%s (%s)", VERSION, env); - const char* espFlashType = "espFlashType"; - if (chip_info.features & CHIP_FEATURE_EMB_FLASH) { - doc[espFlashType] = "embedded"; - } else { - doc[espFlashType] = "external"; + doc["VERSION"] = String(verArr); + + String readableTime; + getReadableTime(readableTime, ConfigSettings.socketTime); + const char *connectedSocketStatus = "connectedSocketStatus"; + const char *connectedSocket = "connectedSocket"; + const char *notConnected = "Not connected"; + const char *yes = "Yes"; + const char *no = "No"; + const char *on = "On"; + const char *off = "Off"; + + if (ConfigSettings.connectedClients > 0) + { + if (ConfigSettings.connectedClients > 1) + { + doc[connectedSocketStatus] = "Yes, " + String(ConfigSettings.connectedClients) + "connection"; + doc[connectedSocket] = readableTime; } + else + { + doc[connectedSocketStatus] = "Yes, " + String(ConfigSettings.connectedClients) + " connections"; + doc[connectedSocket] = readableTime; + } + } + else + { + doc[connectedSocketStatus] = no; + doc[connectedSocket] = notConnected; + } + const char *operationalMode = "operationalMode"; + switch (ConfigSettings.coordinator_mode) + { + case COORDINATOR_MODE_USB: + doc[operationalMode] = "Zigbee-to-USB"; + break; + case COORDINATOR_MODE_WIFI: + doc[operationalMode] = "Zigbee-to-WiFi"; + break; + case COORDINATOR_MODE_LAN: + doc[operationalMode] = "Zigbee-to-Ethernet"; + break; - doc["espFlashSize"] = String(ESP.getFlashChipSize() / (1024 * 1024)); - //wifi - const char* wifiSsid = "wifiSsid"; - const char* wifiRssi = "wifiRssi"; - const char* wifiIp = "wifiIp"; - const char* wifiConnected = "wifiConnected"; - const char* wifiSubnet = "wifiSubnet"; - const char* wifiGate = "wifiGate"; - const char* wifiEnabled = "wifiEnabled"; - const char* wifiMode = "wifiMode"; - const char* wifiModeAPStatus = "wifiModeAPStatus"; - const char* wifiModeAP = "wifiModeAP"; - const char* wifiDhcp = "wifiDhcp"; - doc["wifiMac"] = String(WiFi.macAddress().c_str()); - if (ConfigSettings.coordinator_mode == COORDINATOR_MODE_WIFI) { - doc[wifiEnabled] = yes; - doc[wifiMode] = "Client"; - if (WiFi.status() == WL_CONNECTED) { // STA connected - String rssiWifi = String(WiFi.RSSI()) + String(" dBm"); - doc[wifiSsid] = WiFi.SSID(); - doc[wifiRssi] = rssiWifi; - doc[wifiConnected] = "Connected to " + WiFi.SSID(); - doc[wifiIp] = WiFi.localIP().toString(); - doc[wifiSubnet] = WiFi.subnetMask().toString(); - doc[wifiGate] = WiFi.gatewayIP().toString(); - } else { - const char *connecting = "Connecting..."; - doc["wifiSsid"] = ConfigSettings.ssid; - doc[wifiRssi] = connecting; - doc[wifiConnected] = connecting; - doc[wifiIp] = ConfigSettings.dhcpWiFi ? WiFi.localIP().toString() : connecting; - doc[wifiSubnet] = ConfigSettings.dhcpWiFi ? WiFi.subnetMask().toString() : connecting; - doc[wifiGate] = ConfigSettings.dhcpWiFi ? WiFi.gatewayIP().toString() : connecting; - } - if (ConfigSettings.dhcpWiFi) { - doc[wifiDhcp] = on; - } else { - doc[wifiDhcp] = off; - } - } else { - doc[wifiEnabled] = no; - doc[wifiConnected] = notConnected; - doc[wifiMode] = off; - doc[wifiSsid] = off; - doc[wifiIp] = off; - doc[wifiSubnet] = off; - doc[wifiGate] = off; - doc[wifiRssi] = off; + default: + break; + } + + // ETHERNET TAB + const char *ethDhcp = "ethDhcp"; + if (ConfigSettings.dhcp) + { + doc[ethDhcp] = on; + } + else + { + doc[ethDhcp] = off; + } + + const char *connectedEther = "connectedEther"; + const char *ethConnection = "ethConnection"; + const char *ethMac = "ethMac"; + const char *ethSpd = "ethSpd"; + const char *ethIp = "ethIp"; + const char *etchMask = "etchMask"; + const char *ethGate = "ethGate"; + if (ConfigSettings.connectedEther) + { + doc[connectedEther] = yes; + doc[ethConnection] = "Connected"; + doc[ethMac] = ETH.macAddress(); + doc[ethSpd] = String(ETH.linkSpeed()) + String(" Mbps"); + doc[ethIp] = ETH.localIP().toString(); + doc[etchMask] = ETH.subnetMask().toString(); + doc[ethGate] = ETH.gatewayIP().toString(); + } + else + { + doc[connectedEther] = no; + doc[ethConnection] = notConnected; + doc[ethMac] = notConnected; + doc[ethSpd] = notConnected; + doc[ethIp] = notConnected; + doc[etchMask] = notConnected; + doc[ethGate] = notConnected; + } + + getReadableTime(readableTime, 0); + doc["uptime"] = readableTime; + + float CPUtemp = getCPUtemp(); + doc["deviceTemp"] = String(CPUtemp); + doc["hwRev"] = deviceModel; + doc["espModel"] = String(ESP.getChipModel()); + doc["espCores"] = String(ESP.getChipCores()); + doc["espFreq"] = String(ESP.getCpuFreqMHz()); + doc["espHeapFree"] = String(ESP.getFreeHeap() / 1024); + doc["espHeapSize"] = String(ESP.getHeapSize() / 1024); + if (zbVer.zbRev > 0) + { + doc["zigbeeFwRev"] = String(zbVer.zbRev); + } + else + { + doc["zigbeeFwRev"] = "unknown"; + } + + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + + const char *espFlashType = "espFlashType"; + if (chip_info.features & CHIP_FEATURE_EMB_FLASH) + { + doc[espFlashType] = "embedded"; + } + else + { + doc[espFlashType] = "external"; + } + + doc["espFlashSize"] = String(ESP.getFlashChipSize() / (1024 * 1024)); + // wifi + const char *wifiSsid = "wifiSsid"; + const char *wifiRssi = "wifiRssi"; + const char *wifiIp = "wifiIp"; + const char *wifiConnected = "wifiConnected"; + const char *wifiSubnet = "wifiSubnet"; + const char *wifiGate = "wifiGate"; + const char *wifiEnabled = "wifiEnabled"; + const char *wifiMode = "wifiMode"; + const char *wifiModeAPStatus = "wifiModeAPStatus"; + const char *wifiModeAP = "wifiModeAP"; + const char *wifiDhcp = "wifiDhcp"; + doc["wifiMac"] = String(WiFi.macAddress().c_str()); + if (ConfigSettings.coordinator_mode == COORDINATOR_MODE_WIFI) + { + doc[wifiEnabled] = yes; + doc[wifiMode] = "Client"; + if (WiFi.status() == WL_CONNECTED) + { // STA connected + String rssiWifi = String(WiFi.RSSI()) + String(" dBm"); + doc[wifiSsid] = WiFi.SSID(); + doc[wifiRssi] = rssiWifi; + doc[wifiConnected] = "Connected to " + WiFi.SSID(); + doc[wifiIp] = WiFi.localIP().toString(); + doc[wifiSubnet] = WiFi.subnetMask().toString(); + doc[wifiGate] = WiFi.gatewayIP().toString(); + } + else + { + const char *connecting = "Connecting..."; + doc["wifiSsid"] = ConfigSettings.ssid; + doc[wifiRssi] = connecting; + doc[wifiConnected] = connecting; + doc[wifiIp] = ConfigSettings.dhcpWiFi ? WiFi.localIP().toString() : connecting; + doc[wifiSubnet] = ConfigSettings.dhcpWiFi ? WiFi.subnetMask().toString() : connecting; + doc[wifiGate] = ConfigSettings.dhcpWiFi ? WiFi.gatewayIP().toString() : connecting; + } + if (ConfigSettings.dhcpWiFi) + { + doc[wifiDhcp] = on; + } + else + { doc[wifiDhcp] = off; } - if (ConfigSettings.apStarted) { // AP active - String AP_NameString; - char apSsid[18]; - getDeviceID(apSsid); - char wifiSsidBuf[35]; - sprintf(wifiSsidBuf, "%s (no password)", apSsid); - doc[wifiSsid] = wifiSsidBuf; - doc[wifiIp] = "192.168.1.1 (UZG-01 web interface)"; - doc[wifiSubnet] = "255.255.255.0 (Access point)"; - doc[wifiGate] = "192.168.1.1 (this device)"; - doc[wifiDhcp] = "On (Access point)"; - doc[wifiModeAPStatus] = "AP started"; - doc[wifiMode] = "AP"; - doc[wifiModeAP] = yes; - doc[wifiRssi] = "N/A"; + } + else + { + doc[wifiEnabled] = no; + doc[wifiConnected] = notConnected; + doc[wifiMode] = off; + doc[wifiSsid] = off; + doc[wifiIp] = off; + doc[wifiSubnet] = off; + doc[wifiGate] = off; + doc[wifiRssi] = off; + doc[wifiDhcp] = off; + } + if (ConfigSettings.apStarted) + { // AP active + String AP_NameString; + char apSsid[18]; + getDeviceID(apSsid); + char wifiSsidBuf[35]; + sprintf(wifiSsidBuf, "%s (no password)", apSsid); + doc[wifiSsid] = wifiSsidBuf; + doc[wifiIp] = "192.168.1.1 (UZG-01 web interface)"; + doc[wifiSubnet] = "255.255.255.0 (Access point)"; + doc[wifiGate] = "192.168.1.1 (this device)"; + doc[wifiDhcp] = "On (Access point)"; + doc[wifiModeAPStatus] = "AP started"; + doc[wifiMode] = "AP"; + doc[wifiModeAP] = yes; + doc[wifiRssi] = "N/A"; + } + else + { + doc[wifiModeAP] = no; + doc[wifiModeAPStatus] = "Not started"; + // doc[wifiMode] = "Client"; + } + return doc; +} + + +void handleRoot() +{ + String result; + DynamicJsonDocument doc(1024); + doc = getRootData(); + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); +} + +void handleStatus() { + String result; + DynamicJsonDocument doc(1024); + + // Authentication is needed for status page as well (if enabled) + if(ConfigSettings.webAuth) { + if(!checkAuth()) { + serverWeb.sendHeader("Authentication", "fail"); + serverWeb.send(HTTP_CODE_UNAUTHORIZED, contTypeText, F("wrong login or password")); + return; } else { - doc[wifiModeAP] = no; - doc[wifiModeAPStatus] = "Not started"; - //doc[wifiMode] = "Client"; + serverWeb.sendHeader("Authentication", "ok"); } - serializeJson(doc, result); - serverWeb.sendHeader(respHeaderName, result); + } + + doc = getRootData(); + serializeJsonPretty(doc, result); + serverWeb.send(HTTP_CODE_OK, contTypeJson, result); } -void handleSysTools() { - String result; - DynamicJsonDocument doc(512); - //doc["pageName"] = "System and Tools"; - doc["hostname"] = ConfigSettings.hostname; - doc["refreshLogs"] = ConfigSettings.refreshLogs; - serializeJson(doc, result); - serverWeb.sendHeader(respHeaderName, result); +void handleSysTools() +{ + String result; + DynamicJsonDocument doc(512); + // doc["pageName"] = "System and Tools"; + doc["hostname"] = ConfigSettings.hostname; + doc["refreshLogs"] = ConfigSettings.refreshLogs; + serializeJson(doc, result); + serverWeb.sendHeader(respHeaderName, result); } -void handleSavefile() { - if (checkAuth()) { - if (serverWeb.method() != HTTP_POST) { +void handleSavefile() +{ + if (checkAuth()) + { + if (serverWeb.method() != HTTP_POST) + { serverWeb.send(405, contTypeText, F("Method Not Allowed")); - } else { + } + else + { String filename = "/config/" + serverWeb.arg(0); String content = serverWeb.arg(1); File file = LittleFS.open(filename, "w"); - if (!file) { + if (!file) + { DEBUG_PRINT(F("Failed to open file for reading\r\n")); return; } int bytesWritten = file.print(content); - if (bytesWritten > 0) { + if (bytesWritten > 0) + { DEBUG_PRINTLN(F("File was written")); DEBUG_PRINTLN(bytesWritten); - } else { + } + else + { DEBUG_PRINTLN(F("File write failed")); } @@ -949,105 +1516,353 @@ void handleSavefile() { } } -void handleZigbeeBSL() {//todo move to api - if (checkAuth()) { +void handleZigbeeBSL() +{ // todo move to api + if (checkAuth()) + { zigbeeEnableBSL(); serverWeb.send(HTTP_CODE_OK, contTypeText, ""); } } -void handleZigbeeRestart(){ - if (checkAuth()) { +void handleZigbeeRestart() +{ + if (checkAuth()) + { zigbeeRestart(); serverWeb.send(HTTP_CODE_OK, contTypeText, ""); } } -void printLogTime() { +void printLogTime() +{ String tmpTime; unsigned long timeLog = millis(); tmpTime = String(timeLog, DEC); logPush('['); - for (int j = 0; j < tmpTime.length(); j++) { + for (int j = 0; j < tmpTime.length(); j++) + { logPush(tmpTime[j]); } logPush(']'); } -void printLogMsg(String msg) { +void printLogMsg(String msg) +{ printLogTime(); logPush(' '); logPush('|'); logPush(' '); - for (int j = 0; j < msg.length(); j++) { + for (int j = 0; j < msg.length(); j++) + { logPush(msg[j]); } logPush('\n'); } -// int totalLength; // total size of firmware -// int currentLength = 0; // current size of written firmware - -// void progressFunc(unsigned int progress, unsigned int total) { -// Serial.printf("Progress: %u of %u\r", progress, total); -// }; - -// void checkUpdateFirmware() {//todo del -// clientWeb.begin(UPD_FILE); -// clientWeb.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); -// // Get file, just to check if each reachable -// int resp = clientWeb.GET(); -// Serial.print("Response: "); -// Serial.println(resp); -// // If file is reachable, start downloading -// if (resp == HTTP_CODE_OK) { -// // get length of document (is -1 when Server sends no Content-Length header) -// totalLength = clientWeb.getSize(); -// // transfer to local variable -// int len = totalLength; -// // this is required to start firmware update process -// Update.begin(UPDATE_SIZE_UNKNOWN); -// Update.onProgress(progressFunc); -// DEBUG_PRINT("FW Size: "); - -// DEBUG_PRINTLN(totalLength); -// // create buffer for read -// uint8_t buff[128] = {0}; -// // get tcp stream -// WiFiClient *stream = clientWeb.getStreamPtr(); -// // read all data from server -// DEBUG_PRINTLN("Updating firmware..."); -// while (clientWeb.connected() && (len > 0 || len == -1)) { -// // get available data size -// size_t size = stream->available(); -// if (size) { -// // read up to 128 byte -// int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); -// // pass to function -// runUpdateFirmware(buff, c); -// if (len > 0) { -// len -= c; -// } -// } -// // DEBUG_PRINT("Bytes left to flash "); -// // DEBUG_PRINTLN(len); -// // delay(1); -// } -// } else { -// Serial.println("Cannot download firmware file. Only HTTP response 200: OK is supported. Double check firmware location #defined in UPD_FILE."); -// } -// clientWeb.end(); -// } - -// void runUpdateFirmware(uint8_t *data, size_t len) {//todo del -// Update.write(data, len); -// currentLength += len; -// // Print dots while waiting for update to finish -// Serial.print('.'); -// // if current length of written firmware is not equal to total firmware size, repeat -// if (currentLength != totalLength) return; -// Update.end(true); -// Serial.printf("\nUpdate Success, Total Size: %u\nRebooting...\n", currentLength); -// // Restart ESP32 to see changes -// ESP.restart(); -// } \ No newline at end of file +void progressFunc(unsigned int progress, unsigned int total) +{ + + const char *tagESP_FW_progress = "ESP_FW_prgs"; + const uint8_t eventLen = 11; + + float percent = ((float)progress / total) * 100.0; + + sendEvent(tagESP_FW_progress, eventLen, String(percent)); + + if (int(percent) % 5 == 0) + { + DEBUG_PRINTLN("Update ESP32 progress: " + String(progress) + " of " + String(total) + " | " + String(percent) + "%"); + } +}; + +int totalLength; // total size of firmware +int currentLength = 0; // current size of written firmware + +void getEspUpdate(String esp_fw_url) +{ + DEBUG_PRINTLN("getEspUpdate: " + esp_fw_url); + setClock(); + HTTPClient clientWeb; + WiFiClientSecure client; + client.setInsecure(); // the magic line, use with caution + clientWeb.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + clientWeb.begin(client, esp_fw_url); + clientWeb.addHeader("Content-Type", "application/octet-stream"); + + // Get file, just to check if each reachable + int resp = clientWeb.GET(); + DEBUG_PRINTLN("Response: " + resp); + // If file is reachable, start downloading + if (resp == HTTP_CODE_OK) + { + // get length of document (is -1 when Server sends no Content-Length header) + totalLength = clientWeb.getSize(); + // transfer to local variable + int len = totalLength; + // this is required to start firmware update process + Update.begin(totalLength); + Update.onProgress(progressFunc); + DEBUG_PRINT("FW Size: "); + + DEBUG_PRINTLN(totalLength); + // create buffer for read + uint8_t buff[128] = {0}; + // get tcp stream + WiFiClient *stream = clientWeb.getStreamPtr(); + // read all data from server + DEBUG_PRINTLN("Updating firmware..."); + while (clientWeb.connected() && (len > 0 || len == -1)) + { + // get available data size + size_t size = stream->available(); + if (size) + { + // read up to 128 byte + int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); + // pass to function tagZB_FW_progress + runEspUpdateFirmware(buff, c); + + if (len > 0) + { + len -= c; + } + } + // DEBUG_PRINT("Bytes left to flash "); + // DEBUG_PRINTLN(len); + // delay(1); + } + } + else + { + DEBUG_PRINTLN("Cannot download firmware file."); + } + clientWeb.end(); +} + +void runEspUpdateFirmware(uint8_t *data, size_t len) +{ + Update.write(data, len); + currentLength += len; + + // if current length of written firmware is not equal to total firmware size, repeat + if (currentLength != totalLength) + return; + // only if currentLength == totalLength + Update.end(true); + DEBUG_PRINTLN("Update success. Rebooting..."); + // Restart ESP32 to see changes + ESP.restart(); +} + +/*void runESPUpdateFirmware(uint8_t *data, size_t len) +{ // todo del + // DEBUG_PRINT("-"); + // DEBUG_PRINT(len); + // size_t cur = + // DEBUG_PRINT("!"); + // DEBUG_PRINTLN(cur); + if (Update.write(data, len) != len) + { + DEBUG_PRINT(F("Write error: ")); + Update.printError(Serial); + } +} + +void downloadURLAndUpdateESP(String esp_fw_url) +{ + int totalLength; // total size of firmware + //int currentLength = 0; // current size of written firmware + setClock(); + HTTPClient https; + WiFiClientSecure client; + client.setInsecure(); // the magic line, use with caution + https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + https.begin(client, esp_fw_url); + https.addHeader("Content-Type", "application/octet-stream"); + + // Get file, just to check if each reachable + int resp = https.GET(); + + DEBUG_PRINTLN(F("Response: ")); + DEBUG_PRINTLN(resp); + // If file is reachable, start downloading + if (resp == HTTP_CODE_OK) + { + // get length of document (is -1 when Server sends no Content-Length header) + totalLength = https.getSize(); + // transfer to local variable + int len = totalLength; + // this is required to start firmware update process + // Update.begin(UPDATE_SIZE_UNKNOWN); + Update.begin(totalLength); + Update.onProgress(progressFunc); + + DEBUG_PRINT("FW Size: "); + DEBUG_PRINTLN(totalLength); + + DEBUG_PRINTLN("Updating firmware..."); + + uint8_t buff[128]; + uint32_t downloaded = 0; + while (client.readBytes(buff, sizeof(buff))) + { + size_t size = client.available(); + + if (size) + { + runESPUpdateFirmware(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); + } + } + if (Update.end(true)) + { // true to set the size to the current progress + DEBUG_PRINTLN("Update Success"); + // Restart ESP32 to see changes + DEBUG_PRINTLN("Rebooting..."); + ESP.restart(); + // serverWeb.send(HTTP_CODE_OK, contTypeText, (Update.hasError()) ? "FAIL" : "OK"); + } + else + { + DEBUG_PRINT(F("Update error: ")); + Update.printError(Serial); + } + + } + else + { + DEBUG_PRINTLN(F("Cannot download firmware file. Only HTTP response 200: OK is supported. Double check firmware location #defined in UPD_FILE.")); + } + https.end(); +}*/ + +/* // debug - clean - delete +void getEspUpdate() +{ // todo del + + WiFiClientSecure *client0 = new WiFiClientSecure; + if (client0) + { + // set secure client without certificate + client0->setInsecure(); + // create an HTTPClient instance + setClock(); + HTTPClient https0; + + // Initializing an HTTPS communication using the secure client + DEBUG_PRINTLN(F("[HTTPS] begin...")); + if (https0.begin(*client0, "https://www.howsmyssl.com/a/check")) + { // HTTPS + DEBUG_PRINTLN(F("[HTTPS] GET...")); + // start connection and send HTTP header + int httpCode0 = https0.GET(); + // httpCode will be negative on error + if (httpCode0 > 0) + { + // HTTP header has been send and Server response header has been handled + DEBUG_PRINTLN("[HTTPS] GET... code: " + httpCode0); + // file found at server + if (httpCode0 == HTTP_CODE_OK || httpCode0 == HTTP_CODE_MOVED_PERMANENTLY) + { + // print server response payload + String payload = https0.getString(); + DEBUG_PRINTLN(payload); + } + } + else + { + DEBUG_PRINTLN("[HTTPS] GET... failed, error: " + String(https0.errorToString(httpCode0).c_str())); + } + https0.end(); + } + } + else + { + DEBUG_PRINTLN(F("[HTTPS] Unable to connect")); + } + + DEBUG_PRINTLN(F("Waiting 2s before the next round...")); + delay(2000); + setClock(); + HTTPClient https; + WiFiClientSecure client; + client.setInsecure(); + https.begin(client, "https://raw.githubusercontent.com/Tarik2142/devHost/main/router_20221102.bin"); // https://raw.githubusercontent.com/Tarik2142/devHost/main/coordinator_20211217.bin + https.addHeader("Content-Type", "application/octet-stream"); + const int16_t httpsCode = https.GET(); + // sendEvent(tag, eventLen, String("REQ result: ") + httpsCode); + DEBUG_PRINTLN(String("REQ result: " + String(httpsCode))); + if (httpsCode == HTTP_CODE_OK) + { + const uint32_t fwSize = https.getSize(); + // sendEvent(tagZB_FW_info, eventLen, "[start]"); + DEBUG_PRINTLN(F("[start]")); + // sendEvent(tagZB_FW_info, eventLen, "Downloading firmware..."); + DEBUG_PRINTLN(F("Downloading firmware...")); + const char *tempFile3 = "/config/router.bin"; + LittleFS.remove(tempFile3); + File fwFile = LittleFS.open(tempFile3, "w", 1); + uint8_t buff[4]; + uint32_t downloaded = 0; + while (client.readBytes(buff, sizeof(buff))) + { + downloaded += fwFile.write(buff, sizeof(buff)); + if (!(downloaded % 8192)) + { + const uint8_t d = ((float)downloaded / fwSize) * 100; + // sendEvent(tagZB_FW_progress, eventLen, String(d)); + } + } + fwFile.close(); + // in development + } + else + { + // serverWeb.send(HTTP_CODE_BAD_REQUEST, contTypeText, String(httpsCode)); + DEBUG_PRINTLN(F(httpsCode)); + } + + /* DEBUG_PRINTLN(F()); + DEBUG_PRINTLN(F("Waiting 2min before the next round...")); + delay(120000); + + + + //WiFiClientSecure clientSSL; + + //const char * git_url = "https://github.com/"; + + //clientSSL.setInsecure(); //the magic line, use with caution + //clientSSL.connect(git_url, 443); + + //clientWeb.begin(UPD_FILE); + //clientWeb.begin(clientSSL, UPD_FILE); + + //clientWeb.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + //client.setCACert(test_root_ca); + + //clientWeb.begin(url); + + +} */ + +void setClock() +{ + configTime(0, 0, "pool.ntp.org"); + + Serial.print(F("Waiting for NTP time sync: ")); + time_t nowSecs = time(nullptr); + while ((nowSecs < 8 * 3600 * 2)) + { + delay(500); + Serial.print(F(".")); + yield(); + nowSecs = time(nullptr); + } + + Serial.println(); + struct tm timeinfo; + gmtime_r(&nowSecs, &timeinfo); + Serial.print(F("Current time: ")); + Serial.print(asctime(&timeinfo)); +} diff --git a/src/web.h b/src/web.h index bb88080..210260f 100644 --- a/src/web.h +++ b/src/web.h @@ -1,4 +1,5 @@ #include "Arduino.h" +void handleEvents(); void initWebServer(); void webServerHandleClient(); void handleGeneral(); @@ -6,17 +7,28 @@ void handleSecurity(); void handleRoot(); void handleWifi(); void handleEther(); +void handleMqtt(); void handleZigbeeBSL(); void handleZigbeeRestart(); void handleSerial(); void handleSavefile(); void handleApi(); +void handleStatus(); void sendGzip(const char* contentType, const uint8_t content[], uint16_t contentLen); void handleSysTools(); void printLogTime(); void printLogMsg(String msg); void handleSaveParams(); bool checkAuth(); -void handleZigbeeBSL(); + + +void progressFunc(unsigned int progress, unsigned int total); +//void getEspUpdate(); +//void runESPUpdateFirmware(uint8_t *data, size_t len); +//void downloadURLAndUpdateESP(String esp_fw_url); +void getEspUpdate(String esp_fw_url); +void runEspUpdateFirmware(uint8_t *data, size_t len); + +void setClock(); #define UPD_FILE "https://github.com/mercenaruss/uzg-firmware/releases/latest/download/UZG-01.bin" \ No newline at end of file diff --git a/src/websrc/css/bootstrap.min.css b/src/websrc/css/bootstrap.min.css index ae9b575..cc6d1ba 100644 --- a/src/websrc/css/bootstrap.min.css +++ b/src/websrc/css/bootstrap.min.css @@ -8278,6 +8278,11 @@ textarea.form-control-lg { background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } +.bg-dark-grey { + --bs-bg-opacity: 1; + background-color: #404040; +} + .bg-primary { --bs-bg-opacity: 1; background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; diff --git a/src/websrc/css/required.css b/src/websrc/css/required.css index d1f8f9d..3ad5e56 100644 --- a/src/websrc/css/required.css +++ b/src/websrc/css/required.css @@ -137,7 +137,21 @@ label.swFx{ padding: 0; border: 1px solid #ddd; line-height: 44px; - text-align: left; + text-align: center; + display: block; + cursor: pointer; + width: 100%; + height: 44px; + border-radius: 4px; + margin: 10px auto; + font-size: 15px +} + +#file-input_zb { + padding: 0; + border: 1px solid #ddd; + line-height: 44px; + text-align: center; display: block; cursor: pointer; width: 100%; @@ -153,12 +167,24 @@ label.swFx{ border-radius: 10px } +#bar_zb, +#prgbar_zb { + background-color: #f1f1f1; + border-radius: 10px +} + #bar { background-color: #17a2b8; width: 0%; height: 10px } +#bar_zb { + background-color: #17a2b8; + width: 0%; + height: 10px +} + form#upload_form { background: #fff; max-width: 408px; @@ -166,6 +192,13 @@ form#upload_form { text-align: center } +form#upload_form_zb { + background: #fff; + max-width: 408px; + border-radius: 5px; + text-align: center +} + h2 { text-align: center } @@ -8460,6 +8493,11 @@ textarea.form-control-lg { background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } +.bg-dark-grey { + --bs-bg-opacity: 1; + background-color: #404040; +} + .bg-primary { --bs-bg-opacity: 1; background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; diff --git a/src/websrc/css/style.css b/src/websrc/css/style.css index e522311..7c2ebd7 100644 --- a/src/websrc/css/style.css +++ b/src/websrc/css/style.css @@ -137,7 +137,21 @@ label.swFx{ padding: 0; border: 1px solid #ddd; line-height: 44px; - text-align: left; + text-align: center; + display: block; + cursor: pointer; + width: 100%; + height: 44px; + border-radius: 4px; + margin: 10px auto; + font-size: 15px +} + +#file-input_zb { + padding: 0; + border: 1px solid #ddd; + line-height: 44px; + text-align: center; display: block; cursor: pointer; width: 100%; @@ -153,12 +167,24 @@ label.swFx{ border-radius: 10px } +#bar_zb, +#prgbar_zb { + background-color: #f1f1f1; + border-radius: 10px +} + #bar { background-color: #17a2b8; width: 0%; height: 10px } +#bar_zb { + background-color: #17a2b8; + width: 0%; + height: 10px +} + form#upload_form { background: #fff; max-width: 408px; @@ -166,6 +192,13 @@ form#upload_form { text-align: center } +form#upload_form_zb { + background: #fff; + max-width: 408px; + border-radius: 5px; + text-align: center +} + h2 { text-align: center } diff --git a/src/websrc/html/PAGE_GENERAL.html b/src/websrc/html/PAGE_GENERAL.html index 2763b10..097776e 100644 --- a/src/websrc/html/PAGE_GENERAL.html +++ b/src/websrc/html/PAGE_GENERAL.html @@ -53,11 +53,13 @@ session (not saved on reboot):
- + +
@@ -69,17 +71,16 @@ switches below:
- - + +
- - + +
diff --git a/src/websrc/html/PAGE_LOADER.html b/src/websrc/html/PAGE_LOADER.html index bf2de01..ad6ced4 100644 --- a/src/websrc/html/PAGE_LOADER.html +++ b/src/websrc/html/PAGE_LOADER.html @@ -66,7 +66,7 @@

+