diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index 103a4de..fe07e9f 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] + board: [esp32, esp32-nopsram, esp32-s3, esp32-s3-nopsram, esp32-s3-oct, esp32-c3, esp32-c5, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] build: [release] env: BOARD: ${{ matrix.board }} @@ -36,7 +36,7 @@ jobs: key: ${{ runner.os }}-pio-${{ matrix.board }} - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' - name: Install dependencies run: make deps - name: Add version info diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98196f1..26d708e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] + board: [esp32, esp32-nopsram, esp32-s3, esp32-s3-nopsram, esp32-s3-oct, esp32-c3, esp32-c5, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] build: [release] env: BOARD: ${{ matrix.board }} @@ -42,7 +42,7 @@ jobs: key: ${{ runner.os }}-pio-${{ matrix.board }} - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' - name: Install dependencies run: make deps - name: Add version info diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 9713a6b..f5a1a33 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] + board: [esp32, esp32-nopsram, esp32-s3, esp32-s3-nopsram, esp32-s3-oct, esp32-c3, esp32-c5, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] build: [release] env: BOARD: ${{ matrix.board }} @@ -58,7 +58,7 @@ jobs: key: ${{ runner.os }}-pio-${{ matrix.board }} - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' - name: Install dependencies run: make deps - name: Add version info diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0113275..134fd22 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - board: [esp32, esp32-s3, esp32-s3-oct, esp32-c3, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] + board: [esp32, esp32-nopsram, esp32-s3, esp32-s3-nopsram, esp32-s3-oct, esp32-c3, esp32-c5, esp32-c6, esp32-h2, esp32-solo1, esp32-gl-s10, esp32-p4] build: [release] env: BOARD: ${{ matrix.board }} @@ -36,7 +36,7 @@ jobs: key: ${{ runner.os }}-pio-${{ matrix.board }} - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' - name: Install dependencies run: make deps - name: Add version info @@ -154,6 +154,7 @@ jobs: run: | mkdir -p ota/beta/ mkdir -p ota/master/ + mkdir -p ota/old/ mkdir -p resources/ mkdir -p src/ cp -vf release/*/nuki_hub_*.bin ota/ @@ -168,11 +169,13 @@ jobs: rm -rf .github .gitignore .gitmodules touch ota/beta/empty touch ota/master/empty + echo release/*/nuki_hub_*.bin | tr ' ' '\n' | xargs -n1 -I{} bash -c 'cp {} ota/old/$VERSION.$(basename "{}")' + python3 resources/old_manifest.py $Version - name: Commit binaries to binary uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: "Update binaries" - file_pattern: 'ota/* ota/master/* ota/beta/*' + file_pattern: 'ota/* ota/master/* ota/beta/* ota/old/*' branch: binary skip_dirty_check: true skip_fetch: true diff --git a/Docker/README.md b/Docker/README.md index 1809df4..fb6c479 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -2,7 +2,7 @@ You can build this project using Docker. Just run the following commands in the console: -## Build with PlatformIO (will build for the ESP32, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2, ESP32-P4 and ESP32-Solo1) +## Build with PlatformIO (will build for the ESP32, ESP32-S3, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-H2, ESP32-P4 and ESP32-Solo1) ```console git clone https://github.com/technyon/nuki_hub --recursive cd nuki_hub/Docker diff --git a/Makefile b/Makefile index c9a48b5..bc4cdcc 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ help: .PHONY: clean clean: @echo "Cleaning build artifacts..." - @-rm -rf release debug .pio/build updater/.pio/build + @-rm -rf release debug .pio/build updater/.pio/build dependencies.lock updater/dependencies.lock managed_components updater/managed_components # Install dependencies .PHONY: deps diff --git a/README.md b/README.md index 091d591..1586f49 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Feel free to join us on Discord: https://discord.gg/9nPq85bP4p ## Supported devices Supported ESP32 devices: -- Nuki Hub is compiled against all ESP32 models with Wi-Fi and Bluetooh Low Energy (BLE) which are supported by ESP-IDF 5.4.1 and Arduino Core 3.2.0. -- Tested stable builds are provided for the ESP32, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-P4 (with the ESP32-C6-MINI-1 module for BLE and WiFi) and ESP32-H2. +- Nuki Hub is compiled against all ESP32 models with Wi-Fi and Bluetooh Low Energy (BLE) which are supported by ESP-IDF 5.5.0 and Arduino Core 3.3.0. +- Tested stable builds are provided for the ESP32, ESP32-S3, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-P4 (with the ESP32-C6-MINI-1 module for BLE and WiFi) and ESP32-H2. - Untested builds are provided for the ESP32-Solo1 (as the developers don't own one). Not supported ESP32 devices: @@ -58,8 +58,8 @@ See the "[Connecting via Ethernet](#connecting-via-ethernet-optional)" section f ## Recommended ESP32 devices -We don't recommend using single-core ESP32 devices (ESP32-C3, ESP32-C6, ESP32-H2, ESP32-Solo1).
-Although Nuki Hub supports single-core devices, Nuki Hub uses both CPU cores (if available) to process tasks (e.g. HTTP server/MQTT client/BLE scanner/BLE client) and thus runs much better on dual-core devices.
+We don't recommend using the original ESP32 or ESP32-Solo1 devices because these devices experience unexpected crashes related to the (closed-source) BLE controller.
+In newer models (e.g. ESP32-S3, ESP32-P4, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-H2) these unexpected crashed are seen a lot less. We also don't recommend using the original ESP32 or ESP32-Solo1 devices because these devices experience unexpected crashes related to the (closed-source) BLE controller.
In newer models (e.g. ESP32-S3, ESP32-P4, ESP32-C3, ESP32-C6, ESP32-H2) these unexpected crashes are seen less. @@ -71,16 +71,20 @@ It supports (with the C6 co-processor) anything the ESP32 range has to offer wit The only function missing (when not using a C5 as co-processor) is 5Ghz WiFi support. The ESP32-S3 is a dual-core CPU with many GPIO's, ability to enlarge RAM using PSRAM, ability to connect Ethernet modules over SPI and optionally power the device with a PoE splitter.
-The only functions missing from the ESP32-S3 as compared to other ESP devices is the ability to use some Ethernet modules only supported by the original ESP32 (and ESP32-P4) and the ability to connect over WIFI6 (C6 or ESP32-P4 with C6 module) +The only functions missing from the ESP32-S3 as compared to other ESP devices is the ability to use some Ethernet modules only supported by the original ESP32 (and ESP32-P4) and the ability to connect over WIFI6 (C5, C6 or ESP32-P4 with C6 module) + +The ESP32-C5 with PSRAM is a good option providing higher clockspeeds than the C6 and adding PSRAM and WIFI 6 on the 5 Ghz band support. +Nuki Hub uses both CPU cores (if available) to process tasks (e.g. HTTP server/MQTT client/BLE scanner/BLE client) and thus runs better on dual-core devices.
Other considerations: - If Ethernet/PoE is required: ESP32-P4 with ESP32-C6-MINI-1 module or ESP32-S3 with PSRAM in combination with a SPI Ethernet module ([W5500](https://www.aliexpress.com/w/wholesale-w5500.html)) and [PoE to Ethernet and USB type B/C splitter](https://aliexpress.com/w/wholesale-poe-splitter-usb-c.html) or the LilyGO-T-ETH ELite, LilyGO-T-ETH-Lite-ESP32S3 or M5stack Atom S3R with the M5stack AtomPoe W5500 module -- If WIFI6 is absolutely required: ESP32-P4 with ESP32-C6-MINI-1 module or ESP32-C6 +- If WIFI6 is required: ESP32-P4 with ESP32-C6-MINI-1 module, ESP32-C5 or ESP32-C6 Devices ranked best-to-worst: - ESP32-P4 with ESP32-C6-MINI-1 module - ......
- ESP32-S3 with PSRAM +- ESP32-C5 with PSRAM - ......
(Devices below will not support some Nuki Hub functions, be slower and/or are more likely to experience unexpected crashes) - ESP32-S3 without PSRAM @@ -90,6 +94,7 @@ Devices ranked best-to-worst: - ESP32 without PSRAM - ......
(Devices below will not support more Nuki Hub functions, be slower and/or are more likely to experience unexpected crashes) +- ESP32-C5 - ESP32-C6 - ESP32-solo1 - ESP32-C3 @@ -195,17 +200,19 @@ ESP32 devices have a limited amount of free RAM available.

On version >=9.10 of Nuki Hub with only a Nuki Lock connected the expected free amount of RAM/Heap available is around: -- ESP32: 70.000 bytes -- ESP32 with PSRAM: 110.000 bytes + PSRAM -- ESP32-C3: 90.000 bytes +- ESP32: 105.000 bytes +- ESP32 with PSRAM: 120.000 bytes + PSRAM +- ESP32-C3: 70.000 bytes +- ESP32-C5 with PSRAM: 130.000 bytes + PSRAM - ESP32-C6: 200.000 bytes -- ESP32-S3 130.000 bytes -- ESP32-S3 with PSRAM: 180.000 bytes + PSRAM +- ESP32-P4: 450.000 bytes +- ESP32-S3 135.000 bytes +- ESP32-S3 with PSRAM: 185.000 bytes + PSRAM This free amount of RAM can be reduced (temporarily) by certain actions (such as changing Nuki device config) or continuously when enabling the following: - Connecting both a Nuki opener and a Nuki lock to Nuki Hub - Enlarging stack sizes of the Nuki and Network task to accommodate large amounts of keypad codes, authorization entries or timecontrol entries -- MQTT SSL (Costs about 30k RAM) +- MQTT SSL (Costs about 20k-30k RAM) - HTTP SSL (Costs about 30k RAM) - Developing/debugging Nuki devices and/or Nuki Hub, using WebSerial (Costs about 30k RAM) @@ -287,7 +294,6 @@ In a browser navigate to the IP address assigned to the ESP32. - Nuki Smartlock enabled: Enable if you want Nuki Hub to connect to a Nuki Lock (1.0-4.0 and Ultra) - Nuki Smartlock Ultra/Go/5th gen enabled: Enable if you want Nuki Hub to connect to a Nuki Lock Ultra/Go/5th gen Pro - Nuki Opener enabled: Enable if you want Nuki Hub to connect to a Nuki Opener -- New Nuki Bluetooth connection mode (disable if there are connection issues): Enable to use the latest Nuki BLE connection mode (recommended). Disable if you have issues communicating with the lock/opener #### Advanced Nuki Configuration @@ -705,7 +711,7 @@ If you have enabled "Allow updating using MQTT" you can also use the Home Assist
Alternatively you can select a binary file from your file system to update Nuki Hub or the Nuki Hub updator manually
You can only update Nuki Hub from the Nuki Hub updater and update the updater only from Nuki Hub
-You can reboot from Nuki Hub to the updater and vice versa by selecting the reboot option from the "Firware update" page
+You can reboot from Nuki Hub to the updater and vice versa by selecting the reboot option from the "Firmware update" page
When you are on the right application you can upload the new binary by clicking on "Browse" and select the new "nuki_hub\[board\].bin" or "nuki_hub_updater\[board\].bin" file and select "Upload file".
After about a minute the new firmware should be installed afterwhich the ESP will reboot automatically to the updated binary.
Selecting the wrong binary will lead to an unsuccessfull update
@@ -952,7 +958,16 @@ Now connect via Wi-Fi and change the network hardware to "Generic W5500".
If Ethernet hardware isn't detected or initialised properly after changing the network device, Wi-Fi will be used as a fallback.

-Note: LAN8720 modules are only supported on the ESP32, ESP32-P4 and ESP32-Solo1, not on the ESP32-S3, ESP32-C3 or ESP-C6
+Note: LAN8720 modules are only supported on the ESP32, ESP32-P4 and ESP32-Solo1, not on the ESP32-S3, ESP32-C3, ESP32-C5 or ESP-C6
+ +## Debugging crashes + +If you are running a pre-compiled version of NukiHub (latest release, beta or nightly) you can use https://technyon.github.io/nuki_hub/stacktrace/ to debug crashes. +You will need to collect a stack trace of the crash while connected to the serial logger (over USB). + +A stack trace usually starts with `Guru Mediation Error` and ends with `ELF file SHA256: ......` +Copy the entire stack trace to the box in our online decoder and select the correct binary you are using. +Click Run and check the output to find in which function the crash occurs and consider creating an issue on GitHub. ## FAQ / Troubleshooting diff --git a/boards/nuki-esp32-c5.json b/boards/nuki-esp32-c5.json new file mode 100644 index 0000000..f9719f0 --- /dev/null +++ b/boards/nuki-esp32-c5.json @@ -0,0 +1,31 @@ +{ + "build": { + "core": "esp32", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "mcu": "esp32c5", + "variant": "esp32c5" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "openocd_target": "esp32c5.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "ESP32-C5 (>=4MB QD, QUAD OR NO PSRAM)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32c5/esp32-c5-devkitc-1/user_guide.html", + "vendor": "Espressif" +} diff --git a/boards/nuki-esp32-c5dbg.json b/boards/nuki-esp32-c5dbg.json new file mode 100644 index 0000000..47fcb03 --- /dev/null +++ b/boards/nuki-esp32-c5dbg.json @@ -0,0 +1,31 @@ +{ + "build": { + "core": "esp32", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "mcu": "esp32c5", + "variant": "esp32c5" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "openocd_target": "esp32c5.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "ESP32-C5 (>=8MB QD, QUAD OR NO PSRAM)", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32c5/esp32-c5-devkitc-1/user_guide.html", + "vendor": "Espressif" +} diff --git a/boards/nuki-esp32-s3-nopsram.json b/boards/nuki-esp32-s3-nopsram.json new file mode 100644 index 0000000..6dba8fb --- /dev/null +++ b/boards/nuki-esp32-s3-nopsram.json @@ -0,0 +1,50 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_ESP32S3_DEV", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": [ + "esp-builtin" + ], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "ESP32-S3 (NO PSRAM)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html", + "vendor": "Espressif" +} diff --git a/boards/nuki-esp32dev-nopsram.json b/boards/nuki-esp32dev-nopsram.json new file mode 100644 index 0000000..2b46cd9 --- /dev/null +++ b/boards/nuki-esp32dev-nopsram.json @@ -0,0 +1,37 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV", + "f_cpu": "240000000L", + "f_flash": "40000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "esp32" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_board": "esp-wroom-32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif ESP32 Dev Module", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "AI Thinker" +} diff --git a/index.html b/index.html index 3c6fa7a..cad3d24 100644 --- a/index.html +++ b/index.html @@ -70,23 +70,29 @@
diff --git a/lib/BleScanner/src/BleScanner.cpp b/lib/BleScanner/src/BleScanner.cpp index a955550..1d1d997 100644 --- a/lib/BleScanner/src/BleScanner.cpp +++ b/lib/BleScanner/src/BleScanner.cpp @@ -20,18 +20,27 @@ Scanner::Scanner(int reservedSubscribers) { subscribers.reserve(reservedSubscribers); } +Scanner::~Scanner() { + Serial.println("Destroying scanner"); + bleScan->stop(); + Serial.println("bleScan stopped"); + bleScan->clearResults(); + Serial.println("bleScan results cleared"); + bleScan = nullptr; + Serial.println("bleScan nulled"); +} + void Scanner::initialize(const std::string& deviceName, const bool wantDuplicates, const uint16_t interval, const uint16_t window) { - if (!BLEDevice::isInitialized()) { + if (!NimBLEDevice::isInitialized()) { if (wantDuplicates) { - // reduce memory footprint, cache is not used anyway #ifdef CONFIG_BTDM_BLE_SCAN_DUPL NimBLEDevice::setScanDuplicateCacheSize(10); #endif } - BLEDevice::init(deviceName); + NimBLEDevice::init(deviceName); } - bleScan = BLEDevice::getScan(); + bleScan = NimBLEDevice::getScan(); #ifndef BLESCANNER_USE_LATEST_NIMBLE bleScan->setAdvertisedDeviceCallbacks(this, wantDuplicates); @@ -49,7 +58,6 @@ void Scanner::update() { } if (scanDuration == 0) { - // Avoid unbridled growth of results vector bleScan->setMaxResults(0); } else { log_w("Ble scanner max results not 0. Be aware of memory issue due to unbridled growth of results vector"); @@ -60,12 +68,6 @@ void Scanner::update() { #else bool result = bleScan->start(scanDuration * 1000, false); #endif - // if (!result) { - // scanErrors++; - // if (scanErrors % 100 == 0) { - // log_w("BLE Scan error (100x)"); - // } - // } } void Scanner::enableScanning(bool enable) { @@ -100,7 +102,7 @@ void Scanner::onResult(const NimBLEAdvertisedDevice* advertisedDevice) { } void Scanner::whitelist(BLEAddress bleAddress) { - BLEDevice::whiteListAdd(bleAddress); + NimBLEDevice::whiteListAdd(bleAddress); bleScan->setFilterPolicy(BLE_HCI_SCAN_FILT_USE_WL); } diff --git a/lib/BleScanner/src/BleScanner.h b/lib/BleScanner/src/BleScanner.h index 59480d1..9a9a10f 100644 --- a/lib/BleScanner/src/BleScanner.h +++ b/lib/BleScanner/src/BleScanner.h @@ -25,7 +25,7 @@ namespace BleScanner { class Scanner : public Publisher, BLEAdvertisedDeviceCallbacks { public: Scanner(int reservedSubscribers = 10); - ~Scanner() = default; + ~Scanner(); static Scanner& instance() { static Scanner* scanner = new Scanner(); // only initialized once on first call @@ -95,7 +95,7 @@ class Scanner : public Publisher, BLEAdvertisedDeviceCallbacks { private: uint32_t scanDuration = 0; //default indefinite scanning time - BLEScan* bleScan = nullptr; + NimBLEScan* bleScan = nullptr; std::vector subscribers; uint16_t scanErrors = 0; bool scanningEnabled = true; diff --git a/lib/BleScanner/src/main.cpp b/lib/BleScanner/src/main.cpp deleted file mode 100644 index 6829eea..0000000 --- a/lib/BleScanner/src/main.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -void setup() { - // put your setup code here, to run once: -} - -void loop() { - // put your main code here, to run repeatedly: -} \ No newline at end of file diff --git a/lib/MqttLogger/src/MqttLogger.cpp b/lib/MqttLogger/src/MqttLogger.cpp index 4290c12..32adddd 100644 --- a/lib/MqttLogger/src/MqttLogger.cpp +++ b/lib/MqttLogger/src/MqttLogger.cpp @@ -75,7 +75,7 @@ void MqttLogger::sendBuffer() { bool doSerial = this->mode==MqttLoggerMode::SerialOnly || this->mode==MqttLoggerMode::MqttAndSerial || this->mode==MqttLoggerMode::MqttAndSerialAndWeb || this->mode==MqttLoggerMode::SerialAndWeb; bool doWebSerial = this->mode==MqttLoggerMode::MqttAndSerialAndWeb || this->mode==MqttLoggerMode::SerialAndWeb; - if (this->mode!=MqttLoggerMode::SerialOnly && this->mode!=MqttLoggerMode::SerialAndWeb && this->client != NULL && this->client->connected()) + if (this->mode!=MqttLoggerMode::SerialOnly && this->mode!=MqttLoggerMode::SerialAndWeb && this->client != NULL && this->client->connected()) { this->client->publish(topic, 0, true, this->buffer, this->bufferCnt); } @@ -88,10 +88,9 @@ void MqttLogger::sendBuffer() Serial.write(this->buffer, this->bufferCnt); Serial.println(); } - if (doWebSerial) + if (doWebSerial && websocketHandler != nullptr) { - //WebSerial.write(this->buffer, this->bufferCnt); - //WebSerial.println(); + websocketHandler->sendAll(HTTPD_WS_TYPE_TEXT, this->buffer, this->bufferCnt); } this->bufferCnt=0; } diff --git a/lib/MqttLogger/src/MqttLogger.h b/lib/MqttLogger/src/MqttLogger.h index fceff1c..99bc689 100644 --- a/lib/MqttLogger/src/MqttLogger.h +++ b/lib/MqttLogger/src/MqttLogger.h @@ -12,10 +12,11 @@ #include #include #include -//#include "MycilaWebSerial.h" +#include "PsychicWebSocket.h" #define MQTT_MAX_PACKET_SIZE 1024 +extern PsychicWebSocketHandler* websocketHandler; extern bool coredumpPrinted; enum MqttLoggerMode { diff --git a/lib/PsychicHttp/src/PsychicRequest.cpp b/lib/PsychicHttp/src/PsychicRequest.cpp index 9f0b921..ee1639e 100644 --- a/lib/PsychicHttp/src/PsychicRequest.cpp +++ b/lib/PsychicHttp/src/PsychicRequest.cpp @@ -306,6 +306,9 @@ String PsychicRequest::getCookie(const char* key) if (!hasCookie(key, &size)) return cookie; + //Following line is needed until https://github.com/espressif/esp-idf/pull/16202 is merged and available in ESP-IDF 5.5 (beta2/RC/final) + size = httpd_req_get_hdr_value_len(this->_req, "Cookie"); + // allocate cookie buffer... keep it on the stack char buf[size]; diff --git a/lib/PsychicHttp/src/async_worker.cpp b/lib/PsychicHttp/src/async_worker.cpp index 96f0055..986e280 100644 --- a/lib/PsychicHttp/src/async_worker.cpp +++ b/lib/PsychicHttp/src/async_worker.cpp @@ -141,7 +141,7 @@ void start_async_req_workers(void) #endif /* Calculate the maximum size needed for the scratch buffer */ -#define HTTPD_SCRATCH_BUF MAX(HTTPD_MAX_REQ_HDR_LEN, HTTPD_MAX_URI_LEN) +#define HTTPD_SCRATCH_BUF MAX(CONFIG_HTTPD_MAX_REQ_HDR_LEN, CONFIG_HTTPD_MAX_URI_LEN) /** * @brief Auxiliary data structure for use during reception and processing @@ -169,55 +169,4 @@ struct httpd_req_aux bool ws_final; /*!< WebSocket FIN bit (final frame or not) */ uint8_t mask_key[4]; /*!< WebSocket mask key for this payload */ #endif -}; - -esp_err_t httpd_req_async_handler_begin(httpd_req_t* r, httpd_req_t** out) -{ - if (r == NULL || out == NULL) - { - return ESP_ERR_INVALID_ARG; - } - - // alloc async req - httpd_req_t* async = (httpd_req_t*)malloc(sizeof(httpd_req_t)); - if (async == NULL) - { - return ESP_ERR_NO_MEM; - } - memcpy((void*)async, (void*)r, sizeof(httpd_req_t)); - - // alloc async aux - async->aux = (httpd_req_aux*)malloc(sizeof(struct httpd_req_aux)); - if (async->aux == NULL) - { - free(async); - return ESP_ERR_NO_MEM; - } - memcpy(async->aux, r->aux, sizeof(struct httpd_req_aux)); - - // not available in 4.4.x - // mark socket as "in use" - // struct httpd_req_aux *ra = r->aux; - // ra->sd->for_async_req = true; - - *out = async; - - return ESP_OK; -} - -esp_err_t httpd_req_async_handler_complete(httpd_req_t* r) -{ - if (r == NULL) - { - return ESP_ERR_INVALID_ARG; - } - - // not available in 4.4.x - // struct httpd_req_aux *ra = (httpd_req_aux *)r->aux; - // ra->sd->for_async_req = false; - - free(r->aux); - free(r); - - return ESP_OK; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/nuki_ble b/lib/nuki_ble index 51eb268..98872d9 160000 --- a/lib/nuki_ble +++ b/lib/nuki_ble @@ -1 +1 @@ -Subproject commit 51eb268f53efdf216ce32bba5d214c770dffff45 +Subproject commit 98872d9004118f1ba2cb3685a30863eb19930cda diff --git a/partitions_c5.csv b/partitions_c5.csv new file mode 100644 index 0000000..c6dceb0 --- /dev/null +++ b/partitions_c5.csv @@ -0,0 +1 @@ +# Espressif ESP32 Partition Table # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x250000, app1, app, ota_1, 0x260000, 0x150000, spiffs, data, spiffs, 0x3B0000, 0x40000, coredump, data, coredump,0x3F0000, 0x10000, \ No newline at end of file diff --git a/partitions_c5dbg.csv b/partitions_c5dbg.csv new file mode 100644 index 0000000..dc80cf8 --- /dev/null +++ b/partitions_c5dbg.csv @@ -0,0 +1 @@ +# Espressif ESP32 Partition Table # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x500000, app1, app, ota_1, 0x510000, 0x150000, spiffs, data, spiffs, 0x660000, 0x40000, coredump, data, coredump,0x6A0000, 0x10000, \ No newline at end of file diff --git a/pio_package_post.py b/pio_package_post.py index 456b351..eaf03e8 100644 --- a/pio_package_post.py +++ b/pio_package_post.py @@ -13,6 +13,10 @@ def get_board_name(env): board = 'esp32gls10' elif env.get('BOARD') == 'nuki-esp32-s3-oct': board = 'esp32s3oct' + elif env.get('BOARD') == 'nuki-esp32-s3-nopsram': + board = 'esp32s3nopsram' + elif env.get('BOARD') == 'nuki-esp32dev-nopsram': + board = 'esp32nopsram' return board def create_target_dir(env): @@ -38,34 +42,6 @@ def copy_files(source, target, env): else: shutil.copy(file, f"{target_dir}/{file.name}") -def merge_bin(source, target, env): - #if not env.get('BUILD_TYPE') in ['release']: - # return - - board = get_board_name(env) - chip = env.get('BOARD_MCU') - target_dir = create_target_dir(env) - target_file = f"{target_dir}/webflash_nuki_hub_{board}.bin" - - app_position = "0x10000" - app_path = target[0].get_abspath() - - flash_args = list() - flash_args.append(app_position) - flash_args.append(app_path) - - for position, bin_file in env.get('FLASH_EXTRA_IMAGES'): - if "boot_app0.bin" in bin_file: - bin_file = "resources/boot_app0.bin" - flash_args.append(position) - flash_args.append(bin_file) - - flash_args.append("0x270000") - flash_args.append(f"{target_dir}/nuki_hub_updater_{board}.bin") - - cmd = f"esptool.py --chip {chip} merge_bin -o {target_file} --flash_mode dio --flash_freq keep --flash_size keep " + " ".join(flash_args) - env.Execute(cmd) - def package_last_files(source, target, env): files = ["resources/boot_app0.bin", "resources/how-to-flash.txt"] @@ -79,6 +55,4 @@ env.AddPostAction("$PROJECT_DIR/updater/release/" + get_board_name(env) + "/upda env.AddPostAction("$BUILD_DIR/firmware.bin", package_last_files) env.AddPostAction("$BUILD_DIR/partitions.bin", copy_files) env.AddPostAction("$BUILD_DIR/bootloader.bin", copy_files) -env.AddPostAction("$BUILD_DIR/firmware.elf", copy_files) - -#env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_bin) \ No newline at end of file +env.AddPostAction("$BUILD_DIR/firmware.elf", copy_files) \ No newline at end of file diff --git a/pio_package_pre.py b/pio_package_pre.py index 1cddbd0..8d609fd 100644 --- a/pio_package_pre.py +++ b/pio_package_pre.py @@ -10,6 +10,10 @@ def recursive_purge(dir, pattern): elif re.search(pattern, os.path.join(dir, f)): os.remove(os.path.join(dir, f)) +os.system("python resources/bin2array/bin2array.py icon/favicon-32x32.png -O src/webServerConstants/favicon-32x32.h -l 16") +os.system("python resources/bin2array/bin2array.py resources/style.css -O src/webServerConstants/style.h -l 16") +os.system("python resources/bin2array/bin2array.py resources/AsyncWebSerial/frontend/index.html -O src/webServerConstants/webSerial.h -l 16") + regex = r"\#define NUKI_HUB_DATE \"(.*)\"" content_new = "" file_content = "" diff --git a/platformio.ini b/platformio.ini index 3292efe..2455206 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ default_envs = esp32 boards_dir = boards [env] -platform = https://github.com/iranl/nuki_hub/raw/refs/heads/binary/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip platform_packages = framework = arduino, espidf board_build.embed_txtfiles = @@ -58,8 +58,12 @@ lib_ignore = SimpleBLE WiFiProv NimBLE-Arduino + AsyncTCP + ESPAsyncWebServer + ESPAsyncTCP ESPAsyncTCP-esphome AsyncTCP_RP2040W + RPAsyncTCP monitor_speed = 115200 monitor_filters = esp32_exception_decoder @@ -75,14 +79,24 @@ extra_scripts = build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 + -DNUKI_HUB_HTTPS_SERVER + +[env:esp32-nopsram] +board = nuki-esp32dev-nopsram +board_build.cmake_extra_args = + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.defaults.esp32-nopsram" +extra_scripts = + pre:pio_package_pre.py + post:pio_package_post.py +build_flags = + ${env.build_flags} + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE [env:esp32-gl-s10] extends = env:esp32 board = nuki-esp32gls10 board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.defaults.esp32;sdkconfig.gls10.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.defaults.esp32;sdkconfig.defaults.gls10" build_flags = ${env:esp32.build_flags} -DNUKI_TARGET_GL_S10=y @@ -91,66 +105,74 @@ build_flags = extends = env:esp32 board = esp32-c3-devkitc-02 board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults" +build_flags = + ${env.build_flags} + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE [env:esp32-s3] extends = env:esp32 board = nuki-esp32-s3 board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.defaults.esp32-s3" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-s3" + +[env:esp32-s3-nopsram] +extends = env:esp32 +board = nuki-esp32-s3-nopsram +board_build.cmake_extra_args = + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-s3-nopsram" [env:esp32-s3-oct] extends = env:esp32 board = nuki-esp32-s3-oct board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.defaults.esp32-s3-oct" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-s3-oct" + +[env:esp32-c5] +extends = env:esp32 +board_build.partitions = partitions_c5.csv +board = nuki-esp32-c5 +board_build.cmake_extra_args = + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-c5" [env:esp32-c6] extends = env:esp32 board = esp32-c6-devkitm-1 board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults" +build_flags = + ${env:esp32.build_flags} + -DNUKI_HUB_HTTPS_SERVER + -DFORCE_NUKI_HUB_HTTPS_SERVER [env:esp32-h2] extends = env:esp32 board = esp32-h2-devkitm-1 board_build.cmake_extra_args = -DNUKI_TARGET_H2=y - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults" lib_ignore = - BLE - BluetoothSerial - SimpleBLE - WiFiProv - NimBLE-Arduino - ESPAsyncTCP-esphome - AsyncTCP_RP2040W + ${env.lib_ignore} WiFiManager +build_flags = + ${env.build_flags} + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE [env:esp32-solo1] extends = env:esp32 board = nuki-esp32solo1 board_build.cmake_extra_args = -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.singlecore.defaults;sdkconfig.defaults.esp32-solo1" +build_flags = + ${env.build_flags} + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE [env:esp32-p4] extends = env:esp32 board_build.embed_txtfiles = board = esp32-p4 board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.defaults.esp32-p4" -build_flags = - ${env.build_flags} - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 - -DCONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 - -DCONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32 - -DCONFIG_ESP_WIFI_TX_BUFFER_TYPE=0 - -DCONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16 - -DCONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 - -DCONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0 - -DCONFIG_ESP_WIFI_SOFTAP_SUPPORT=y + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-p4" [env:esp32_dbg] extends = env:esp32 @@ -160,15 +182,24 @@ board_build.cmake_extra_args = build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 + -DDEBUG_NUKIHUB + -DNUKI_HUB_HTTPS_SERVER + +[env:esp32-nopsram_dbg] +extends = env:esp32-nopsram +custom_build = debug +board_build.cmake_extra_args = + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.defaults.esp32-nopsram" +build_flags = + ${env.build_flags} + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG -DDEBUG_NUKIHUB [env:esp32-gl-s10_dbg] extends = env:esp32-gl-s10 custom_build = debug board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.defaults.esp32;sdkconfig.gls10.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.defaults.esp32;sdkconfig.defaults.gls10" build_flags = ${env:esp32_dbg.build_flags} -DNUKI_TARGET_GL_S10=y @@ -177,62 +208,65 @@ build_flags = extends = env:esp32-c3 custom_build = debug board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.singlecore.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults" build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 -DDEBUG_NUKIHUB +[env:esp32-c5_dbg] +extends = env:esp32-c5 +board_build.partitions = partitions_c5dbg.csv +board = nuki-esp32-c5dbg +custom_build = debug +board_build.cmake_extra_args = + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-c5;sdkconfig.defaults.esp32-c5dbg" +build_flags = + ${env:esp32_dbg.build_flags} + [env:esp32-c6_dbg] extends = env:esp32-c6 custom_build = debug board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.singlecore.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults" build_flags = - ${env.build_flags} - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 - -DDEBUG_NUKIHUB + ${env:esp32_dbg.build_flags} + -DFORCE_NUKI_HUB_HTTPS_SERVER [env:esp32-h2_dbg] extends = env:esp32-h2 custom_build = debug board_build.cmake_extra_args = -DNUKI_TARGET_H2=y - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.singlecore.defaults" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.singlecore.defaults;sdkconfig.ramoptimize.defaults" build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 -DDEBUG_NUKIHUB [env:esp32-s3_dbg] extends = env:esp32-s3 custom_build = debug board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.defaults.esp32-s3" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-s3" build_flags = - ${env.build_flags} - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 - -DDEBUG_NUKIHUB + ${env:esp32_dbg.build_flags} + +[env:esp32-s3-nopsram_dbg] +extends = env:esp32-s3-nopsram +custom_build = debug +board_build.cmake_extra_args = + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-s3-nopsram" +build_flags = + ${env:esp32-nopsram_dbg.build_flags} [env:esp32-s3-oct_dbg] extends = env:esp32-s3-oct custom_build = debug board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.defaults.esp32-s3-oct" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-s3-oct" build_flags = - ${env.build_flags} - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 - -DDEBUG_NUKIHUB + ${env:esp32_dbg.build_flags} [env:esp32-solo1_dbg] extends = env:esp32-solo1 @@ -242,25 +276,12 @@ board_build.cmake_extra_args = build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 -DDEBUG_NUKIHUB [env:esp32-p4_dbg] extends = env:esp32-p4 custom_build = debug board_build.cmake_extra_args = - -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.defaults.esp32-p4" + -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.debug.defaults;sdkconfig.ramoptimize.defaults;sdkconfig.defaults.esp32-p4" build_flags = - ${env.build_flags} - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0 - -DCONFIG_BT_NIMBLE_LOG_LEVEL=0 - -DDEBUG_NUKIHUB - -DCONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 - -DCONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32 - -DCONFIG_ESP_WIFI_TX_BUFFER_TYPE=0 - -DCONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16 - -DCONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 - -DCONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0 - -DCONFIG_ESP_WIFI_SOFTAP_SUPPORT=y \ No newline at end of file + ${env:esp32_dbg.build_flags} \ No newline at end of file diff --git a/resources/AsyncWebSerial/.clang-format b/resources/AsyncWebSerial/.clang-format new file mode 100644 index 0000000..e34a665 --- /dev/null +++ b/resources/AsyncWebSerial/.clang-format @@ -0,0 +1,39 @@ +# Base style +BasedOnStyle: LLVM + +# Custom configurations +IndentWidth: 4 # Number of spaces per indentation level +TabWidth: 4 # Width of a tab (useful if 'UseTab' is set) +UseTab: Never # Use spaces instead of tabs + +# Function formatting +BreakBeforeBraces: Linux +AllowShortFunctionsOnASingleLine: Inline # Allow short functions to stay on a single line +AlwaysBreakBeforeMultilineStrings: false # Keep multiline strings together + +# Alignment +AlignConsecutiveAssignments: true # Align consecutive assignments +AlignTrailingComments: true # Align trailing comments + +# Parameter formatting +AllowAllParametersOfDeclarationOnNextLine: false # Parameters declared on the same line if possible + +# Array and list formatting +BinPackArguments: true # Place multiple arguments on the same line if possible +BinPackParameters: true # Same logic for parameters +ColumnLimit: 120 # Line length limit + +# Pointer and reference formatting +PointerAlignment: Right # Align pointer near the variable name or type (values: Left, Right, Middle) +ReferenceAlignment: Right # Same logic for references + +# Namespace +IndentNamespace: None # No indentation for namespaces +NamespaceIndentation: None # Alternative namespace indentation option + +# Comments +ReflowComments: true # Reformat comments to fit within the line length limit + +# Optional advanced configurations +IncludeBlocks: Preserve # Preserve blank lines between includes +SortIncludes: true # Sort includes alphabetically EmptyLineBeforeAccessModifier: Always # Blank line before "public:", "private:", etc. diff --git a/resources/AsyncWebSerial/.github/dependabot.yml b/resources/AsyncWebSerial/.github/dependabot.yml new file mode 100644 index 0000000..c833124 --- /dev/null +++ b/resources/AsyncWebSerial/.github/dependabot.yml @@ -0,0 +1,7 @@ +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/resources/AsyncWebSerial/.github/workflows/ci.yml b/resources/AsyncWebSerial/.github/workflows/ci.yml new file mode 100644 index 0000000..f22ffc6 --- /dev/null +++ b/resources/AsyncWebSerial/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: Build + +on: + push: + pull_request: + branches: + - master + +jobs: + platformio: + name: PlatformIO + runs-on: ubuntu-latest + strategy: + matrix: + board: [esp32dev] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache PlatformIO + uses: actions/cache@v4 + with: + key: ${{ runner.os }}-pio + path: | + ~/.cache/pip + ~/.platformio + + - name: Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install PlatformIO + run: | + python -m pip install -U pip + pip install -U platformio + + - run: PLATFORMIO_SRC_DIR=examples/demo PIO_BOARD=${{ matrix.board }} pio run \ No newline at end of file diff --git a/resources/AsyncWebSerial/.github/workflows/dependabot.yml b/resources/AsyncWebSerial/.github/workflows/dependabot.yml new file mode 100644 index 0000000..dffa49e --- /dev/null +++ b/resources/AsyncWebSerial/.github/workflows/dependabot.yml @@ -0,0 +1,19 @@ +name: PlatformIO Dependabot + +on: + workflow_dispatch: # option to manually trigger the workflow + schedule: + # Runs every day at 00:00 + - cron: '0 0 * * *' + +jobs: + dependabot: + runs-on: ubuntu-latest + name: run PlatformIO Dependabot + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: run PlatformIO Dependabot + uses: peterus/platformio_dependabot@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/resources/AsyncWebSerial/.gitignore b/resources/AsyncWebSerial/.gitignore new file mode 100644 index 0000000..20d4fc3 --- /dev/null +++ b/resources/AsyncWebSerial/.gitignore @@ -0,0 +1,3 @@ +/.pio +/.vscode +node_modules diff --git a/resources/AsyncWebSerial/LICENSE b/resources/AsyncWebSerial/LICENSE new file mode 100644 index 0000000..77ddc7c --- /dev/null +++ b/resources/AsyncWebSerial/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Andrea Sessa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/resources/AsyncWebSerial/README.md b/resources/AsyncWebSerial/README.md new file mode 100644 index 0000000..d0a7499 --- /dev/null +++ b/resources/AsyncWebSerial/README.md @@ -0,0 +1,95 @@ +# AsyncWebSerial + +AsyncWebSerial: Simplify ESP32 debugging and logging with seamless browser-based serial communication. + +## Features + +- **Browser-Based Serial Access**: Log and debug ESP32 microcontrollers directly from a web browser using the Web Serial API. +- **Real-Time Communication**: Stream logs and data in real time, eliminating the need for traditional serial monitors. +- **Asynchronous Operation**: Leverages asynchronous processing for smooth and efficient communication. +- **Cross-Platform Compatibility**: Works on any browser that supports the Web Serial API, no additional software required. +- **Customizable Integration**: Easily integrates into ESP32 projects, enabling tailored debugging and logging workflows. +- **User-Friendly Interface**: Provides an intuitive way to monitor and interact with the ESP32 during development. + +## Dependencies + +- [AsyncTCP (mathieucarbou fork)](https://github.com/mathieucarbou/AsyncTCP) +- [ESPAsyncWebServer (mathieucarbou fork)](https://github.com/mathieucarbou/ESPAsyncWebServer) + +## Build web interface + +Install frontend dependencies: + +```bash +yarn && yarn build +``` + +## Implementation + +### Add AsyncWebSerial to your platformIO project platformio.ini + +```ini +[env:esp32] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 +lib_deps = + circuitcode/AsyncWebSerial +``` + +### Include the library in your code + +```cpp +#include +``` + +### Initialize the library + +```cpp +AsyncWebServer server(80); +AsyncWebSerial webSerial; +``` + +### Use the library + +```cpp +void setup() { + webSerial.begin(&server); + server.begin(); +} + +void loop() { + webSerial.loop(); +} +``` + +### Send data to the AsyncWebSerial + +```cpp +webSerial.println("Hello, World!"); +webSerial.printf("Hello, %s!", "World"); +``` + +### Receive data from the AsyncWebSerial + +You can use the `onMessage` method to receive data from the AsyncWebSerial. The method accepts a callback function that will be called when data is received. The callback function can accept both `const char *` and `String` data types. + +```cpp +webSerial.onMessage([](const char *data, size_t len) { + Serial.write(data, len); +}); + +webSerial.onMessage([](const String &msg) { + Serial.println(msg); +}); +``` + +### Connect to the device serial page + +Navigate to `http:///webserial` to access the serial page. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + diff --git a/resources/AsyncWebSerial/examples/demo/demo.ino b/resources/AsyncWebSerial/examples/demo/demo.ino new file mode 100644 index 0000000..cc5b8f8 --- /dev/null +++ b/resources/AsyncWebSerial/examples/demo/demo.ino @@ -0,0 +1,18 @@ +#include +#include +#include + +AsyncWebSerial webSerial; +AsyncWebServer server(80); + +void setup() +{ + webSerial.begin(&server); + server.begin(); +} + +void loop() +{ + webSerial.loop(); + delay(10); +} \ No newline at end of file diff --git a/resources/AsyncWebSerial/frontend/build.js b/resources/AsyncWebSerial/frontend/build.js new file mode 100644 index 0000000..e5fb7aa --- /dev/null +++ b/resources/AsyncWebSerial/frontend/build.js @@ -0,0 +1,74 @@ +const fs = require("fs"); +const path = require("path"); +const zlib = require("zlib"); +const htmlMinifier = require("html-minifier-terser").minify; + +// Define paths +const inputPath = path.join(__dirname, "index.html"); +const outputPath = path.join(__dirname, "../src/AsyncWebSerialHTML.h"); + +// Function to split buffer into 64-byte chunks +function splitIntoChunks(buffer, chunkSize) { + let chunks = []; + for (let i = 0; i < buffer.length; i += chunkSize) { + chunks.push(buffer.slice(i, i + chunkSize)); + } + return chunks; +} + +(async function () { + // read the index.html file + const indexHtml = fs.readFileSync(inputPath, "utf8").toString(); + + // Minify the HTML content + const minifiedHtml = await htmlMinifier(indexHtml, { + collapseWhitespace: true, + removeComments: true, + removeAttributeQuotes: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true, + minifyCSS: true, + minifyJS: true, + shortAttributes: true, + shortClassName: true, + }); + + let oldSize = (indexHtml.length / 1024).toFixed(2); + let newSize = (minifiedHtml.length / 1024).toFixed(2); + + console.log(`[Minifier] Original: ${oldSize}KB | Minified: ${newSize}KB`); + + // Gzip the minified HTML content + let gzippedHtml = zlib.gzipSync(minifiedHtml); + + // Recreate the AsyncWebSerialHTML.h file with the new gzipped content + // the content is stored as a byte array split into 64 byte chunks to avoid issues with the IDE + let content = `#ifndef AsyncWebSerial_HTML_H +#define AsyncWebSerial_HTML_H + +#include + +const uint8_t ASYNCWEBSERIAL_HTML[] PROGMEM = {\n`; + + // Split gzipped HTML into 64-byte chunks + let chunks = splitIntoChunks(gzippedHtml, 64); + chunks.forEach((chunk, index) => { + content += ` ${Array.from(chunk) + .map((byte) => `0x${byte.toString(16).padStart(2, "0")}`) + .join(", ")}`; + if (index < chunks.length - 1) { + content += ",\n"; + } + }); + + content += `\n}; + +#endif // AsyncWebSerial_HTML_H`; + + // Write the content to the output file + fs.writeFileSync(outputPath, content); + + console.log("AsyncWebSerialHTML.h file created successfully!"); +})(); diff --git a/resources/AsyncWebSerial/frontend/index.html b/resources/AsyncWebSerial/frontend/index.html new file mode 100644 index 0000000..737a8aa --- /dev/null +++ b/resources/AsyncWebSerial/frontend/index.html @@ -0,0 +1,365 @@ + + + + + + + NukiHub WebSerial + + + + + +
+
+
+
+ Buffer size: + +
+ + + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/resources/AsyncWebSerial/frontend/package.json b/resources/AsyncWebSerial/frontend/package.json new file mode 100644 index 0000000..c6a889b --- /dev/null +++ b/resources/AsyncWebSerial/frontend/package.json @@ -0,0 +1,12 @@ +{ + "scripts": { + "build": "node build.js" + }, + "dependencies": { + "html-minifier-terser": "^7.1.0" + }, + "license": "MIT", + "files": [ + "LICENSE" + ] +} \ No newline at end of file diff --git a/resources/AsyncWebSerial/frontend/yarn.lock b/resources/AsyncWebSerial/frontend/yarn.lock new file mode 100644 index 0000000..29fb547 --- /dev/null +++ b/resources/AsyncWebSerial/frontend/yarn.lock @@ -0,0 +1,168 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +acorn@^8.8.2: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +clean-css@~5.3.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" + integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== + dependencies: + source-map "~0.6.0" + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +html-minifier-terser@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz#18752e23a2f0ed4b0f550f217bb41693e975b942" + integrity sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA== + dependencies: + camel-case "^4.1.2" + clean-css "~5.3.2" + commander "^10.0.0" + entities "^4.4.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.15.1" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +terser@^5.15.1: + version "5.36.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e" + integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +tslib@^2.0.3: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== diff --git a/resources/AsyncWebSerial/library.json b/resources/AsyncWebSerial/library.json new file mode 100644 index 0000000..22c3c52 --- /dev/null +++ b/resources/AsyncWebSerial/library.json @@ -0,0 +1,33 @@ +{ + "name": "AsyncWebSerial", + "version": "1.0.1", + "keywords": "web serial, ESP32", + "description": "AsyncWebSerial: Simplify ESP32 debugging and logging with seamless browser-based serial communication.", + "homepage": "https://github.com/circuitcode/AsyncWebSerial", + "repository": { + "type": "git", + "url": "https://github.com/circuitcode/AsyncWebSerial.git" + }, + "authors": [ + { + "name": "Andrea Sessa", + "maintanier": true + } + ], + "license": "MIT", + "framworks": "arduino", + "platforms": "espressif32", + "headers": [ + "AsyncWebSerial.h" + ], + "export": { + "include": [ + "examples", + "src", + "library.json", + "library.properties", + "LICENSE", + "README.md" + ] + } +} \ No newline at end of file diff --git a/resources/AsyncWebSerial/library.properties b/resources/AsyncWebSerial/library.properties new file mode 100644 index 0000000..586227e --- /dev/null +++ b/resources/AsyncWebSerial/library.properties @@ -0,0 +1,10 @@ +name=AsyncWebSerial +version=1.0.1 +author=Andrea Sessa +maintainer=Andrea Sessa +category=Device Control +sentence=Browser-based logging and debugging for ESP32 using the Web Serial API. +paragraph=AsyncWebSerial is a lightweight library designed for ESP32 microcontrollers, enabling developers to log and debug their projects directly from a web browser. Its asynchronous design ensures smooth communication, making it an ideal tool for modern ESP32 development workflows. +url=https://github.com/circuitcode/AsyncWebSerial +architectures=esp32 +license=MIT diff --git a/resources/AsyncWebSerial/platformio.ini b/resources/AsyncWebSerial/platformio.ini new file mode 100644 index 0000000..258543a --- /dev/null +++ b/resources/AsyncWebSerial/platformio.ini @@ -0,0 +1,13 @@ +[platformio] +lib_dir = . + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + mathieucarbou/AsyncTCP@^3.2.14 + mathieucarbou/ESPAsyncWebServer@^3.3.23 +build_flags = + -D CONFIG_ASYNC_TCP_QUEUE_SIZE=512 + -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 \ No newline at end of file diff --git a/resources/AsyncWebSerial/src/AsyncWebSerial.cpp b/resources/AsyncWebSerial/src/AsyncWebSerial.cpp new file mode 100644 index 0000000..b68f3a1 --- /dev/null +++ b/resources/AsyncWebSerial/src/AsyncWebSerial.cpp @@ -0,0 +1,119 @@ +/* + * AsyncWebSerial.cpp + * + * This file implements the AsyncWebSerial class, which provides a web-based serial interface + * using an asynchronous web server and WebSocket communication. It allows for sending + * and receiving serial data over a web interface, with optional authentication. + * + * Usage: + * - Call begin() to initialize the AsyncWebSerial with a server and URL. + * - Use onMessage() to set a callback for received messages. + * - Use setAuthentication() to enable authentication for the web interface. + * - Use the Print interface to send data to the web interface. + * - Call loop() periodically to clean up inactive clients. + */ + +#include "AsyncWebSerial.h" +#include "AsyncWebSerialHTML.h" + +void AsyncWebSerial::begin(AsyncWebServer *server, const char *url) +{ + _server = server; + _socket = new AsyncWebSocket("/ws_serial"); + + if (_isAuthenticationRequired) + _socket->setAuthentication(_username.c_str(), _password.c_str()); + + // handle web page request + _server->on(url, HTTP_GET, [&](AsyncWebServerRequest *request) { + if (_isAuthenticationRequired && !request->authenticate(_username.c_str(), _password.c_str())) + return request->requestAuthentication(); + + AsyncWebServerResponse *response = + request->beginResponse(200, "text/html", ASYNCWEBSERIAL_HTML, sizeof(ASYNCWEBSERIAL_HTML)); + response->addHeader("Content-Encoding", "gzip"); + request->send(response); + }); + + // handle websocket connection + _socket->onEvent([&](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, + uint8_t *data, size_t len) { + switch (type) { + case WS_EVT_CONNECT: + // set the client to not close when the queue is full + client->setCloseClientOnQueueFull(false); + break; + case WS_EVT_DATA: + if (len > 0) { + // invoke the message received callback + if (_onMessageReceived != nullptr) + _onMessageReceived(data, len); + } + break; + } + }); + + _server->addHandler(_socket); +} + +// onMessage callback setter +void AsyncWebSerial::onMessage(WSMessageCallback callback) +{ + _onMessageReceived = callback; +} + +// onMessage callback setter +void AsyncWebSerial::onMessage(WSStringMessageCallback callback) +{ + // keep a reference to the callback function + _stringMessageCallback = callback; + + // set the internal callback to convert the uint8_t data to a string and + // call the string callback + _onMessageReceived = [&](uint8_t *data, size_t len) { + if (data && len) { + String msg; + msg.reserve(len); + msg = String((char *)data, len); + _stringMessageCallback(msg); + } + }; +} + +void AsyncWebSerial::setAuthentication(const String &username, const String &password) +{ + _username = username; + _password = password; + _isAuthenticationRequired = !_username.isEmpty() && !_password.isEmpty(); + + // if the socket is already created, set the authentication immediately + if (_socket != nullptr) + _socket->setAuthentication(_username.c_str(), _password.c_str()); +} + +void AsyncWebSerial::loop() +{ + if (_socket) + _socket->cleanupClients(); +} + +// Print interface implementation +size_t AsyncWebSerial::write(uint8_t m) +{ + if (!_socket) + return 0; + + return write(&m, 1); + return 1; +} + +// Print interface implementation +size_t AsyncWebSerial::write(const uint8_t *buffer, size_t size) +{ + if (!_socket || size == 0) + return 0; + + String message((const char *)buffer, size); + _socket->textAll(message); + return size; +} \ No newline at end of file diff --git a/resources/AsyncWebSerial/src/AsyncWebSerial.h b/resources/AsyncWebSerial/src/AsyncWebSerial.h new file mode 100644 index 0000000..be1edb9 --- /dev/null +++ b/resources/AsyncWebSerial/src/AsyncWebSerial.h @@ -0,0 +1,48 @@ +/** + * @file AsyncWebSerial.h + * @brief A web serial interface for the ESP32 microcontroller. + * @license MIT License (https://opensource.org/licenses/MIT) + */ + +#ifndef AsyncWebSerial_h +#define AsyncWebSerial_h + +#include +#include +#include +#include +#include + +#include + +// define the callable object type for the message received callback +typedef std::function WSMessageCallback; +typedef std::function WSStringMessageCallback; + +class AsyncWebSerial : public Print +{ + public: + void begin(AsyncWebServer *server, const char *url = "/webserial"); + + void setAuthentication(const String &username, const String &password); + void onMessage(WSMessageCallback callback); + void onMessage(WSStringMessageCallback callback); + + void loop(); + + // Print interface implementation + virtual size_t write(uint8_t) override; + virtual size_t write(const uint8_t *buffer, size_t size) override; + + private: + AsyncWebServer *_server; + AsyncWebSocket *_socket; + bool _isAuthenticationRequired = false; + String _username; + String _password; + + WSMessageCallback _onMessageReceived = nullptr; + WSStringMessageCallback _stringMessageCallback = nullptr; +}; + +#endif // AsyncWebSerial_h \ No newline at end of file diff --git a/resources/AsyncWebSerial/src/AsyncWebSerialHTML.h b/resources/AsyncWebSerial/src/AsyncWebSerialHTML.h new file mode 100644 index 0000000..3d09129 --- /dev/null +++ b/resources/AsyncWebSerial/src/AsyncWebSerialHTML.h @@ -0,0 +1,46 @@ +#ifndef AsyncWebSerial_HTML_H +#define AsyncWebSerial_HTML_H + +#include + +const uint8_t ASYNCWEBSERIAL_HTML[] PROGMEM = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x59, 0x61, 0x8f, 0xdb, 0x36, 0x12, 0xfd, 0x2b, 0xac, 0xf6, 0x9a, 0x48, 0xb7, 0xa6, 0x2c, 0xc9, 0xf6, 0xae, 0x57, 0xb2, 0x0c, 0x24, 0x9b, 0x1c, 0x12, 0x20, 0xc9, 0x01, 0xb7, 0x29, 0x7a, 0x40, 0x51, 0xb4, 0x94, 0x34, 0xb2, 0xd9, 0xa5, 0x48, 0x95, 0xa2, 0x6c, 0x6f, 0x04, 0xff, 0xf7, 0x03, 0x29, 0xd9, 0x96, 0xec, 0x4d, + 0xdb, 0xe0, 0xbe, 0x35, 0x89, 0x2d, 0x71, 0x38, 0xe4, 0x90, 0x6f, 0xde, 0x0c, 0xc7, 0xcc, 0xe2, 0xbb, 0x4c, 0xa4, 0xea, 0xa9, 0x04, 0xb4, 0x56, 0x05, 0x5b, 0x2e, 0xba, 0x6f, 0x20, 0xd9, 0x72, 0x51, 0x80, 0x22, 0x28, 0x5d, 0x13, 0x59, 0x81, 0x8a, 0x6b, 0x95, 0xe3, 0x79, 0x27, 0x5b, 0x2b, 0x55, 0x62, 0xf8, 0xbd, 0xa6, 0x9b, 0xf8, 0xbf, 0xf8, 0x87, 0x57, 0xf8, 0x5e, 0x14, 0x25, 0x51, + 0x34, 0x61, 0x80, 0x52, 0xc1, 0x15, 0x70, 0x15, 0x5b, 0xef, 0xdf, 0xc6, 0x90, 0xad, 0xc0, 0x5a, 0x2e, 0x14, 0x55, 0x0c, 0x96, 0xaf, 0xaa, 0x27, 0x9e, 0xfe, 0x08, 0xc9, 0x03, 0x48, 0x4a, 0xd8, 0x62, 0xdc, 0x4a, 0xdb, 0xf9, 0x38, 0x29, 0x20, 0xde, 0x50, 0xd8, 0x96, 0x42, 0xaa, 0xd3, 0x14, 0x5b, 0x9a, 0xa9, 0x75, 0x9c, 0xc1, 0x86, 0xa6, 0x80, 0x4d, 0x63, 0x44, 0x39, 0x55, 0x94, 0x30, + 0x5c, 0xa5, 0x84, 0x41, 0xec, 0x5b, 0xcb, 0x45, 0xa5, 0x9e, 0x18, 0x2c, 0x33, 0xba, 0x69, 0x32, 0x5a, 0x95, 0x8c, 0x3c, 0x85, 0x09, 0x13, 0xe9, 0xe3, 0x9e, 0x34, 0x05, 0x91, 0x2b, 0xca, 0x43, 0x77, 0x2a, 0xa1, 0x88, 0x14, 0xec, 0x14, 0xce, 0x20, 0x15, 0x92, 0x28, 0x2a, 0x78, 0xc8, 0x05, 0x87, 0xfd, 0x3f, 0x47, 0x61, 0x48, 0x72, 0x05, 0x72, 0x14, 0x86, 0x09, 0xe4, 0x42, 0x42, 0x93, + 0x88, 0x1d, 0xae, 0xe8, 0x17, 0xca, 0x57, 0x61, 0x22, 0x64, 0x06, 0x12, 0x27, 0x62, 0x17, 0x75, 0xaf, 0x66, 0x0d, 0xa1, 0xb7, 0xd7, 0x20, 0x35, 0x6b, 0xa0, 0xab, 0xb5, 0x0a, 0x7d, 0xcf, 0xfb, 0x7e, 0x9f, 0x88, 0xec, 0xa9, 0x11, 0x1b, 0x90, 0x55, 0x2a, 0x05, 0x63, 0x38, 0x81, 0x35, 0xd9, 0x50, 0x21, 0x8d, 0x99, 0xa8, 0xa7, 0x19, 0xb5, 0x53, 0x98, 0xd7, 0x6e, 0x7d, 0x5e, 0x54, 0x92, + 0x2c, 0xd3, 0x06, 0xbd, 0x28, 0x17, 0x5c, 0xe1, 0x9c, 0x14, 0x94, 0x3d, 0x85, 0x98, 0x94, 0x25, 0x03, 0x5c, 0x3d, 0x55, 0x0a, 0x8a, 0x51, 0xfb, 0xc0, 0x35, 0x1d, 0xbd, 0x66, 0x94, 0x3f, 0x7e, 0x24, 0xe9, 0x83, 0x91, 0xfc, 0x4b, 0x70, 0x35, 0xb2, 0x1e, 0x60, 0x25, 0x00, 0xfd, 0xf0, 0xde, 0x1a, 0xfd, 0x47, 0x24, 0x42, 0x89, 0x91, 0xf5, 0x0e, 0xd8, 0x06, 0x14, 0x4d, 0x09, 0xfa, 0x04, + 0x35, 0x58, 0xa3, 0x8a, 0xf0, 0x0a, 0x57, 0x20, 0x69, 0x1e, 0x25, 0x24, 0x7d, 0x5c, 0x49, 0x51, 0xf3, 0x2c, 0xbc, 0xf2, 0x3c, 0xaf, 0xbf, 0xbc, 0xbd, 0x4b, 0xca, 0xb2, 0xbf, 0xb3, 0xe8, 0x00, 0x6a, 0xce, 0x60, 0x17, 0xe9, 0x2f, 0x9c, 0x51, 0x09, 0xa9, 0xc1, 0x30, 0x15, 0xac, 0x2e, 0xf8, 0xde, 0x5d, 0x49, 0x9a, 0x1d, 0xd1, 0xd7, 0x8d, 0xbd, 0xbb, 0x22, 0x25, 0x0e, 0x9a, 0x15, 0x29, + 0x43, 0x77, 0x26, 0xa1, 0xd8, 0xbb, 0x9d, 0x53, 0x1b, 0x3d, 0x45, 0xe8, 0x23, 0x1f, 0x79, 0xdf, 0x47, 0x84, 0xd1, 0x15, 0xc7, 0x5d, 0x8f, 0xb1, 0x80, 0x81, 0x67, 0x03, 0xb8, 0x52, 0xc1, 0x84, 0x0c, 0xaf, 0xb2, 0x69, 0x36, 0xcd, 0xe6, 0x03, 0x78, 0x0a, 0xc1, 0x45, 0x55, 0x92, 0x14, 0x22, 0x8d, 0x7b, 0xce, 0xc4, 0x16, 0xef, 0xc2, 0x35, 0xcd, 0x32, 0xe0, 0x27, 0xc9, 0x53, 0x48, 0x6a, + 0x25, 0xda, 0x71, 0x15, 0xfd, 0x02, 0xa1, 0x3f, 0x2d, 0x77, 0xc7, 0xc5, 0xa0, 0xb2, 0x39, 0xfa, 0xe0, 0x38, 0x64, 0x2b, 0x49, 0x19, 0x26, 0x12, 0xc8, 0x23, 0xde, 0x0a, 0x99, 0xb5, 0xc4, 0x31, 0x42, 0xfd, 0x15, 0x6d, 0xd7, 0x54, 0x01, 0x36, 0x86, 0xc3, 0x52, 0x02, 0x66, 0x94, 0xc3, 0xde, 0x2d, 0x09, 0x07, 0xd6, 0x94, 0xa2, 0xa2, 0x06, 0x18, 0x09, 0x8c, 0x28, 0xba, 0x81, 0x8e, 0x37, + 0xe1, 0x55, 0x9e, 0xe7, 0xc8, 0x00, 0x81, 0x2a, 0xc1, 0x68, 0x76, 0xe0, 0x93, 0x24, 0x19, 0xad, 0xab, 0xd0, 0xd7, 0x04, 0x2d, 0xc8, 0xae, 0xe3, 0xd7, 0x54, 0x2b, 0x76, 0x44, 0x49, 0x09, 0x4b, 0x6d, 0x8d, 0x04, 0xc2, 0x48, 0xab, 0x39, 0xbd, 0xbd, 0x14, 0x90, 0xd1, 0xba, 0xd8, 0xbb, 0x49, 0xad, 0x94, 0xe0, 0x55, 0x33, 0x70, 0x55, 0x8b, 0x2d, 0x55, 0x50, 0x54, 0x61, 0x0a, 0x5c, 0x81, + 0x8c, 0x7e, 0xab, 0x2b, 0x45, 0xf3, 0xa7, 0x4b, 0xc0, 0x5b, 0x10, 0x30, 0x83, 0x5c, 0xb5, 0x78, 0x75, 0x02, 0x69, 0xfc, 0x60, 0x24, 0xc7, 0x58, 0x50, 0x4a, 0x14, 0xdd, 0x3a, 0xfd, 0xf2, 0x82, 0x11, 0x52, 0x6c, 0xa3, 0x96, 0x15, 0x58, 0xfb, 0xde, 0x37, 0x7b, 0x3e, 0xf0, 0x1b, 0x2b, 0xd1, 0xd1, 0xe1, 0x28, 0x69, 0xe7, 0x3b, 0x13, 0xb6, 0x66, 0x5b, 0x59, 0xe7, 0x7f, 0xe2, 0x13, 0x9f, + 0x90, 0xe3, 0x56, 0x51, 0xfb, 0x6c, 0xd2, 0x5a, 0x56, 0x42, 0x86, 0xa5, 0xa0, 0x66, 0x83, 0x87, 0x38, 0x9a, 0x97, 0x3b, 0xe4, 0x7b, 0xe5, 0x0e, 0xcd, 0xf5, 0x0a, 0xcf, 0xf0, 0x8a, 0x44, 0xad, 0xb4, 0xd3, 0xb0, 0x49, 0x1c, 0x6d, 0x80, 0x76, 0x6e, 0xf2, 0x86, 0xe6, 0x7a, 0x81, 0x82, 0xdb, 0x0e, 0x25, 0x09, 0xaf, 0x4a, 0x22, 0x81, 0xab, 0xf3, 0xb5, 0xa0, 0x6a, 0xb3, 0x6a, 0x3a, 0x5c, + 0xda, 0x8c, 0x73, 0xa0, 0xb1, 0x69, 0x9d, 0xab, 0x87, 0x6b, 0x4d, 0xb8, 0xe6, 0xc2, 0xc2, 0x95, 0x3f, 0xf7, 0xe7, 0x7e, 0xb2, 0x77, 0xb7, 0x38, 0xaf, 0x19, 0x6b, 0x4e, 0xe9, 0x62, 0xef, 0x1a, 0x3d, 0xc8, 0x9a, 0x21, 0x79, 0xba, 0xf8, 0xd2, 0x8e, 0x18, 0x30, 0x40, 0x87, 0xa5, 0xd8, 0x9a, 0x78, 0xc3, 0xfa, 0x2d, 0xf4, 0xf7, 0x6e, 0xb5, 0x26, 0x99, 0x96, 0x51, 0xa6, 0x40, 0x86, 0x99, + 0x14, 0x25, 0x6e, 0x45, 0xb6, 0x87, 0xa6, 0xe5, 0x0e, 0x4d, 0xca, 0x1d, 0x92, 0xab, 0xc4, 0xf6, 0x90, 0xfe, 0x3b, 0x46, 0xae, 0x77, 0xeb, 0x38, 0x68, 0xa8, 0x17, 0x94, 0x3b, 0xf3, 0x19, 0xea, 0xdd, 0x38, 0xce, 0xde, 0x35, 0x6c, 0xc3, 0x2d, 0xdb, 0x9a, 0x4b, 0x02, 0xea, 0xb8, 0x2b, 0x0a, 0xc2, 0xb3, 0x5f, 0x34, 0x03, 0x09, 0xe5, 0x1a, 0x80, 0x76, 0x2f, 0x4a, 0x94, 0x3d, 0x52, 0x0d, + 0xc8, 0xd6, 0x97, 0x0c, 0x30, 0x8a, 0xbe, 0x95, 0xef, 0x26, 0x68, 0x71, 0x02, 0x6a, 0x0b, 0xc0, 0x8f, 0x64, 0xd1, 0x51, 0xb5, 0xa7, 0xbc, 0xac, 0x55, 0x73, 0x91, 0x90, 0xdd, 0xc9, 0xad, 0x09, 0x5a, 0xd7, 0x3c, 0x8e, 0x34, 0x69, 0x83, 0x18, 0x5d, 0xdd, 0x24, 0xb7, 0xc1, 0xdc, 0x3b, 0x0b, 0x66, 0x37, 0x30, 0xba, 0xcf, 0xa7, 0xaa, 0x13, 0x19, 0xdd, 0x79, 0x3b, 0xa7, 0xa1, 0xe2, 0x91, + 0x2a, 0xad, 0x99, 0x0b, 0x52, 0xe4, 0xe6, 0x8f, 0x97, 0x45, 0xa7, 0xf6, 0x11, 0xcc, 0x3e, 0x45, 0xae, 0xaa, 0x3a, 0x29, 0xa8, 0xc2, 0x5d, 0x78, 0x9c, 0xaf, 0x17, 0x66, 0x70, 0x0b, 0xc9, 0xe9, 0xdc, 0x31, 0xa9, 0xcd, 0x70, 0x3a, 0x17, 0xb2, 0xe8, 0x42, 0xe1, 0x64, 0x9c, 0x16, 0x64, 0xd5, 0x05, 0xc8, 0x59, 0xa0, 0x0d, 0x90, 0xef, 0x9d, 0x68, 0x7f, 0xc1, 0x09, 0x9d, 0xf8, 0x59, 0xcc, + 0x8e, 0xb0, 0x1b, 0xd0, 0x3b, 0x34, 0xfe, 0x0c, 0xb2, 0xa0, 0x9f, 0x27, 0xf2, 0x3c, 0xbf, 0x84, 0x4f, 0x33, 0x35, 0xb8, 0x43, 0xb7, 0x73, 0x14, 0xf8, 0x37, 0xce, 0x19, 0x48, 0xfd, 0xb8, 0xed, 0x47, 0xad, 0x09, 0xaa, 0x82, 0xe1, 0x69, 0xd3, 0xcf, 0x8e, 0xfe, 0xe1, 0x2c, 0xe3, 0x6d, 0xc2, 0xc3, 0x95, 0x22, 0xaa, 0x3e, 0x4b, 0xbd, 0x5f, 0xcd, 0xb3, 0x5f, 0x4b, 0xa2, 0x67, 0xfb, 0x3e, + 0xdb, 0xf5, 0x33, 0x9b, 0x3e, 0xed, 0x58, 0xef, 0xcd, 0xf7, 0x27, 0xc8, 0x7c, 0x82, 0xc0, 0x79, 0xce, 0x05, 0x83, 0xe0, 0x31, 0x60, 0x4c, 0x91, 0xfe, 0x77, 0xeb, 0x44, 0xbd, 0x03, 0x3a, 0x21, 0xd9, 0x0a, 0x3a, 0x24, 0x3a, 0xde, 0x1f, 0xc0, 0x38, 0x34, 0x87, 0x4e, 0xbb, 0xbb, 0xbb, 0xbb, 0xd3, 0x87, 0xa9, 0x19, 0xe8, 0xae, 0x24, 0x00, 0x7f, 0x26, 0x9f, 0x05, 0x41, 0x3a, 0x9b, 0xc1, + 0x41, 0x4b, 0x48, 0xc2, 0x57, 0xf0, 0x8c, 0x5a, 0x3e, 0xbb, 0x03, 0x2f, 0x39, 0xa8, 0x49, 0x9d, 0xe5, 0x2e, 0x74, 0x20, 0x9f, 0x4e, 0xa7, 0xd3, 0xfd, 0x62, 0xdc, 0x96, 0x7c, 0x8b, 0x71, 0x5b, 0xa5, 0xea, 0xda, 0x6b, 0xb9, 0xc8, 0xe8, 0x06, 0xa5, 0x8c, 0x54, 0x55, 0x4c, 0xca, 0xb2, 0x2d, 0x60, 0x41, 0xf6, 0xc5, 0x5d, 0xfe, 0x35, 0xa2, 0xe5, 0xeb, 0x3a, 0xcf, 0x41, 0x22, 0x03, 0x30, + 0x5a, 0x98, 0xe8, 0x47, 0x34, 0x8b, 0x93, 0x56, 0xdc, 0x0e, 0xb0, 0xba, 0x7c, 0x8b, 0xda, 0xd4, 0x67, 0xa1, 0x92, 0x91, 0x14, 0xd6, 0x82, 0x65, 0x20, 0x63, 0xab, 0x37, 0x83, 0x85, 0x36, 0x84, 0xd5, 0x10, 0xfb, 0x9e, 0xe7, 0x2d, 0x17, 0x63, 0x3d, 0xff, 0xa2, 0x23, 0xd7, 0x57, 0x66, 0x12, 0x3c, 0x65, 0x34, 0x7d, 0x8c, 0x15, 0xc8, 0x82, 0x72, 0xc2, 0xee, 0x19, 0x10, 0x6e, 0x3b, 0xcb, + 0x45, 0xb5, 0x59, 0xa1, 0x5d, 0xc1, 0x78, 0x15, 0xeb, 0x12, 0x3b, 0x1c, 0x8f, 0xb7, 0xdb, 0xad, 0xbb, 0x9d, 0xb8, 0x42, 0xae, 0xc6, 0x81, 0xe7, 0x79, 0x63, 0xad, 0xd0, 0x96, 0xc5, 0xc1, 0x14, 0xb5, 0xee, 0xd1, 0x6f, 0xba, 0x76, 0x7e, 0x2d, 0x76, 0xb1, 0xa5, 0xf3, 0xb1, 0x71, 0xaf, 0x85, 0x72, 0xca, 0x58, 0xac, 0xa3, 0x17, 0x55, 0x4a, 0x8a, 0x47, 0x88, 0xd3, 0x5a, 0xea, 0x93, 0xeb, + 0x5e, 0xa3, 0xd9, 0xc9, 0x70, 0x37, 0xd7, 0xa1, 0xa9, 0x59, 0x96, 0x92, 0x32, 0x36, 0x0b, 0xee, 0x0b, 0x7f, 0x13, 0x94, 0xb7, 0xd2, 0xe5, 0xa2, 0x24, 0x6a, 0x7d, 0x98, 0xd3, 0xcc, 0x9f, 0xc5, 0xd6, 0x47, 0x0f, 0x79, 0xeb, 0x60, 0xba, 0x09, 0xa6, 0xef, 0xbc, 0x2f, 0x7d, 0xdb, 0xe3, 0x4e, 0x5f, 0xeb, 0x4c, 0xd1, 0x2d, 0xf3, 0x6f, 0x90, 0x67, 0xf5, 0x85, 0xbe, 0x87, 0x7c, 0x9f, 0x79, + 0xe8, 0x66, 0x28, 0x9d, 0x3e, 0x27, 0x9d, 0xe9, 0x09, 0x90, 0x1f, 0x90, 0x00, 0x05, 0xa8, 0x3d, 0x7c, 0x02, 0x14, 0xac, 0xe7, 0x83, 0x36, 0x0e, 0x98, 0x8f, 0xb0, 0x1f, 0x0c, 0x46, 0xde, 0xa1, 0xdb, 0x0d, 0x9e, 0x10, 0x53, 0x78, 0x22, 0x0f, 0xe9, 0x27, 0xf6, 0xd7, 0xd3, 0x81, 0xc0, 0xdf, 0x4c, 0xf4, 0x18, 0x8d, 0xf1, 0x72, 0x31, 0x6e, 0x3d, 0xb8, 0x44, 0x7f, 0xd1, 0x95, 0x16, 0x70, + 0x92, 0x30, 0x78, 0x30, 0xf5, 0x7f, 0xfc, 0x5d, 0xbf, 0x65, 0xfd, 0xdd, 0xfd, 0x3a, 0x43, 0xfe, 0xe4, 0xe8, 0x02, 0xdf, 0xb8, 0x60, 0xed, 0x7b, 0x03, 0x49, 0xb0, 0xb9, 0xe9, 0xb5, 0xb1, 0xf6, 0x1a, 0x1e, 0xa8, 0x60, 0x3d, 0x6a, 0x83, 0x6f, 0xbe, 0x0c, 0x69, 0xe0, 0x23, 0xff, 0xa6, 0x73, 0x92, 0x6f, 0xbc, 0xeb, 0x1d, 0x5d, 0xe6, 0xe9, 0x31, 0x43, 0x2e, 0xcd, 0x91, 0xef, 0x6f, 0xf0, + 0x94, 0x4c, 0xd1, 0xb4, 0xf3, 0xe9, 0x1c, 0x79, 0x9b, 0xe9, 0xff, 0xed, 0xd5, 0xcf, 0xb4, 0x80, 0x4a, 0x91, 0xa2, 0x3c, 0x38, 0xf6, 0x28, 0xf8, 0xdb, 0xfb, 0x56, 0x1f, 0x2b, 0xe4, 0x0e, 0xdd, 0x75, 0x0e, 0xf0, 0xe7, 0xc8, 0xeb, 0x9a, 0xc6, 0x03, 0xba, 0x3d, 0xf4, 0x58, 0x80, 0x6e, 0x37, 0x33, 0x36, 0x41, 0xcf, 0xc4, 0x52, 0x97, 0x1b, 0xc7, 0x97, 0xc9, 0xb9, 0x3b, 0x2a, 0x0f, 0x1a, + 0xb9, 0x10, 0xea, 0x5c, 0xe1, 0xac, 0x72, 0xd4, 0x4a, 0xb2, 0x38, 0xf8, 0x4f, 0x1f, 0xb1, 0xa8, 0x2d, 0x99, 0x51, 0xbf, 0x0a, 0xb5, 0x96, 0xa7, 0xe4, 0xde, 0x4d, 0x81, 0xf4, 0xef, 0x99, 0x54, 0x14, 0x25, 0x03, 0x05, 0xb1, 0xc8, 0x73, 0x24, 0xf5, 0x4d, 0x86, 0x84, 0x2c, 0xb6, 0xac, 0xa1, 0xb5, 0x61, 0xc6, 0x7f, 0xab, 0x27, 0x44, 0x87, 0xae, 0x35, 0x48, 0x7d, 0xb7, 0x71, 0x5a, 0xa1, + 0xae, 0x15, 0x8e, 0x79, 0x9f, 0x66, 0xf1, 0xb0, 0xca, 0xd0, 0x37, 0x2c, 0x9d, 0xe8, 0xef, 0xce, 0x18, 0x9d, 0xd0, 0xa7, 0xcc, 0xd7, 0x09, 0xd6, 0x1f, 0x50, 0x23, 0xf0, 0xd1, 0x84, 0xe1, 0x1b, 0x77, 0x86, 0xfc, 0x39, 0x71, 0x67, 0x33, 0xa4, 0x3f, 0x5d, 0xf4, 0xfb, 0xc8, 0x63, 0x78, 0xe2, 0xce, 0x10, 0xbe, 0x65, 0xf8, 0x16, 0xe9, 0xd7, 0x33, 0x15, 0x4d, 0x36, 0xe6, 0xcf, 0x91, 0x9e, + 0xe0, 0xeb, 0xd4, 0xd2, 0xa4, 0x38, 0x34, 0x06, 0xf4, 0x1a, 0x16, 0x6c, 0x3d, 0xfd, 0x96, 0x69, 0x5d, 0xb3, 0xad, 0x25, 0xaa, 0x54, 0xd2, 0x52, 0x2d, 0x19, 0x28, 0xb4, 0x85, 0xa4, 0x12, 0xe9, 0x23, 0xa8, 0xd1, 0x30, 0xbf, 0x7b, 0xa3, 0x8b, 0xcc, 0xe0, 0x8d, 0x6a, 0xc9, 0xe2, 0x5f, 0xb7, 0x55, 0x38, 0x1e, 0xff, 0xa3, 0xd9, 0x52, 0x9e, 0x89, 0xad, 0xcb, 0x44, 0x6a, 0xee, 0x9b, 0xdc, + 0xb5, 0xa8, 0x94, 0xbe, 0xe6, 0xda, 0x87, 0x97, 0x7d, 0xfa, 0xd6, 0x6b, 0x3f, 0xde, 0x56, 0xbf, 0x54, 0xe6, 0x6e, 0xec, 0xd7, 0x51, 0x17, 0x0e, 0xaf, 0x24, 0x90, 0x38, 0x13, 0x69, 0x5d, 0x00, 0x57, 0xee, 0xef, 0x35, 0xc8, 0xa7, 0x07, 0x60, 0x90, 0x2a, 0x21, 0x6d, 0xeb, 0x70, 0x89, 0x61, 0x39, 0xa3, 0xd3, 0xf6, 0x1e, 0xcc, 0xee, 0xfe, 0x70, 0xcc, 0x10, 0x08, 0xcb, 0x89, 0xf2, 0x9a, + 0x1b, 0x09, 0xd2, 0x77, 0x6b, 0xaf, 0xca, 0xd2, 0x76, 0x9a, 0xf3, 0x09, 0x5d, 0xca, 0x39, 0xc8, 0x77, 0x9f, 0x3f, 0x7e, 0x88, 0x2d, 0xcb, 0xdc, 0xc1, 0xe9, 0x8b, 0x3c, 0x83, 0x8b, 0xed, 0x8c, 0xbe, 0x66, 0x4d, 0xfb, 0xc2, 0x72, 0x5c, 0x92, 0x65, 0x6f, 0x37, 0xc0, 0xd5, 0x07, 0x5a, 0x29, 0xe0, 0x20, 0x6d, 0xab, 0x8d, 0x03, 0x6b, 0x64, 0x1f, 0x4c, 0xdb, 0xe0, 0x34, 0xe0, 0x96, 0x12, + 0xb4, 0xde, 0x1b, 0xc8, 0x49, 0xcd, 0x94, 0xed, 0x44, 0xda, 0x01, 0xfc, 0xb4, 0x99, 0x15, 0xa8, 0xb7, 0x0c, 0xf4, 0xeb, 0xeb, 0xa7, 0xf7, 0x99, 0x6d, 0x75, 0xa1, 0x68, 0x39, 0xae, 0xa9, 0xbf, 0xa2, 0xa3, 0xaf, 0xdc, 0x0a, 0x78, 0x66, 0xf3, 0xde, 0xca, 0xfe, 0x64, 0x68, 0x6c, 0x59, 0x7b, 0xc7, 0xd9, 0x0f, 0x90, 0xe8, 0xed, 0xf0, 0x8f, 0xf0, 0x78, 0xd9, 0xa3, 0x99, 0x65, 0xaa, 0x57, + 0xd4, 0x16, 0xb9, 0x56, 0x47, 0x2a, 0x74, 0xdf, 0x0d, 0xe6, 0x2b, 0xd7, 0x75, 0x5f, 0x8e, 0x8e, 0xab, 0x8c, 0x39, 0x6c, 0xd1, 0xc9, 0x4a, 0x2d, 0x99, 0x73, 0xea, 0x74, 0x05, 0x17, 0x25, 0xf0, 0x58, 0xf0, 0x7f, 0x97, 0xc0, 0x07, 0xf2, 0x94, 0x89, 0x0a, 0x62, 0xc1, 0xef, 0xf5, 0x73, 0xd0, 0x53, 0x40, 0x55, 0x91, 0x95, 0xee, 0xfb, 0xd8, 0xbe, 0x9d, 0x76, 0xd4, 0xce, 0xa3, 0x71, 0xfe, + 0xb6, 0xbd, 0x98, 0xb2, 0xfe, 0x7c, 0x2b, 0x90, 0xbd, 0x1c, 0x1d, 0x4a, 0xd7, 0x1f, 0x25, 0x55, 0x60, 0x5b, 0xc7, 0x1e, 0xa4, 0x04, 0xb2, 0xae, 0xf5, 0x66, 0xfa, 0xc6, 0xcd, 0x5a, 0xbf, 0xdd, 0xba, 0x84, 0xec, 0x68, 0xfb, 0x0d, 0xad, 0xd2, 0x93, 0xf9, 0x0a, 0x94, 0x0e, 0x3d, 0x51, 0x2b, 0x7b, 0xe0, 0xac, 0x51, 0x00, 0x93, 0x81, 0xe5, 0x0e, 0x09, 0x6d, 0x7b, 0xb8, 0x64, 0x70, 0x33, + 0xa2, 0x48, 0x4f, 0xf7, 0xac, 0xdb, 0x69, 0x68, 0x6e, 0x9f, 0x05, 0xb9, 0xd3, 0x40, 0x6c, 0xfd, 0x64, 0x5d, 0xdb, 0xda, 0x75, 0x6f, 0x88, 0x02, 0xc7, 0x55, 0xe2, 0x83, 0xd0, 0x77, 0xd1, 0x5a, 0xe7, 0x41, 0x49, 0xca, 0x57, 0xb6, 0x73, 0x6d, 0xfd, 0x8c, 0xac, 0x6b, 0xd8, 0xf7, 0xc2, 0xf8, 0xb4, 0xcf, 0xeb, 0xd8, 0x5a, 0x94, 0x4b, 0xeb, 0x1a, 0xae, 0xad, 0xc5, 0xb8, 0x5c, 0x5a, 0x83, + 0xc4, 0xf2, 0xe2, 0x85, 0xdd, 0x1f, 0xd4, 0xde, 0x26, 0x7f, 0x16, 0x65, 0x7c, 0x29, 0x7d, 0x67, 0x8e, 0x85, 0x43, 0x90, 0x94, 0xfa, 0x92, 0xfe, 0x3d, 0x57, 0xf6, 0x57, 0x29, 0xdf, 0xfe, 0xb6, 0x39, 0x30, 0xde, 0x89, 0x68, 0xf5, 0x89, 0x7c, 0xb2, 0xb9, 0xf3, 0xe2, 0x85, 0xcd, 0x63, 0x1f, 0x26, 0xed, 0x4c, 0x6a, 0x60, 0x69, 0x10, 0xd0, 0xaf, 0x18, 0xb3, 0xad, 0xd2, 0x72, 0x22, 0x9a, + 0xdb, 0xca, 0x65, 0xc0, 0x57, 0x6a, 0xbd, 0xe4, 0x4e, 0x2e, 0xa4, 0xad, 0x47, 0x42, 0xec, 0x45, 0xb0, 0x38, 0x74, 0x60, 0x1e, 0xc1, 0xf5, 0xb5, 0xd3, 0x9f, 0x4c, 0x42, 0x21, 0x36, 0x70, 0xbf, 0xa6, 0x2c, 0xb3, 0xd5, 0x4f, 0xf0, 0xf3, 0x33, 0xd0, 0x77, 0xbf, 0x83, 0x9a, 0x67, 0x81, 0xd3, 0x41, 0x7a, 0x4c, 0x51, 0x8b, 0x71, 0x97, 0xa4, 0x17, 0x63, 0xf3, 0x3f, 0x16, 0xff, 0x03, 0xd8, + 0x33, 0xf7, 0x07, 0xc7, 0x18, 0x00, 0x00 +}; + +#endif // AsyncWebSerial_HTML_H \ No newline at end of file diff --git a/resources/bin2array/.gitignore b/resources/bin2array/.gitignore new file mode 100644 index 0000000..3d45606 --- /dev/null +++ b/resources/bin2array/.gitignore @@ -0,0 +1,163 @@ + +# Created by https://www.gitignore.io/api/macos,python,windows,visualstudiocode + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/macos,python,windows,visualstudiocode \ No newline at end of file diff --git a/resources/bin2array/LICENSE b/resources/bin2array/LICENSE new file mode 100644 index 0000000..4489d76 --- /dev/null +++ b/resources/bin2array/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 James Swineson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/resources/bin2array/README.md b/resources/bin2array/README.md new file mode 100644 index 0000000..0a989d5 --- /dev/null +++ b/resources/bin2array/README.md @@ -0,0 +1,95 @@ +# bin2array + +Converts binary file to C-style array initializer. + +Ever wanted to embed a binary file in your program? Trying to serve images and executables from a tiny web server on Arduino or ESP8266? This utility is here to help. + +## Requirements + + * Python 3 + +## Usage + +I guess it is self-explanatory. + +``` +usage: bin2array.py [-h] [-O OUTPUT] [-l LINEBREAK] [-L LINEBREAK_STRING] + [-S SEPARATOR_STRING] [-H ELEMENT_PREFIX] + [-T ELEMENT_SUFFIX] [-U] [-n] + filename + +Convert binary file to C-style array initializer. + +positional arguments: + filename the file to be converted + +optional arguments: + -h, --help show this help message and exit + -O OUTPUT, --output OUTPUT + write output to a file + -l LINEBREAK, --linebreak LINEBREAK + add linebreak after every N element + -L LINEBREAK_STRING, --linebreak-string LINEBREAK_STRING + use what to break link, defaults to "\n" + -S SEPARATOR_STRING, --separator-string SEPARATOR_STRING + use what to separate elements, defaults to ", " + -H ELEMENT_PREFIX, --element-prefix ELEMENT_PREFIX + string to be added to the head of element, defaults to + "0x" + -T ELEMENT_SUFFIX, --element-suffix ELEMENT_SUFFIX + string to be added to the tail of element, defaults to + none + -U, --force-uppercase + force uppercase HEX representation + -n, --newline add a newline on file end +``` + +## Caveats + +### Arduino IDE + +**Do not put large source code files in the root folder of your project.** Otherwise some of the following events will happen: + + * One of your CPU cores been eaten up by java + * The splash screen shows up but never loads + * 3rd World War + +Make a new folder inside project root, put the converted file (use `.h` as extension, otherwise may not be recognized) in, then use the following grammer to use it: + +```C++ +const char great_image[] PROGMEM = { +#include "data/great_image.png.h" +} +``` + +If you are using ESP8266WebServer to serve static binary files, you can use the following code: + +```C++ +#include + +// create server +ESP8266WebServer server(80); + +// include the image data +const char image[] PROGMEM = { +#include "data/image.png.h" +}; + +// statis binary file handler +void handleImage() { + server.sendHeader("Cache-Control", "max-age=31536000", false); + server.send_P(200, "image/png", image, sizeof(image)); +} + +void setup() { + // do other things... + // register image handler before server.begin() + server.on("/image.png", handleImage); + // do other things... + server.begin(); +} +``` + +### Windows + +Do not use command line redirection (`python bin2array.py test.png > test.png.h`) since CMD will save the file using UTF-16 which is not recognized by some compiler. Use `-O` option to save output to file, or manually convert UTF-16 to UTF-8 for maximum compatibility. \ No newline at end of file diff --git a/resources/bin2array/bin2array.py b/resources/bin2array/bin2array.py new file mode 100644 index 0000000..8bbac05 --- /dev/null +++ b/resources/bin2array/bin2array.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import binascii +import sys +import argparse + +parser = argparse.ArgumentParser(description='Convert binary file to C-style array initializer.') +parser.add_argument("filename", help="the file to be converted") +parser.add_argument("-O", "--output", help="write output to a file") +parser.add_argument("-l", "--linebreak", type=int, help="add linebreak after every N element") +parser.add_argument("-L", "--linebreak-string", default="\n", help="use what to break link, defaults to \"\\n\"") +parser.add_argument("-S", "--separator-string", default=", ", help="use what to separate elements, defaults to \", \"") +parser.add_argument("-H", "--element-prefix", default="0x", help="string to be added to the head of element, defaults to \"0x\"") +parser.add_argument("-T", "--element-suffix", default="", help="string to be added to the tail of element, defaults to none") +parser.add_argument("-U", "--force-uppercase", action='store_true', help="force uppercase HEX representation") +parser.add_argument("-n", "--newline", action='store_true', help="add a newline on file end") +args = parser.parse_args() + +def make_sublist_group(lst: list, grp: int) -> list: + """ + Group list elements into sublists. + + make_sublist_group([1, 2, 3, 4, 5, 6, 7], 3) = [[1, 2, 3], [4, 5, 6], 7] + """ + return [lst[i:i+grp] for i in range(0, len(lst), grp)] + +def do_convension(content: bytes, to_uppercase: bool=False) -> str: + hexstr = binascii.hexlify(content).decode("UTF-8") + if to_uppercase: + hexstr = hexstr.upper() + array = [args.element_prefix + hexstr[i:i + 2] + args.element_suffix for i in range(0, len(hexstr), 2)] + if args.linebreak: + array = make_sublist_group(array, args.linebreak) + else: + array = [array,] + + return args.linebreak_string.join([args.separator_string.join(e) + args.separator_string for e in array]) + +if __name__ == "__main__": + with open(args.filename, 'rb') as f: + file_content = f.read() + ret = do_convension(file_content, to_uppercase=args.force_uppercase) + if args.newline: + ret = ret + args.linebreak_string + if args.output: + with open(args.output, 'w') as f: + f.write(ret) + else: + print(ret) \ No newline at end of file diff --git a/resources/how-to-flash.txt b/resources/how-to-flash.txt index a874353..6b2e6aa 100644 --- a/resources/how-to-flash.txt +++ b/resources/how-to-flash.txt @@ -29,12 +29,19 @@ e000 boot_app0.bin 10000 nuki_hub_esp32.bin 270000 nuki_hub_updater_esp32.bin +ESP32-NOPSRAM +e000 boot_app0.bin +1000 nuki_hub_bootloader_esp32nopsram.bin +8000 nuki_hub_partitions_esp32nopsram.bin +10000 nuki_hub_esp32nopsram.bin +270000 nuki_hub_updater_esp32nopsram.bin + ESP32-GL-S10 e000 boot_app0.bin -1000 nuki_hub_bootloader_esp32.bin -8000 nuki_hub_partitions_esp32.bin -10000 nuki_hub_esp32.bin -270000 nuki_hub_updater_esp32.bin +1000 nuki_hub_bootloader_esp32gls10.bin +8000 nuki_hub_partitions_esp32gls10.bin +10000 nuki_hub_esp32gls10.bin +270000 nuki_hub_updater_esp32gls10.bin ESP32-S3 e000 boot_app0.bin @@ -43,6 +50,13 @@ e000 boot_app0.bin 10000 nuki_hub_esp32s3.bin 270000 nuki_hub_updater_esp32s3.bin +ESP32-S3-NOPSRAM +e000 boot_app0.bin +0 nuki_hub_bootloader_esp32s3nopsram.bin +8000 nuki_hub_partitions_esp32s3nopsram.bin +10000 nuki_hub_esp32s3nopsram.bin +270000 nuki_hub_updater_esp32s3nopsram.bin + ESP32-S3-OCT e000 boot_app0.bin 0 nuki_hub_bootloader_esp32s3oct.bin @@ -57,6 +71,13 @@ e000 boot_app0.bin 10000 nuki_hub_esp32c3.bin 270000 nuki_hub_updater_esp32c3.bin +ESP32-C5 +e000 boot_app0.bin +2000 nuki_hub_bootloader_esp32c5.bin +8000 nuki_hub_partitions_esp32c5.bin +10000 nuki_hub_esp32c5.bin +260000 nuki_hub_updater_esp32c5.bin + ESP32-C6 e000 boot_app0.bin 0 nuki_hub_bootloader_esp32c6.bin @@ -80,10 +101,10 @@ e000 boot_app0.bin ESP32-SOLO1 e000 boot_app0.bin -1000 nuki_hub_bootloader_esp32-solo1.bin -8000 nuki_hub_partitions_esp32-solo1.bin -10000 nuki_hub_esp32-solo1.bin -270000 nuki_hub_updater_esp32-solo1.bin +1000 nuki_hub_bootloader_esp32solo1.bin +8000 nuki_hub_partitions_esp32solo1.bin +10000 nuki_hub_esp32solo1.bin +270000 nuki_hub_updater_esp32solo1.bin Make sure the checkmarks for all files are enabled. @@ -99,38 +120,50 @@ As an alternative to the Download Tools, you can also use the esptool from the E ## ESP32 -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x1000 nuki_hub_bootloader_esp32.bin 0x10000 nuki_hub_esp32.bin 0x270000 nuki_hub_updater_esp32.bin 0x8000 nuki_hub_partitions_esp32.bin +esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x1000 nuki_hub_bootloader_esp32.bin 0x10000 nuki_hub_esp32.bin 0x270000 nuki_hub_updater_esp32.bin 0x8000 nuki_hub_partitions_esp32.bin + +## ESP32-NOPSRAM + +esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x1000 nuki_hub_bootloader_esp32nopsram.bin 0x10000 nuki_hub_esp32nopsram.bin 0x270000 nuki_hub_updater_esp32nopsram.bin 0x8000 nuki_hub_partitions_esp32nopsram.bin ## ESP32-GL-S10 -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x1000 nuki_hub_bootloader_esp32.bin 0x10000 nuki_hub_esp32.bin 0x270000 nuki_hub_updater_esp32.bin 0x8000 nuki_hub_partitions_esp32.bin +esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x1000 nuki_hub_bootloader_esp32gls10.bin 0x10000 nuki_hub_esp32gls10.bin 0x270000 nuki_hub_updater_esp32gls10.bin 0x8000 nuki_hub_partitions_esp32gls10.bin ## ESP32-S3 -esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32s3.bin 0x10000 nuki_hub_esp32s3.bin 0x270000 nuki_hub_updater_esp32s3.bin 0x8000 nuki_hub_partitions_esp32s3.bin +esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32s3.bin 0x10000 nuki_hub_esp32s3.bin 0x270000 nuki_hub_updater_esp32s3.bin 0x8000 nuki_hub_partitions_esp32s3.bin + +## ESP32-S3-NOPSRAM + +esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32s3nopsram.bin 0x10000 nuki_hub_esp32s3nopsram.bin 0x270000 nuki_hub_updater_esp32s3nopsram.bin 0x8000 nuki_hub_partitions_esp32s3nopsram.bin ## ESP32-S3-OCT -esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32s3oct.bin 0x10000 nuki_hub_esp32s3oct.bin 0x270000 nuki_hub_updater_esp32s3oct.bin 0x8000 nuki_hub_partitions_esp32s3oct.bin +esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32s3oct.bin 0x10000 nuki_hub_esp32s3oct.bin 0x270000 nuki_hub_updater_esp32s3oct.bin 0x8000 nuki_hub_partitions_esp32s3oct.bin ## ESP32-C3 -esptool.py --chip esp32c3 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32c3.bin 0x10000 nuki_hub_esp32c3.bin 0x270000 nuki_hub_updater_esp32c3.bin 0x8000 nuki_hub_partitions_esp32c3.bin +esptool.py --chip esp32c3 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32c3.bin 0x10000 nuki_hub_esp32c3.bin 0x270000 nuki_hub_updater_esp32c3.bin 0x8000 nuki_hub_partitions_esp32c3.bin + +## ESP32-C5 + +esptool.py --chip esp32c5 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x2000 nuki_hub_bootloader_esp32c5.bin 0x10000 nuki_hub_esp32c5.bin 0x260000 nuki_hub_updater_esp32c5.bin 0x8000 nuki_hub_partitions_esp32c5.bin ## ESP32-C6 -esptool.py --chip esp32c6 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32c6.bin 0x10000 nuki_hub_esp32c6.bin 0x270000 nuki_hub_updater_esp32c6.bin 0x8000 nuki_hub_partitions_esp32c6.bin +esptool.py --chip esp32c6 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32c6.bin 0x10000 nuki_hub_esp32c6.bin 0x270000 nuki_hub_updater_esp32c6.bin 0x8000 nuki_hub_partitions_esp32c6.bin ## ESP32-H2 -esptool.py --chip esp32h2 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32h2.bin 0x10000 nuki_hub_esp32h2.bin 0x270000 nuki_hub_updater_esp32h2.bin 0x8000 nuki_hub_partitions_esp32h2.bin +esptool.py --chip esp32h2 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x0 nuki_hub_bootloader_esp32h2.bin 0x10000 nuki_hub_esp32h2.bin 0x270000 nuki_hub_updater_esp32h2.bin 0x8000 nuki_hub_partitions_esp32h2.bin ## ESP32-P4 -esptool.py --chip esp32p4 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x2000 nuki_hub_bootloader_esp32p4.bin 0x10000 nuki_hub_esp32p4.bin 0x270000 nuki_hub_updater_esp32p4.bin 0x8000 nuki_hub_partitions_esp32p4.bin +esptool.py --chip esp32p4 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x2000 nuki_hub_bootloader_esp32p4.bin 0x10000 nuki_hub_esp32p4.bin 0x270000 nuki_hub_updater_esp32p4.bin 0x8000 nuki_hub_partitions_esp32p4.bin ## ESP32-SOLO1 -esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq keep --flash_size detect 0xe000 boot_app0.bin 0x1000 nuki_hub_bootloader_esp32-solo1.bin 0x10000 nuki_hub_esp32-solo1.bin 0x270000 nuki_hub_updater_esp32-solo1.bin 0x8000 nuki_hub_partitions_esp32-solo1.bin +esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default-reset --after hard-reset write-flash -z --flash-mode dio --flash-freq keep --flash-size detect 0xe000 boot_app0.bin 0x1000 nuki_hub_bootloader_esp32solo1.bin 0x10000 nuki_hub_esp32solo1.bin 0x270000 nuki_hub_updater_esp32solo1.bin 0x8000 nuki_hub_partitions_esp32solo1.bin Adjust the serial device and path to the binaries if necessary. \ No newline at end of file diff --git a/resources/old_manifest.py b/resources/old_manifest.py new file mode 100644 index 0000000..20c73bd --- /dev/null +++ b/resources/old_manifest.py @@ -0,0 +1,20 @@ +import re, json, argparse + +parser = argparse.ArgumentParser() + +parser.add_argument('version', type=str) +args = parser.parse_args() + +with open('ota/old/manifest.json', 'r+') as json_file: + data = json.load(json_file) + data[str(int((float(args.version)*100)+0.1))] = args.version + data2 = sorted(data.items(), reverse=True) + sorted_dict = {} + k = 6 + for key, value in data2: + if k > 0: + sorted_dict[key] = value + k = k - 1 + json_file.seek(0) + json.dump(sorted_dict, json_file, indent=4) + json_file.truncate() \ No newline at end of file diff --git a/resources/style.css b/resources/style.css index 0c296d2..dd419d5 100644 --- a/resources/style.css +++ b/resources/style.css @@ -11,32 +11,32 @@ source: https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css */ :root { - --nc-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; - --nc-font-mono: Consolas, monaco, 'Ubuntu Mono', 'Liberation Mono', 'Courier New', Courier, monospace; - --nc-tx-1: #000000; - --nc-tx-2: #1A1A1A; - --nc-bg-1: #FFFFFF; - --nc-bg-2: #F6F8FA; - --nc-bg-3: #E5E7EB; - --nc-lk-1: #0070F3; - --nc-lk-2: #0366D6; - --nc-lk-tx: #FFFFFF; - --nc-ac-1: #79FFE1; - --nc-ac-tx: #0C4047 + --nc-font-sans: 'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol'; + --nc-font-mono: Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace; + --nc-tx-1: #000; + --nc-tx-2: #1a1a1a; + --nc-bg-1: #fff; + --nc-bg-2: #f6f8fa; + --nc-bg-3: #e5e7eb; + --nc-lk-1: #0070f3; + --nc-lk-2: #0366d6; + --nc-lk-tx: #fff; + --nc-ac-1: #79ffe1; + --nc-ac-tx: #0c4047 } -@media (prefers-color-scheme:dark) { +@media(prefers-color-scheme: dark) { :root { - --nc-tx-1: #ffffff; - --nc-tx-2: #eeeeee; - --nc-bg-1: #000000; - --nc-bg-2: #111111; - --nc-bg-3: #222222; - --nc-lk-1: #3291FF; - --nc-lk-2: #0070F3; - --nc-lk-tx: #FFFFFF; - --nc-ac-1: #7928CA; - --nc-ac-tx: #FFFFFF + --nc-tx-1:#fff; + --nc-tx-2: #eee; + --nc-bg-1: #000; + --nc-bg-2: #111; + --nc-bg-3: #222; + --nc-lk-1: #3291ff; + --nc-lk-2: #0070f3; + --nc-lk-tx: #fff; + --nc-ac-1: #7928ca; + --nc-ac-tx: #fff } } @@ -45,20 +45,11 @@ source: https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css padding: 0 } -img, -input, -option, -p, -table, -textarea, -ul { +img,input,option,p,table,textarea,ul { margin-bottom: 1rem } -button, -html, -input, -select { +button,html,input,select { font-family: var(--nc-font-sans) } @@ -81,39 +72,64 @@ body { color: var(--nc-ac-tx) } -h1, h2, h3, h4, h5, h6 { +h1,h2,h3,h4,h5,h6 { line-height: 1; color: var(--nc-tx-1); padding-top: .875rem } -h1, h2, h3 { +h1,h2,h3 { color: var(--nc-tx-1); padding-bottom: 2px; margin-bottom: 8px; border-bottom: 1px solid var(--nc-bg-2) } -h4, h5, h6 { +h4,h5,h6 { margin-bottom: .3rem } -h1 { font-size: 2.25rem } -h2 { font-size: 1.85rem } -h3 { font-size: 1.55rem } -h4 { font-size: 1.25rem } -h5 { font-size: 1rem } -h6 { font-size: .875rem } -a { color: var(--nc-lk-1) } -a:hover { color: var(--nc-lk-2) !important; } -abbr { cursor: help } -abbr:hover { cursor: help } +h1 { + font-size: 2.25rem +} -a button, -button, -input[type=button], -input[type=reset], -input[type=submit] { +h2 { + font-size: 1.85rem +} + +h3 { + font-size: 1.55rem +} + +h4 { + font-size: 1.25rem +} + +h5 { + font-size: 1rem +} + +h6 { + font-size: .875rem +} + +a { + color: var(--nc-lk-1) +} + +a:hover { + color: var(--nc-lk-2) !important; +} + +abbr { + cursor: help +} + +abbr:hover { + cursor: help +} + +a button,button,input[type=button],input[type=reset],input[type=submit] { font-size: 1rem; display: inline-block; padding: 6px 12px; @@ -129,26 +145,13 @@ input[type=submit] { color: var(--nc-lk-tx) } -a button[disabled], -button[disabled], -input[type=button][disabled], -input[type=reset][disabled], -input[type=submit][disabled] { +a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled] { cursor: default; opacity: .5; cursor: not-allowed } -.button:focus, -.button:hover, -button:focus, -button:hover, -input[type=button]:focus, -input[type=button]:hover, -input[type=reset]:focus, -input[type=reset]:hover, -input[type=submit]:focus, -input[type=submit]:hover { +.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover { background: var(--nc-lk-2) } @@ -157,18 +160,25 @@ table { width: 100% } -td, th { +td,th { border: 1px solid var(--nc-bg-3); text-align: left; padding: .5rem } -th { background: var(--nc-bg-2) } -tr:nth-child(even) { background: var(--nc-bg-2) } +th { + background: var(--nc-bg-2) +} -textarea { max-width: 100% } +tr:nth-child(even) { + background: var(--nc-bg-2) +} -input, select, textarea { +textarea { + max-width: 100% +} + +input,select,textarea { padding: 6px 12px; margin-bottom: .5rem; background: var(--nc-bg-2); @@ -179,50 +189,57 @@ input, select, textarea { box-sizing: border-box } -img { max-width: 100% } +img { + max-width: 100% +} td>input { - margin-top: 0px; - margin-bottom: 0px + margin-top: 0; + margin-bottom: 0 } td>textarea { - margin-top: 0px; - margin-bottom: 0px + margin-top: 0; + margin-bottom: 0 } td>select { - margin-top: 0px; - margin-bottom: 0px + margin-top: 0; + margin-bottom: 0 } .warning { - color: #f00; + color: red } @media only screen and (max-width: 600px) { - .adapt td { display: block; } + .adapt td { + display:block + } - .adapt input[type=text], - .adapt input[type=password], - .adapt input[type=submit], - .adapt textarea, - .adapt select { width: 100%; } + .adapt input[type=text],.adapt input[type=password],.adapt input[type=submit],.adapt textarea,.adapt select { + width: 100% + } .adapt td:has(input[type=checkbox]) { - text-align: center; + text-align: center } .adapt input[type=checkbox] { width: 1.5em; - height: 1.5em; + height: 1.5em } - .adapt table td:first-child { border-bottom: 0; } - .adapt table td:last-child { border-top: 0; } + .adapt table td:first-child { + border-bottom: 0 + } + + .adapt table td:last-child { + border-top: 0 + } #tblnav a li>span { - max-width: 140px; + max-width: 140px } } @@ -236,32 +253,31 @@ td>select { line-height: 1; color: var(--nc-tx-1); text-decoration: none; - - background: linear-gradient(to left, transparent 50%, rgba(255,255,255,0.4) 50%) right; + background: linear-gradient(to left,transparent 50%,rgba(255,255,255,0.4) 50%) right; background-size: 200% 100%; - transition:all .2s ease; + transition: all .2s ease } -#tblnav a:nth-child(even) { - background: linear-gradient(to left, var(--nc-bg-2) 50%, rgba(255,255,255,0.4) 50%) right; - background-size: 200% 100%; +#tblnav a { + background: linear-gradient(to left,var(--nc-bg-2) 50%,rgba(255,255,255,0.4) 50%) right; + background-size: 200% 100% } #tblnav a:hover { background-position: left; - transition:all .45s ease; + transition: all .45s ease } #tblnav a:active { background: var(--nc-lk-1); - transition:all .15s ease; + transition: all .15s ease } #tblnav a li { list-style: none; padding: .5rem; display: inline-block; - width: 100%; + width: 100% } #tblnav a li>span { @@ -271,10 +287,16 @@ td>select { color: #f70; font-weight: 100; font-style: italic; - display: block; + display: block } .tdbtn { text-align: center; - vertical-align: middle; + vertical-align: middle } + +.naventry { + float: left; + max-width: 375px; + width: 100% +} \ No newline at end of file diff --git a/scripts/sendcfg/README.md b/scripts/sendcfg/README.md index e80961c..399ac54 100644 --- a/scripts/sendcfg/README.md +++ b/scripts/sendcfg/README.md @@ -16,7 +16,7 @@ Prior to running the script, pyserial has to be installed via pip. The ESP will only accept a configuration via serial as long as the device in not configured yet, meaning the ESP Wifi access point is open. Once the ESP is connected to a network, it will ignore any commands send via the serial interface.

To generate a configuration a configuration file, the export configuration function can be used. -After exporting a configuration, it can be edited to the required values. Usually only specific valus like +After exporting a configuration, it can be edited to the required values. Usually only specific values like the IP address, Wifi credentials or network hardware are intended to be set. In that case all other configuration values can be deleted from the configuration.
For example configurations, see the "example_configurations" subdirectory. @@ -35,7 +35,7 @@ All configuration entries are saved in JSON format. - ipgtw: Gateway used to connect to the internet - dnssrv: DNS server used to resolve domain names - nwhw: Network hardware used. See WebCfgServer.cpp, method getNetworkDetectionOptions for possible values. -At the time of writing: 1=Wifi, 2=Generic W5500, 3 = M5Stack Atom POE (W5500), 4 = "Olimex ESP32-POE / ESP-POE-ISO, 5 = WT32-ETH01, 6 = M5STACK PoESP32 Unit, 7 = LilyGO T-ETH-POE, 8 = GL-S10, 9 = ETH01-Evo, 10 = M5Stack Atom POE S3 (W5500), 11 = Custom LAN module, 12 = LilyGO T-ETH ELite, 13 = Waveshare ESP32-S3-ETH / ESP32-S3-ETH-POE, 14 = LilyGO T-ETH-Lite-ESP32S3 +At the time of writing: 1=Wifi, 2=Generic W5500, 3 = M5Stack Atom POE (W5500), 4 = "Olimex ESP32-POE / ESP-POE-ISO, 5 = WT32-ETH01, 6 = M5STACK PoESP32 Unit, 7 = LilyGO T-ETH-POE, 8 = GL-S10, 9 = ETH01-Evo, 10 = M5Stack Atom POE S3 (W5500), 11 = Custom LAN module, 12 = LilyGO T-ETH ELite, 13 = Waveshare ESP32-S3-ETH / ESP32-S3-ETH-POE, 14 = LilyGO T-ETH-Lite-ESP32S3, 15 = Waveshare ESP32-P4-NANO, 16 = Waveshare ESP32-P4-Module-DEV-KIT, 17 = ESP32-P4-Function-EV-Board ## Serial commands diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 765cffb..2f5cbff 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -39,7 +39,9 @@ CONFIG_ARDUINO_SELECTIVE_Insights=n CONFIG_ARDUINO_LOOP_STACK_SIZE=12288 # LOGS -CONFIG_HEAP_TASK_TRACKING=n +#CONFIG_HEAP_TASK_TRACKING=y +#CONFIG_HEAP_TRACK_DELETED_TASKS=y +#CONFIG_ESP_SYSTEM_USE_FRAME_POINTER=y CONFIG_LOG_COLORS=n CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=n CONFIG_LOG_MAXIMUM_LEVEL=4 @@ -84,11 +86,12 @@ CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=12288 CONFIG_BT_NIMBLE_ROLE_CENTRAL=y -CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=n +CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y CONFIG_BT_NIMBLE_ROLE_OBSERVER=y -CONFIG_BT_NIMBLE_SM_LEGACY=y -CONFIG_BT_NIMBLE_SM_SC=y +CONFIG_BT_NIMBLE_SECURITY_ENABLE=n +CONFIG_BT_NIMBLE_SM_LEGACY=n +CONFIG_BT_NIMBLE_SM_SC=n CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=256 @@ -127,6 +130,7 @@ CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM=100 CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 CONFIG_BT_NIMBLE_MSYS_BUF_FROM_HEAP=n CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n +CONFIG_NIMBLE_CPP_LOG_LEVEL=0 # LWIP CONFIG_LWIP_MAX_SOCKETS=24 @@ -161,7 +165,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y # HTTP(S) SERVER CONFIG_ESP_HTTPS_SERVER_ENABLE=y CONFIG_HTTPD_MAX_REQ_HDR_LEN=2048 -CONFIG_HTTPD_MAX_URI_LEN=512 +CONFIG_HTTPD_MAX_URI_LEN=2048 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 CONFIG_HTTPD_WS_SUPPORT=y diff --git a/sdkconfig.defaults.esp32-c5 b/sdkconfig.defaults.esp32-c5 new file mode 100644 index 0000000..bf51711 --- /dev/null +++ b/sdkconfig.defaults.esp32-c5 @@ -0,0 +1,6 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_IGNORE_NOTFOUND=y +CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC=y +CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT=y +CONFIG_ESPTOOLPY_NO_STUB=n +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=50768 \ No newline at end of file diff --git a/sdkconfig.defaults.esp32-c5dbg b/sdkconfig.defaults.esp32-c5dbg new file mode 100644 index 0000000..43229b0 --- /dev/null +++ b/sdkconfig.defaults.esp32-c5dbg @@ -0,0 +1,4 @@ +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG=y +CONFIG_BT_NIMBLE_LOG_LEVEL=4 +CONFIG_NIMBLE_CPP_LOG_LEVEL=4 \ No newline at end of file diff --git a/sdkconfig.defaults.esp32-nopsram b/sdkconfig.defaults.esp32-nopsram new file mode 100644 index 0000000..92f4d3b --- /dev/null +++ b/sdkconfig.defaults.esp32-nopsram @@ -0,0 +1 @@ +CONFIG_SPIRAM=n \ No newline at end of file diff --git a/sdkconfig.defaults.esp32-p4 b/sdkconfig.defaults.esp32-p4 index c6dcc6b..3f14f4b 100644 --- a/sdkconfig.defaults.esp32-p4 +++ b/sdkconfig.defaults.esp32-p4 @@ -18,6 +18,7 @@ CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y CONFIG_ESP_WIFI_TX_BA_WIN=32 CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP_WIFI_RX_BA_WIN=32 + CONFIG_ESP_WIFI_REMOTE_ENABLED=y CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 @@ -27,11 +28,12 @@ CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16 CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0 -CONFIG_ESP_HCI_VHCI=y -CONFIG_BT_CONTROLLER_DISABLED=y +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_TRANSPORT_UART=n -CONFIG_ESP_ENABLE_BT=y -CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_CONTROLLER_DISABLED=y +CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y +CONFIG_ESP_HOSTED_NIMBLE_HCI_VHCI=y CONFIG_SLAVE_IDF_TARGET_ESP32C6=y \ No newline at end of file diff --git a/sdkconfig.defaults.esp32-s3-nopsram b/sdkconfig.defaults.esp32-s3-nopsram new file mode 100644 index 0000000..92f4d3b --- /dev/null +++ b/sdkconfig.defaults.esp32-s3-nopsram @@ -0,0 +1 @@ +CONFIG_SPIRAM=n \ No newline at end of file diff --git a/sdkconfig.gls10.defaults b/sdkconfig.defaults.gls10 similarity index 100% rename from sdkconfig.gls10.defaults rename to sdkconfig.defaults.gls10 diff --git a/sdkconfig.ramoptimize.defaults b/sdkconfig.ramoptimize.defaults new file mode 100644 index 0000000..4fe0e7e --- /dev/null +++ b/sdkconfig.ramoptimize.defaults @@ -0,0 +1,34 @@ +CONFIG_COMPILER_OPTIMIZATION_NONE=n +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_LWIP_IRAM_OPTIMIZATION=n +CONFIG_LWIP_EXTRA_IRAM_OPTIMIZATION=n +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=n +CONFIG_SPI_MASTER_IN_IRAM=n +CONFIG_SPI_SLAVE_IN_IRAM=n +CONFIG_ANA_CMPR_ISR_HANDLER_IN_IRAM=n +CONFIG_PARLIO_TX_ISR_HANDLER_IN_IRAM=n +CONFIG_PARLIO_RX_ISR_HANDLER_IN_IRAM=n +CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=n +CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=n +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=n +CONFIG_GDMA_ISR_HANDLER_IN_IRAM=n +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=n +CONFIG_HAL_ASSERTION_SILENT=y +#CONFIG_SPI_FLASH_AUTO_SUSPEND=y ## CAUSES CRASHES ON SOME DEVICES +#CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=n ## CAUSES CRASHES ON SOME DEVICES +#CONFIG_SPI_FLASH_AUTO_CHECK_SUSPEND_STATUS=y ## CAUSES CRASHES ON SOME DEVICES +CONFIG_LIBC_MISC_IN_IRAM=n +CONFIG_ESP_TIMER_IN_IRAM=n +CONFIG_ESP_INTR_IN_IRAM=n +CONFIG_LOG_IN_IRAM=n +CONFIG_ESP_ROM_PRINT_IN_IRAM=n +CONFIG_PM_SLEEP_FUNC_IN_IRAM=n +CONFIG_PM_RTOS_IDLE_OPT=n +CONFIG_ESP_SLEEP_POWER_DOWN_FLASH=n +CONFIG_PM_SLP_IRAM_OPT=n +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=n +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=n +CONFIG_ESP_PHY_IRAM_OPT=n +CONFIG_ESP_WIFI_SLP_IRAM_OPT=n +CONFIG_ESP_WIFI_EXTRA_IRAM_OPT=n +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=n \ No newline at end of file diff --git a/src/Config.h b/src/Config.h index 9c5033f..640f55b 100644 --- a/src/Config.h +++ b/src/Config.h @@ -5,7 +5,7 @@ #define NUKI_HUB_VERSION "9.12" #define NUKI_HUB_VERSION_INT (uint32_t)912 #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2025-06-09" +#define NUKI_HUB_DATE "2025-07-21" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" @@ -56,9 +56,11 @@ #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32s3oct.bin" #define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3nopsram.bin" #define NUKI_HUB_HW (char*)"ESP32-S3 (Octal PSRAM)" #define BOOT_BUTTON_GPIO (gpio_num_t)0 -#else +#elif defined(CONFIG_SPIRAM) #define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3.bin" #define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32s3.bin" @@ -73,9 +75,45 @@ #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32s3.bin" #define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3oct.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3oct.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3nopsram.bin" #define NUKI_HUB_HW (char*)"ESP32-S3" #define BOOT_BUTTON_GPIO (gpio_num_t)0 +#else +#define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3nopsram.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_BETA_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_updater_esp32s3nopsram.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_MASTER_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_updater_esp32s3nopsram.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_updater_esp32s3nopsram.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32s3nopsram.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32s3nopsram.bin" +#define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32s3nopsram.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32s3oct.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32s3oct.bin" +#define NUKI_HUB_HW (char*)"ESP32-S3 (No PSRAM)" +#define BOOT_BUTTON_GPIO (gpio_num_t)0 #endif +#elif defined(CONFIG_IDF_TARGET_ESP32C5) +#define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32c5.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32c5.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32c5.bin" +#define GITHUB_BETA_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_updater_esp32c5.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_esp32c5.bin" +#define GITHUB_MASTER_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_updater_esp32c5.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_esp32c5.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_updater_esp32c5.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_esp32c5.bin" +#define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32c5.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32c5.bin" +#define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32c5.bin" +#define NUKI_HUB_HW (char*)"ESP32-C5" +#define BOOT_BUTTON_GPIO (gpio_num_t)28 #elif defined(CONFIG_IDF_TARGET_ESP32C6) #define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32c6.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32c6.bin" @@ -108,7 +146,7 @@ #define BOOT_BUTTON_GPIO (gpio_num_t)9 #else #if defined(CONFIG_FREERTOS_UNICORE) -#define GITHUB_LATEST_RELEASE_BINARY_URL "https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32-solo1.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32-solo1.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32-solo1.bin" #define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32-solo1.bin" #define GITHUB_BETA_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_updater_esp32-solo1.bin" @@ -123,24 +161,26 @@ #define NUKI_HUB_HW (char*)"ESP32-SOLO1" #define BOOT_BUTTON_GPIO (gpio_num_t)0 #elif defined(NUKI_TARGET_GL_S10) -#define GITHUB_LATEST_RELEASE_BINARY_URL "https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32-gl-s10.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32gls10.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32gls10.bin" -#define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32-gl-s10.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32gls10.bin" #define GITHUB_BETA_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_updater_esp32gls10.bin" -#define GITHUB_MASTER_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_esp32-gl-s10.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_esp32gls10.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_updater_esp32gls10.bin" -#define GITHUB_LATEST_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_esp32-gl-s10.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_esp32gls10.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_updater_esp32gls10.bin" -#define GITHUB_BETA_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_esp32-gl-s10.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_esp32gls10.bin" #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32gls10.bin" -#define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32-gl-s10.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32gls10.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32gls10.bin" #define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32nopsram.bin" #define NUKI_HUB_HW (char*)"ESP32-GL-S10" #define BOOT_BUTTON_GPIO (gpio_num_t)0 -#else -#define GITHUB_LATEST_RELEASE_BINARY_URL "https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32.bin" +#elif defined(CONFIG_SPIRAM) +#define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32.bin" #define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32.bin" #define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32.bin" #define GITHUB_BETA_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_updater_esp32.bin" @@ -152,10 +192,31 @@ #define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32.bin" #define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32.bin" #define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32.bin" -#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32-gl-s10.bin" -#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32gls10.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32gls10.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32nopsram.bin" #define NUKI_HUB_HW (char*)"ESP32" #define BOOT_BUTTON_GPIO (gpio_num_t)0 +#else +#define GITHUB_LATEST_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32nopsram.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_esp32nopsram.bin" +#define GITHUB_BETA_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/beta/nuki_hub_updater_esp32nopsram.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_esp32nopsram.bin" +#define GITHUB_MASTER_UPDATER_BINARY_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/master/nuki_hub_updater_esp32nopsram.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_esp32nopsram.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/nuki_hub_updater_esp32nopsram.bin" +#define GITHUB_BETA_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_esp32nopsram.bin" +#define GITHUB_BETA_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/beta/nuki_hub_updater_esp32nopsram.bin" +#define GITHUB_MASTER_RELEASE_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_esp32nopsram.bin" +#define GITHUB_MASTER_UPDATER_BINARY_URL_DBG (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/debug/master/nuki_hub_updater_esp32nopsram.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32gls10.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32gls10.bin" +#define GITHUB_LATEST_RELEASE_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_esp32.bin" +#define GITHUB_LATEST_UPDATER_BINARY_URL_OTHER2 (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/nuki_hub_updater_esp32.bin" +#define NUKI_HUB_HW (char*)"ESP32 (No PSRAM)" +#define BOOT_BUTTON_GPIO (gpio_num_t)0 #endif #endif diff --git a/src/Gpio.cpp b/src/Gpio.cpp index fdf2447..e6b8420 100644 --- a/src/Gpio.cpp +++ b/src/Gpio.cpp @@ -15,7 +15,6 @@ Gpio::Gpio(Preferences* preferences) : _preferences(preferences) { _inst = this; - loadPinConfiguration(); _inst->init(); } @@ -120,6 +119,13 @@ void Gpio::init() _inst->_triggerState.push_back(0); } + _inst->setPins(); +} + +void Gpio::setPins() +{ + loadPinConfiguration(); + bool hasInputPin = false; if (_inst->_preferences->getBool(preference_cred_bypass_boot_btn_enabled, false)) @@ -180,8 +186,9 @@ void Gpio::init() } } - if(hasInputPin) + if(hasInputPin && _first) { + _first = false; _inst->timer = timerBegin(1000000); timerAttachInterrupt(_inst->timer, isrOnTimer); timerAlarm(_inst->timer, 100000, true, 0); diff --git a/src/Gpio.h b/src/Gpio.h index 9d48dc7..52f7686 100644 --- a/src/Gpio.h +++ b/src/Gpio.h @@ -76,6 +76,7 @@ public: const std::vector& getAllRoles() const; void setPinOutput(const uint8_t& pin, const uint8_t& state); + void setPins(); private: void IRAM_ATTR notify(const GpioAction& action, const int& pin); @@ -85,16 +86,22 @@ private: static void IRAM_ATTR isrOnTimer(); #if defined(CONFIG_IDF_TARGET_ESP32C3) - //Based on https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf + //Based on https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf const std::vector _availablePins = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 18, 19, 20, 21 }; #elif defined(CONFIG_IDF_TARGET_ESP32S3) - //Based on https://github.com/atomic14/esp32-s3-pinouts?tab=readme-ov-file and https://docs.espressif.com/projects/esp-idf/en/v5.3/esp32s3/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf + //Based on https://github.com/atomic14/esp32-s3-pinouts?tab=readme-ov-file and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf const std::vector _availablePins = { 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 38, 39, 40, 41, 42 }; + #elif defined(CONFIG_IDF_TARGET_ESP32C5) + //Based on https://docs.espressif.com/projects/esp-idf/en/latest/esp32c5/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-c5_datasheet_en.pdf + const std::vector _availablePins = { 0, 1, 3, 4, 5, 6, 8, 9, 10, 11, 12, 15, 23, 24, 26 }; #elif defined(CONFIG_IDF_TARGET_ESP32C6) - //Based on https://docs.espressif.com/projects/esp-idf/en/v5.3/esp32c6/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf + //Based on https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf const std::vector _availablePins = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + #elif defined(CONFIG_IDF_TARGET_ESP32P4) + //Based on https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/gpio.html + const std::vector _availablePins = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 }; #elif defined(CONFIG_IDF_TARGET_ESP32H2) - //Based on https://docs.espressif.com/projects/esp-idf/en/v5.3/esp32h2/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf + //Based on https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/gpio.html and https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf const std::vector _availablePins = { 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14, 22, 23, 24, 25, 26, 27 }; #else //Based on https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ @@ -133,6 +140,8 @@ private: std::vector _triggerState; hw_timer_t* timer = nullptr; + + bool _first = true; Preferences* _preferences = nullptr; }; diff --git a/src/HomeAssistantDiscovery.cpp b/src/HomeAssistantDiscovery.cpp index 972cdf3..0e4a5fd 100644 --- a/src/HomeAssistantDiscovery.cpp +++ b/src/HomeAssistantDiscovery.cpp @@ -11,11 +11,7 @@ HomeAssistantDiscovery::HomeAssistantDiscovery(NetworkDevice* device, Preference _buffer(buffer), _bufferSize(bufferSize) { - _discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery, ""); _baseTopic = _preferences->getString(preference_mqtt_lock_path); - _offEnabled = _preferences->getBool(preference_official_hybrid_enabled, false); - _checkUpdates = _preferences->getBool(preference_check_updates, false); - _updateFromMQTT = _preferences->getBool(preference_update_from_mqtt, false); _hostname = _preferences->getString(preference_hostname, ""); uint64_t savedDevId = _preferences->getULong64(preference_nukihub_id, 0); uint8_t mac[8]; @@ -29,7 +25,10 @@ HomeAssistantDiscovery::HomeAssistantDiscovery(NetworkDevice* device, Preference char uidString[20]; itoa(_preferences->getUInt(preference_device_id_lock, 0), uidString, 10); removeHASSConfig(uidString); - delay(3000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(3000 / portTICK_PERIOD_MS); } else if(savedDevId != curDevId) { @@ -42,10 +41,16 @@ HomeAssistantDiscovery::HomeAssistantDiscovery(NetworkDevice* device, Preference char uidString[20]; itoa(_preferences->getUInt(preference_device_id_lock, 0), uidString, 10); removeHASSConfig(uidString); - delay(3000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(3000 / portTICK_PERIOD_MS); itoa(savedDevId, uidString, 10); removeHASSConfig(uidString); - delay(3000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(3000 / portTICK_PERIOD_MS); } sprintf(_nukiHubUidString, "%" PRIu64, curDevId); @@ -97,7 +102,10 @@ void HomeAssistantDiscovery::setupHASS(int type, uint32_t nukiId, char* nukiName void HomeAssistantDiscovery::disableHASS() { removeHASSConfig(_nukiHubUidString); - delay(3000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(3000 / portTICK_PERIOD_MS); char uidString[20]; @@ -105,13 +113,19 @@ void HomeAssistantDiscovery::disableHASS() { itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16); removeHASSConfig(uidString); - delay(3000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(3000 / portTICK_PERIOD_MS); } if(_preferences->getUInt(preference_nuki_id_opener, 0) != 0) { itoa(_preferences->getUInt(preference_nuki_id_opener, 0), uidString, 16); removeHASSConfig(uidString); - delay(3000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(3000 / portTICK_PERIOD_MS); } } @@ -349,7 +363,7 @@ void HomeAssistantDiscovery::publishHASSNukiHubConfig() "", { { (char*)"en", (char*)"true" }}); - if(_checkUpdates) + if(_preferences->getBool(preference_check_updates, false)) { // Nuki Hub latest publishHassTopic("sensor", @@ -375,7 +389,7 @@ void HomeAssistantDiscovery::publishHASSNukiHubConfig() _baseTopic.toCharArray(latest_version_topic,_baseTopic.length() + 1); strcat(latest_version_topic, mqtt_topic_info_nuki_hub_latest); - if(!_updateFromMQTT) + if(!_preferences->getBool(preference_update_from_mqtt, false)) { publishHassTopic("update", "nuki_hub_update", @@ -667,7 +681,7 @@ void HomeAssistantDiscovery::publishHASSDeviceConfig(char* deviceType, const cha "", { { (char*)"en", (char*)"true" } }); - if(_offEnabled && strcmp(deviceType, "SmartLock") == 0) + if(_preferences->getBool(preference_official_hybrid_enabled, false) && strcmp(deviceType, "SmartLock") == 0) { // Hybrid connected String hybridPath = _baseTopic; @@ -2988,7 +3002,7 @@ void HomeAssistantDiscovery::publishHassTopic(const String& mqttDeviceType, std::vector> additionalEntries ) { - if (_discoveryTopic != "") + if (_preferences->getString(preference_mqtt_hass_discovery, "") != "") { JsonDocument json; json = createHassJson(uidString, uidStringPostfix, displayName, name, baseTopic, stateTopic, deviceType, deviceClass, stateClass, entityCat, commandTopic, additionalEntries); @@ -3000,7 +3014,7 @@ void HomeAssistantDiscovery::publishHassTopic(const String& mqttDeviceType, String HomeAssistantDiscovery::createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) { - String path = _discoveryTopic; + String path = _preferences->getString(preference_mqtt_hass_discovery, ""); path.concat("/"); path.concat(mqttDeviceType); path.concat("/"); @@ -3014,7 +3028,7 @@ String HomeAssistantDiscovery::createHassTopicPath(const String& mqttDeviceType, void HomeAssistantDiscovery::removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) { - if (_discoveryTopic != "") + if (_preferences->getString(preference_mqtt_hass_discovery, "") != "") { String path = createHassTopicPath(mqttDeviceType, mqttDeviceName, uidString); _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); diff --git a/src/HomeAssistantDiscovery.h b/src/HomeAssistantDiscovery.h index 176990b..a81f9b4 100644 --- a/src/HomeAssistantDiscovery.h +++ b/src/HomeAssistantDiscovery.h @@ -59,17 +59,12 @@ private: NetworkDevice* _device = nullptr; Preferences* _preferences = nullptr; - String _discoveryTopic; String _baseTopic; String _hostname; JsonDocument _uidToName; char _nukiHubUidString[20]; - bool _offEnabled = false; - bool _checkUpdates = false; - bool _updateFromMQTT = false; - char* _buffer; const size_t _bufferSize; }; \ No newline at end of file diff --git a/src/ImportExport.cpp b/src/ImportExport.cpp index 049be5c..ada783a 100644 --- a/src/ImportExport.cpp +++ b/src/ImportExport.cpp @@ -41,6 +41,7 @@ void ImportExport::readSettings() _totpEnabled = _totpKey.length() > 0; _bypassKey = _preferences->getString(preference_bypass_secret, ""); _bypassEnabled = _bypassKey.length() > 0; + _updateTime = _preferences->getBool(preference_update_time, false); } bool ImportExport::getDuoEnabled() @@ -117,7 +118,7 @@ void ImportExport::setDuoCheckId(String duoCheckId) void ImportExport::saveSessions() { - if(_preferences->getBool(preference_update_time, false)) + if(_updateTime) { if (!SPIFFS.begin(true)) { diff --git a/src/ImportExport.h b/src/ImportExport.h index 7d04516..62c3073 100644 --- a/src/ImportExport.h +++ b/src/ImportExport.h @@ -43,6 +43,7 @@ private: bool _duoActiveRequest; bool _duoEnabled = false; bool _bypassGPIO = false; + bool _updateTime = false; int _bypassGPIOHigh = -1; int _bypassGPIOLow = -1; int64_t _duoRequestTS = 0; diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index 879ebf4..17cadec 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -23,7 +23,7 @@ extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_ extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_bundle_end"); #ifndef NUKI_HUB_UPDATER -NukiNetwork::NukiNetwork(Preferences *preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize, ImportExport* importExport) +NukiNetwork::NukiNetwork(Preferences *preferences, Gpio* gpio, char* buffer, size_t bufferSize, ImportExport* importExport) : _preferences(preferences), _gpio(gpio), _buffer(buffer), @@ -35,33 +35,6 @@ NukiNetwork::NukiNetwork(Preferences *preferences) #endif { _inst = this; - _webEnabled = _preferences->getBool(preference_webserver_enabled, true); - -#ifndef NUKI_HUB_UPDATER - memset(_maintenancePathPrefix, 0, sizeof(_maintenancePathPrefix)); - size_t len = maintenancePathPrefix.length(); - for(int i=0; i < len; i++) - { - _maintenancePathPrefix[i] = maintenancePathPrefix.charAt(i); - } - - _lockPath = _preferences->getString(preference_mqtt_lock_path); - String connectionStateTopic = _lockPath + mqtt_topic_mqtt_connection_state; - - memset(_mqttConnectionStateTopic, 0, sizeof(_mqttConnectionStateTopic)); - len = connectionStateTopic.length(); - for(int i=0; i < len; i++) - { - _mqttConnectionStateTopic[i] = connectionStateTopic.charAt(i); - } - - if(_preferences->getString(preference_mqtt_hass_discovery, "") != "" && !_preferences->getBool(preference_mqtt_hass_enabled, false)) - { - _preferences->putBool(preference_mqtt_hass_enabled, true); - } - -#endif - setupDevice(); } @@ -76,9 +49,9 @@ void NukiNetwork::setupDevice() if(hardwareDetect == 0) { -#ifndef CONFIG_IDF_TARGET_ESP32H2 + #ifndef CONFIG_IDF_TARGET_ESP32H2 hardwareDetect = 1; -#else + #else hardwareDetect = 11; _preferences->putInt(preference_network_custom_addr, 1); _preferences->putInt(preference_network_custom_cs, 8); @@ -88,13 +61,13 @@ void NukiNetwork::setupDevice() _preferences->putInt(preference_network_custom_miso, 12); _preferences->putInt(preference_network_custom_mosi, 13); _preferences->putBool(preference_ntw_reconfigure, true); -#endif + #endif _preferences->putInt(preference_network_hardware, hardwareDetect); } if(wifiFallback == true) { -#ifndef CONFIG_IDF_TARGET_ESP32H2 + #ifndef CONFIG_IDF_TARGET_ESP32H2 if(!_firstBootAfterDeviceChange) { Log->println("Failed to connect to network. Wi-Fi fallback is disabled, rebooting."); @@ -105,7 +78,7 @@ void NukiNetwork::setupDevice() Log->println("Switching to Wi-Fi device as fallback."); _networkDeviceType = NetworkDeviceType::WiFi; -#else + #else int custEth = _preferences->getInt(preference_network_custom_phy, 0); if(custEth<3) @@ -118,7 +91,7 @@ void NukiNetwork::setupDevice() } _preferences->putInt(preference_network_custom_phy, custEth); _preferences->putBool(preference_ntw_reconfigure, true); -#endif + #endif } else { @@ -130,19 +103,9 @@ void NukiNetwork::setupDevice() Log->print("Network device: "); Log->println(_device->deviceName()); -#ifndef NUKI_HUB_UPDATER - _device->mqttOnConnect([&](bool sessionPresent) - { - onMqttConnect(sessionPresent); - }); - _device->mqttOnDisconnect([&](espMqttClientTypes::DisconnectReason reason) - { - onMqttDisconnect(reason); - }); - + #ifndef NUKI_HUB_UPDATER _hadiscovery = new HomeAssistantDiscovery(_device, _preferences, _buffer, _bufferSize); -#endif - + #endif } void NukiNetwork::reconfigureDevice() @@ -212,6 +175,11 @@ bool NukiNetwork::wifiConnected() } } +String NukiNetwork::localIP() +{ + return _device->localIP(); +} + #ifdef NUKI_HUB_UPDATER void NukiNetwork::initialize() { @@ -248,6 +216,9 @@ bool NukiNetwork::update() #else void NukiNetwork::initialize() { + readSettings(); + setMQTTConnectionSettings(); + _gpio->addCallback([this](const GpioAction& action, const int& pin) { gpioActionCallback(action, pin); @@ -255,14 +226,6 @@ void NukiNetwork::initialize() if(!disableNetwork) { - String mqttPath = _preferences->getString(preference_mqtt_lock_path, ""); - - size_t len = mqttPath.length(); - for(int i=0; i < len; i++) - { - _nukiHubPath[i] = mqttPath.charAt(i); - } - _hostname = _preferences->getString(preference_hostname, ""); if(_hostname == "") @@ -277,48 +240,12 @@ void NukiNetwork::initialize() _preferences->putString(preference_hostname, _hostname); } - _mqttPort = _preferences->getInt(preference_mqtt_broker_port, 0); - - if(_mqttPort == 0) - { - _mqttPort = 1883; - _preferences->putInt(preference_mqtt_broker_port, _mqttPort); - } - strcpy(_hostnameArr, _hostname.c_str()); _device->initialize(); Log->print("Host name: "); Log->println(_hostname); - String brokerAddr = _preferences->getString(preference_mqtt_broker); - strcpy(_mqttBrokerAddr, brokerAddr.c_str()); - - String mqttUser = _preferences->getString(preference_mqtt_user); - if(mqttUser.length() > 0) - { - size_t len = mqttUser.length(); - for(int i=0; i < len; i++) - { - _mqttUser[i] = mqttUser.charAt(i); - } - } - - String mqttPass = _preferences->getString(preference_mqtt_password); - if(mqttPass.length() > 0) - { - size_t len = mqttPass.length(); - for(int i=0; i < len; i++) - { - _mqttPass[i] = mqttPass.charAt(i); - } - } - - Log->print("MQTT Broker: "); - Log->print(_mqttBrokerAddr); - Log->print(":"); - Log->println(_mqttPort); - _device->mqttSetClientId(_hostnameArr); _device->mqttSetCleanSession(false); _device->mqttSetKeepAlive(60); @@ -359,13 +286,12 @@ void NukiNetwork::initialize() break; } } - - readSettings(); } } void NukiNetwork::readSettings() { + _webEnabled = _preferences->getBool(preference_webserver_enabled, true); _disableNetworkIfNotConnected = _preferences->getBool(preference_disable_network_not_connected, false); _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect, false); _checkUpdates = _preferences->getBool(preference_check_updates, false); @@ -388,6 +314,110 @@ void NukiNetwork::readSettings() _publishDebugInfo = _preferences->getBool(preference_publish_debug_info, false); } +void NukiNetwork::setMQTTConnectionSettings() +{ + String mqttPath = _preferences->getString(preference_mqtt_lock_path, ""); + memset(_nukiHubPath, 0, sizeof(_nukiHubPath)); + size_t len = mqttPath.length(); + for(int i=0; i < len; i++) + { + _nukiHubPath[i] = mqttPath.charAt(i); + } + + String maintenancePathPrefix = _preferences->getString(preference_mqtt_lock_path); + memset(_maintenancePathPrefix, 0, sizeof(_maintenancePathPrefix)); + len = maintenancePathPrefix.length(); + for(int i=0; i < len; i++) + { + _maintenancePathPrefix[i] = maintenancePathPrefix.charAt(i); + } + + _lockPath = _preferences->getString(preference_mqtt_lock_path); + String connectionStateTopic = _lockPath + mqtt_topic_mqtt_connection_state; + + memset(_mqttConnectionStateTopic, 0, sizeof(_mqttConnectionStateTopic)); + len = connectionStateTopic.length(); + for(int i=0; i < len; i++) + { + _mqttConnectionStateTopic[i] = connectionStateTopic.charAt(i); + } + + if(_preferences->getString(preference_mqtt_hass_discovery, "") != "" && !_preferences->getBool(preference_mqtt_hass_enabled, false)) + { + _preferences->putBool(preference_mqtt_hass_enabled, true); + } + + memset(_mqttBrokerAddr, 0, sizeof(_mqttBrokerAddr)); + memset(_mqttUser, 0, sizeof(_mqttUser)); + memset(_mqttPass, 0, sizeof(_mqttPass)); + + String brokerAddr = _preferences->getString(preference_mqtt_broker); + strcpy(_mqttBrokerAddr, brokerAddr.c_str()); + + _mqttPort = _preferences->getInt(preference_mqtt_broker_port, 0); + + if(_mqttPort == 0) + { + _mqttPort = 1883; + _preferences->putInt(preference_mqtt_broker_port, _mqttPort); + } + + String mqttUser = _preferences->getString(preference_mqtt_user); + if(mqttUser.length() > 0) + { + len = mqttUser.length(); + for(int i=0; i < len; i++) + { + _mqttUser[i] = mqttUser.charAt(i); + } + } + + String mqttPass = _preferences->getString(preference_mqtt_password); + if(mqttPass.length() > 0) + { + len = mqttPass.length(); + for(int i=0; i < len; i++) + { + _mqttPass[i] = mqttPass.charAt(i); + } + } + + Log->print("MQTT Broker: "); + Log->print(_mqttBrokerAddr); + Log->print(":"); + Log->println(_mqttPort); + + #ifndef NUKI_HUB_UPDATER + _device->mqttOnConnect([&](bool sessionPresent) + { + onMqttConnect(sessionPresent); + }); + _device->mqttOnDisconnect([&](espMqttClientTypes::DisconnectReason reason) + { + onMqttDisconnect(reason); + }); + #endif +} + +int NukiNetwork::getRestartServices() +{ + int restartServices = _restartServices; + _restartServices = 0; + return restartServices; +} + +void NukiNetwork::setRestartServices(bool reconnect) +{ + if (reconnect) + { + _restartServices = 2; + } + else + { + _restartServices = 1; + } +} + bool NukiNetwork::update() { wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); @@ -446,7 +476,10 @@ bool NukiNetwork::update() bool success = reconnect(); if(!success) { - delay(2000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(2000 / portTICK_PERIOD_MS); _mqttConnectCounter++; return false; } @@ -455,14 +488,20 @@ bool NukiNetwork::update() if(forceEnableWebServer && !_webEnabled) { forceEnableWebServer = false; - delay(200); - restartEsp(RestartReason::ReconfigureWebServer); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); + setRestartServices(false); } else if(!_webEnabled) { forceEnableWebServer = false; } - delay(2000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(2000 / portTICK_PERIOD_MS); } if(!_device->isConnected() || !_device->mqttConnected() ) @@ -474,10 +513,16 @@ bool NukiNetwork::update() forceEnableWebServer = true; } Log->println("Network timeout has been reached, restarting ..."); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::NetworkTimeoutWatchdog); } - delay(2000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(2000 / portTICK_PERIOD_MS); return false; } @@ -656,12 +701,21 @@ void NukiNetwork::onMqttDisconnect(const espMqttClientTypes::DisconnectReason &r } } -bool NukiNetwork::reconnect() +bool NukiNetwork::reconnect(bool force) { _mqttConnectionState = 0; - while (!_device->mqttConnected() && espMillis() > _nextReconnect) + while (force || (!_device->mqttConnected() && espMillis() > _nextReconnect)) { + if (force) + { + _mqttReceivers.clear(); + _device->mqttRestart(); + setMQTTConnectionSettings(); + } + + force = false; + if(strcmp(_mqttBrokerAddr, "") == 0) { Log->println("MQTT Broker not configured, aborting connection attempt."); @@ -697,7 +751,10 @@ bool NukiNetwork::reconnect() while(!_connectReplyReceived && espMillis() < timeout) { - delay(50); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(50 / portTICK_PERIOD_MS); _device->update(); if(_keepAliveCallback != nullptr) { @@ -710,7 +767,10 @@ bool NukiNetwork::reconnect() Log->println("MQTT connected"); _mqttConnectedTs = millis(); _mqttConnectionState = 1; - delay(100); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); _device->mqttOnMessage(onMqttDataReceivedCallback); if(_firstConnect) @@ -955,7 +1015,10 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns { Log->println("Restart requested via MQTT."); clearWifiFallback(); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::RequestedViaMqtt); } else if(comparePrefixedPath(topic, mqtt_topic_update) && strcmp(data, "1") == 0 && _preferences->getBool(preference_update_from_mqtt, false) && !mqttRecentlyConnected()) @@ -1008,7 +1071,10 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); Log->println("Updating to latest release version."); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::OTAReboot); } } @@ -1023,7 +1089,10 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns _preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL); Log->println("Updating to latest beta version."); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::OTAReboot); } } @@ -1038,7 +1107,10 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL); Log->println("Updating to latest developmemt version."); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::OTAReboot); } } @@ -1053,7 +1125,10 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); Log->println("Updating to latest release version."); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::OTAReboot); } } @@ -1077,7 +1152,7 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns { return; } - Log->println("Webserver enabled, restarting."); + Log->println("Webserver enabled"); _preferences->putBool(preference_webserver_enabled, true); } else if (strcmp(data, "0") == 0) @@ -1086,12 +1161,15 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns { return; } - Log->println("Webserver disabled, restarting."); + Log->println("Webserver disabled"); _preferences->putBool(preference_webserver_enabled, false); } clearWifiFallback(); - delay(200); - restartEsp(RestartReason::ReconfigureWebServer); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); + setRestartServices(false); } else if(comparePrefixedPath(topic, mqtt_topic_nuki_hub_config_action) && !mqttRecentlyConnected()) { @@ -1141,8 +1219,10 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns while (duoResult == 2) { duoResult = _importExport->checkDuoApprove(); - delay(2000); - esp_task_wdt_reset(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(2000 / portTICK_PERIOD_MS); } } @@ -1259,7 +1339,10 @@ void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const uns serializeJson(json, _buffer, _bufferSize); publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_json, _buffer, false); publishString(_maintenancePathPrefix, mqtt_topic_nuki_hub_config_action, "--", true); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::ConfigurationUpdated); } else @@ -1396,10 +1479,10 @@ void NukiNetwork::removeTopic(const String& mqttPath, const String& mqttTopic) path.concat(mqttTopic); publish(path.c_str(), "", true); -#ifdef DEBUG_NUKIHUB + #ifdef DEBUG_NUKIHUB Log->print("Removing MQTT topic: "); Log->println(path.c_str()); -#endif + #endif } void NukiNetwork::setupHASS(int type, uint32_t nukiId, char* nukiName, const char* firmwareVersion, const char* hardwareVersion, bool hasDoorSensor, bool hasKeypad) @@ -1653,9 +1736,4 @@ void NukiNetwork::disableMqtt() _device->mqttDisable(); _mqttEnabled = false; } - -String NukiNetwork::localIP() -{ - return _device->localIP(); -} #endif diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index 8fde0e7..df7d707 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -32,24 +32,27 @@ public: bool mqttConnected(); bool wifiConnected(); void clearWifiFallback(); + int getRestartServices(); + void setRestartServices(bool reconnect = false); const String networkDeviceName() const; const String networkBSSID() const; const NetworkDeviceType networkDeviceType(); void setKeepAliveCallback(std::function reconnectTick); + String localIP(); NetworkDevice* device(); #ifdef NUKI_HUB_UPDATER explicit NukiNetwork(Preferences* preferences); #else - explicit NukiNetwork(Preferences* preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize, ImportExport* importExport); + explicit NukiNetwork(Preferences* preferences, Gpio* gpio, char* buffer, size_t bufferSize, ImportExport* importExport); void registerMqttReceiver(MqttReceiver* receiver); void disableAutoRestarts(); // disable on OTA start void disableMqtt(); - String localIP(); + bool reconnect(bool force = false); void subscribe(const char* prefix, const char* path); void initTopic(const char* prefix, const char* path, const char* value); void publishFloat(const char* prefix, const char* topic, const float value, bool retain, const uint8_t precision = 2); @@ -93,8 +96,8 @@ public: #endif private: void setupDevice(); - bool reconnect(); - + void setMQTTConnectionSettings(); + static NukiNetwork* _inst; const char* _latestVersion; @@ -132,6 +135,7 @@ private: ImportExport* _importExport; Gpio* _gpio; + int _restartServices = 0; int _mqttConnectionState = 0; int _mqttConnectCounter = 0; int _mqttPort = 1883; diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index b7189c4..e02f0db 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -7,6 +7,7 @@ #include "RestartReason.h" #include #include +#include "hal/wdt_hal.h" extern bool forceEnableWebServer; extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_bundle_start"); @@ -160,6 +161,11 @@ void NukiNetworkLock::initialize() bool NukiNetworkLock::update() { + wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_feed(&rtc_wdt_ctx); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); + bool ret = false; if(_nukiOfficial->hasOffStateToPublish()) diff --git a/src/NukiNetworkOpener.cpp b/src/NukiNetworkOpener.cpp index 8574ebd..53dc37f 100644 --- a/src/NukiNetworkOpener.cpp +++ b/src/NukiNetworkOpener.cpp @@ -5,6 +5,7 @@ #include "Logger.h" #include "Config.h" #include +#include "hal/wdt_hal.h" NukiNetworkOpener::NukiNetworkOpener(NukiNetwork* network, Preferences* preferences, char* buffer, size_t bufferSize) : _preferences(preferences), @@ -131,6 +132,11 @@ void NukiNetworkOpener::initialize() void NukiNetworkOpener::update() { + wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_feed(&rtc_wdt_ctx); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); + if(_resetRingStateTs != 0 && espMillis() >= _resetRingStateTs) { _resetRingStateTs = 0; diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index 28ba921..1a0998a 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -62,10 +62,10 @@ void NukiOpenerWrapper::initialize() _nukiOpener.setDebugCommand(_preferences->getBool(preference_debug_command, false)); _nukiOpener.registerLogger(Log); - _nukiOpener.initialize(_preferences->getBool(preference_connect_mode, true)); + _nukiOpener.initialize(); _nukiOpener.registerBleScanner(_bleScanner); _nukiOpener.setEventHandler(this); - _nukiOpener.setConnectTimeout(3); + _nukiOpener.setConnectTimeout(2); _nukiOpener.setDisconnectTimeout(2000); _hassEnabled = _preferences->getBool(preference_mqtt_hass_enabled, false); @@ -214,6 +214,16 @@ void NukiOpenerWrapper::readSettings() } } +uint8_t NukiOpenerWrapper::restartController() +{ + return _restartController; +} + +bool NukiOpenerWrapper::hasConnected() +{ + return _hasConnected; +} + void NukiOpenerWrapper::update() { wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); @@ -237,7 +247,10 @@ void NukiOpenerWrapper::update() } else { - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); return; } } @@ -254,9 +267,12 @@ void NukiOpenerWrapper::update() { Log->print("No BLE beacon received from the opener for "); Log->print((ts - lastReceivedBeaconTs) / 1000); - Log->println(" seconds, restarting device."); - delay(200); - restartEsp(RestartReason::BLEBeaconWatchdog); + Log->println(" seconds, signalling to restart BLE controller."); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); + _restartController = 2; } _nukiOpener.updateConnectionState(); @@ -288,7 +304,10 @@ void NukiOpenerWrapper::update() _network->publishRetry(std::to_string(retryCount + 1)); - delay(_retryDelay); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(_retryDelay / portTICK_PERIOD_MS); ++retryCount; } @@ -318,9 +337,17 @@ void NukiOpenerWrapper::update() } if(_statusUpdated || _nextLockStateUpdateTs == 0 || ts >= _nextLockStateUpdateTs || (queryCommands & QUERY_COMMAND_LOCKSTATE) > 0) { - _statusUpdated = updateKeyTurnerState(); _nextLockStateUpdateTs = ts + _intervalLockstate * 1000; + _statusUpdated = updateKeyTurnerState(); _network->publishStatusUpdated(_statusUpdated); + + if(_statusUpdated) + { + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(500 / portTICK_PERIOD_MS); + } } if(_network->mqttConnectionState() == 2) { @@ -498,9 +525,18 @@ bool NukiOpenerWrapper::updateKeyTurnerState() Log->println("ms"); _nextLockStateUpdateTs = espMillis() + _retryDelay; } + else + { + _nextLockStateUpdateTs = espMillis() + (_retryLockstateCount * 333); + } _network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState); return false; } + else if (!_hasConnected) + { + _hasConnected = true; + } + _retryLockstateCount = 0; const NukiOpener::LockState& lockState = _keyTurnerState.lockState; @@ -579,7 +615,10 @@ void NukiOpenerWrapper::updateBatteryState() { Log->print("Querying opener battery state: "); result = _nukiOpener.requestBatteryReport(&_batteryReport); - delay(250); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(250 / portTICK_PERIOD_MS); if(result != Nuki::CmdResult::Success) { ++retryCount; @@ -751,7 +790,10 @@ void NukiOpenerWrapper::updateAuthData(bool retrieved) if(result == Nuki::CmdResult::Success) { _waitAuthLogUpdateTs = espMillis() + 5000; - delay(100); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); std::list log; _nukiOpener.getLogEntries(&log); @@ -978,7 +1020,10 @@ void NukiOpenerWrapper::updateAuth(bool retrieved) { Log->print("Querying opener authorization: "); result = _nukiOpener.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH)); - delay(250); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(250 / portTICK_PERIOD_MS); if(result != Nuki::CmdResult::Success) { ++retryCount; @@ -3017,7 +3062,10 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value) if(resultKp == Nuki::CmdResult::Success) { - delay(5000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); std::list entries; _nukiOpener.getKeypadEntries(&entries); @@ -3383,7 +3431,10 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value) if(resultTc == Nuki::CmdResult::Success) { - delay(5000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); std::list timeControlEntries; _nukiOpener.getTimeControlEntries(&timeControlEntries); @@ -3839,7 +3890,10 @@ void NukiOpenerWrapper::onAuthCommandReceived(const char *value) if(resultAuth == Nuki::CmdResult::Success) { - delay(5000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); std::list entries; _nukiOpener.getAuthorizationEntries(&entries); @@ -4068,8 +4122,8 @@ void NukiOpenerWrapper::notify(Nuki::EventType eventType) } else if(eventType == Nuki::EventType::BLE_ERROR_ON_DISCONNECT) { - Log->println("Error in disconnecting BLE client, rebooting"); - restartEsp(RestartReason::BLEError); + Log->println("Error in disconnecting BLE client, signalling to restart BLE controller"); + _restartController = 1; } } diff --git a/src/NukiOpenerWrapper.h b/src/NukiOpenerWrapper.h index 5e09d1c..5c346e8 100644 --- a/src/NukiOpenerWrapper.h +++ b/src/NukiOpenerWrapper.h @@ -25,6 +25,7 @@ public: void deactivateRTO(); void deactivateCM(); + bool hasConnected(); bool isPinValid(); void setPin(const uint16_t pin); uint16_t getPin(); @@ -36,6 +37,7 @@ public: const bool isPaired() const; const bool hasKeypad() const; const BLEAddress getBleAddress() const; + uint8_t restartController(); std::string firmwareVersion() const; std::string hardwareVersion() const; @@ -135,9 +137,11 @@ private: bool _forceKeypad = false; bool _keypadEnabled = false; bool _forceId = false; + bool _hasConnected = false; uint _maxKeypadCodeCount = 0; uint _maxTimeControlEntryCount = 0; uint _maxAuthEntryCount = 0; + uint8_t _restartController = 0; int _rssiPublishInterval = 0; int64_t _statusUpdatedTs = 0; int64_t _nextLockStateUpdateTs = 0; @@ -158,7 +162,7 @@ private: std::string _firmwareVersion = ""; std::string _hardwareVersion = ""; NukiOpener::LockAction _nextLockAction = (NukiOpener::LockAction)0xff; - + char* _buffer; const size_t _bufferSize; }; diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 9dc8c2e..5021299 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -68,10 +68,10 @@ void NukiWrapper::initialize() _nukiLock.saveUltraPincode(_preferences->getInt(preference_lock_gemini_pin, 0), false); } - _nukiLock.initialize(_preferences->getBool(preference_connect_mode, true)); + _nukiLock.initialize(); _nukiLock.registerBleScanner(_bleScanner); _nukiLock.setEventHandler(this); - _nukiLock.setConnectTimeout(3); + _nukiLock.setConnectTimeout(2); _nukiLock.setDisconnectTimeout(2000); _hassEnabled = _preferences->getBool(preference_mqtt_hass_enabled, false); @@ -229,6 +229,16 @@ void NukiWrapper::readSettings() } } +uint8_t NukiWrapper::restartController() +{ + return _restartController; +} + +bool NukiWrapper::hasConnected() +{ + return _hasConnected; +} + void NukiWrapper::update(bool reboot) { wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); @@ -253,7 +263,10 @@ void NukiWrapper::update(bool reboot) } else { - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); return; } } @@ -270,9 +283,12 @@ void NukiWrapper::update(bool reboot) { Log->print("No BLE beacon received from the lock for "); Log->print((ts - lastReceivedBeaconTs) / 1000); - Log->println(" seconds, restarting device."); - delay(200); - restartEsp(RestartReason::BLEBeaconWatchdog); + Log->println(" seconds, signalling to restart BLE controller."); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); + _restartController = 2; } _nukiLock.updateConnectionState(); @@ -308,7 +324,10 @@ void NukiWrapper::update(bool reboot) _network->publishRetry(std::to_string(retryCount + 1)); - delay(_retryDelay); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(_retryDelay / portTICK_PERIOD_MS); ++retryCount; } @@ -342,9 +361,17 @@ void NukiWrapper::update(bool reboot) if(_nukiOfficial->getStatusUpdated() || _statusUpdated || _nextLockStateUpdateTs == 0 || ts >= _nextLockStateUpdateTs || (queryCommands & QUERY_COMMAND_LOCKSTATE) > 0) { Log->println("Updating Lock state based on status, timer or query"); - _statusUpdated = updateKeyTurnerState(); _nextLockStateUpdateTs = ts + _intervalLockstate * 1000; + _statusUpdated = updateKeyTurnerState(); _network->publishStatusUpdated(_statusUpdated); + + if(_statusUpdated) + { + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(500 / portTICK_PERIOD_MS); + } } if(_network->mqttConnectionState() == 2) { @@ -363,7 +390,7 @@ void NukiWrapper::update(bool reboot) updateConfig(); if(_isDebugging) { - updateDebug(); + //updateDebug(); } } if(_waitAuthLogUpdateTs != 0 && ts > _waitAuthLogUpdateTs) @@ -532,9 +559,17 @@ bool NukiWrapper::updateKeyTurnerState() Log->println("ms"); _nextLockStateUpdateTs = espMillis() + _retryDelay; } + else + { + _nextLockStateUpdateTs = espMillis() + (_retryLockstateCount * 333); + } _network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState); return false; } + else if (!_hasConnected) + { + _hasConnected = true; + } _retryLockstateCount = 0; @@ -787,8 +822,10 @@ void NukiWrapper::updateDebug() Log->println(result); count = 0; while (count < 5) { - delay(1000); - esp_task_wdt_reset(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); count++; } @@ -807,8 +844,10 @@ void NukiWrapper::updateDebug() count = 0; while (count < 15) { - delay(1000); - esp_task_wdt_reset(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); count++; } @@ -827,8 +866,10 @@ void NukiWrapper::updateDebug() count = 0; while (count < 20) { - delay(1000); - esp_task_wdt_reset(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); count++; } @@ -907,7 +948,10 @@ void NukiWrapper::updateAuthData(bool retrieved) if(result == Nuki::CmdResult::Success) { _waitAuthLogUpdateTs = espMillis() + 5000; - delay(100); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); std::list log; _nukiLock.getLogEntries(&log); @@ -1132,7 +1176,10 @@ void NukiWrapper::updateAuth(bool retrieved) { Log->print("Querying lock authorization: "); result = _nukiLock.retrieveAuthorizationEntries(0, _preferences->getInt(preference_auth_max_entries, MAX_AUTH)); - delay(250); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(250 / portTICK_PERIOD_MS); if(result != Nuki::CmdResult::Success) { ++retryCount; @@ -3265,7 +3312,10 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value) if(resultKp == Nuki::CmdResult::Success) { - delay(5000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); std::list entries; _nukiLock.getKeypadEntries(&entries); @@ -3632,7 +3682,10 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value) if(resultTc == Nuki::CmdResult::Success) { - delay(5000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); std::list timeControlEntries; _nukiLock.getTimeControlEntries(&timeControlEntries); @@ -3848,7 +3901,10 @@ void NukiWrapper::onAuthCommandReceived(const char *value) if(idExists) { result = _nukiLock.deleteAuthorizationEntry(authId); - delay(250); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(250 / portTICK_PERIOD_MS); Log->print("Delete authorization: "); Log->println((int)result); } @@ -4068,7 +4124,10 @@ void NukiWrapper::onAuthCommandReceived(const char *value) } result = _nukiLock.addAuthorizationEntry(entry); - delay(250); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(250 / portTICK_PERIOD_MS); Log->print("Add authorization: "); Log->println((int)result); } @@ -4091,7 +4150,10 @@ void NukiWrapper::onAuthCommandReceived(const char *value) if(resultAuth == Nuki::CmdResult::Success) { - delay(5000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); std::list entries; _nukiLock.getAuthorizationEntries(&entries); @@ -4234,7 +4296,10 @@ void NukiWrapper::onAuthCommandReceived(const char *value) } result = _nukiLock.updateAuthorizationEntry(entry); - delay(250); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(250 / portTICK_PERIOD_MS); Log->print("Update authorization: "); Log->println((int)result); } @@ -4320,8 +4385,8 @@ void NukiWrapper::notify(Nuki::EventType eventType) } else if(eventType == Nuki::EventType::BLE_ERROR_ON_DISCONNECT) { - Log->println("Error in disconnecting BLE client, rebooting"); - restartEsp(RestartReason::BLEError); + Log->println("Error in disconnecting BLE client, signalling to restart BLE controller"); + _restartController = 1; } } } @@ -4346,7 +4411,10 @@ void NukiWrapper::readConfig() { ++retryCount; Log->println("Failed to retrieve lock config, retrying in 1s"); - delay(1000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); } else { @@ -4374,7 +4442,10 @@ void NukiWrapper::readAdvancedConfig() { ++retryCount; Log->println("Failed to retrieve lock advanced config, retrying in 1s"); - delay(1000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); } else { diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index 1f132f9..6696334 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -27,6 +27,7 @@ public: void lockngo(); void lockngounlatch(); + bool hasConnected(); bool isPinValid(); void setPin(const uint16_t pin); void setUltraPin(const uint32_t pin); @@ -42,6 +43,7 @@ public: bool hasDoorSensor() const; bool offConnected(); const BLEAddress getBleAddress() const; + uint8_t restartController(); std::string firmwareVersion() const; std::string hardwareVersion() const; @@ -142,9 +144,11 @@ private: bool _forceId = false; bool _isUltra = false; bool _isDebugging = false; + bool _hasConnected = false; uint _maxKeypadCodeCount = 0; uint _maxTimeControlEntryCount = 0; uint _maxAuthEntryCount = 0; + uint8_t _restartController = 0; int _nrOfRetries = 0; int _retryDelay = 0; int _retryConfigCount = 0; diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index 5762e56..b13c9c2 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -12,23 +12,12 @@ #endif //CHANGE REQUIRES REBOOT TO TAKE EFFECT +//NETWORK RELATED #define preference_ip_dhcp_enabled (char*)"dhcpena" #define preference_ip_address (char*)"ipaddr" #define preference_ip_subnet (char*)"ipsub" #define preference_ip_gateway (char*)"ipgtw" #define preference_ip_dns_server (char*)"dnssrv" -#define preference_mqtt_broker (char*)"mqttbroker" -#define preference_mqtt_broker_port (char*)"mqttport" -#define preference_mqtt_user (char*)"mqttuser" -#define preference_mqtt_password (char*)"mqttpass" -#define preference_mqtt_log_enabled (char*)"mqttlog" -#define preference_webserial_enabled (char*)"weblog" -#define preference_lock_enabled (char*)"lockena" -#define preference_mqtt_lock_path (char*)"mqttpath" -#define preference_opener_enabled (char*)"openerena" -#define preference_mqtt_ca (char*)"mqttca" -#define preference_mqtt_crt (char*)"mqttcrt" -#define preference_mqtt_key (char*)"mqttkey" #define preference_network_hardware (char*)"nwhw" #define preference_hostname (char*)"hostname" #define preference_network_custom_phy (char*)"ntwPHY" @@ -43,61 +32,22 @@ #define preference_network_custom_mdio (char*)"ntwMDIO" #define preference_network_custom_mdc (char*)"ntwMDC" #define preference_network_custom_clk (char*)"ntwCLK" -#define preference_auth_control_enabled (char*)"authCtrlEna" -#define preference_keypad_control_enabled (char*)"kpCntrlEnabled" -#define preference_timecontrol_control_enabled (char*)"tcCntrlEnabled" -#define preference_ota_main_url (char*)"otaMainUrl" -#define preference_ota_updater_url (char*)"otaUpdUrl" -#define preference_task_size_network (char*)"tsksznetw" -#define preference_task_size_nuki (char*)"tsksznuki" -#define preference_buffer_size (char*)"buffsize" -#define preference_cred_user (char*)"crdusr" -#define preference_cred_password (char*)"crdpass" -#define preference_gpio_configuration (char*)"gpiocfg" -#define preference_mqtt_hass_enabled (char*)"hassena" -#define preference_mqtt_hass_discovery (char*)"hassdiscovery" -#define preference_hass_device_discovery (char*)"hassdevdisc" -#define preference_webserver_enabled (char*)"websrvena" -#define preference_update_from_mqtt (char*)"updMqtt" -#define preference_disable_non_json (char*)"disnonjson" -#define preference_official_hybrid_enabled (char*)"offHybrid" #define preference_wifi_ssid (char*)"wifiSSID" #define preference_wifi_pass (char*)"wifiPass" -#define preference_disable_network_not_connected (char*)"disNtwNoCon" -#define preference_debug_connect (char*)"dbgConnect" -#define preference_debug_communication (char*)"dbgCommu" -#define preference_debug_readable_data (char*)"dbgReadData" -#define preference_debug_hex_data (char*)"dbgHexData" -#define preference_debug_command (char*)"dbgCommand" -#define preference_connect_mode (char*)"nukiConnMode" -#define preference_http_auth_type (char*)"httpdAuthType" -#define preference_update_time (char*)"updateTime" #define preference_time_server (char*)"timeServer" -#define preference_mqtt_ssl_enabled (char*)"mqttSSLena" -#define preference_lock_gemini_pin (char*)"geminiPin" -#define preference_lock_gemini_enabled (char*)"geminiena" -#define preference_cred_duo_enabled (char*)"duoena" -#define preference_cred_duo_host (char*)"duoHost" -#define preference_cred_duo_ikey (char*)"duoIkey" -#define preference_cred_duo_skey (char*)"duoSkey" -#define preference_cred_duo_user (char*)"duoUser" -#define preference_https_fqdn (char*)"httpsFQDN" -#define preference_bypass_proxy (char*)"credBypass" -#define preference_cred_session_lifetime (char*)"credLf" -#define preference_cred_session_lifetime_remember (char*)"credLfRmbr" -#define preference_cred_session_lifetime_duo (char*)"credLfDuo" -#define preference_cred_session_lifetime_duo_remember (char*)"credLfDuoRmbr" -#define preference_cred_session_lifetime_totp (char*)"credLfTotp" -#define preference_cred_session_lifetime_totp_remember (char*)"credLfTotpRmbr" -#define preference_cred_duo_approval (char*)"duoApprove" -#define preference_cred_bypass_boot_btn_enabled (char*)"bypassBtBtn" -#define preference_cred_bypass_gpio_high (char*)"bypassHigh" -#define preference_cred_bypass_gpio_low (char*)"bypassLow" -#define preference_publish_config (char*)"nhPubConfig" -#define preference_config_from_mqtt (char*)"nhCntrlEnabled" -#define preference_totp_secret (char*)"totpsecret" -#define preference_bypass_secret (char*)"bypassecret" -#define preference_admin_secret (char*)"adminsecret" + +//MQTT RELATED +#define preference_mqtt_log_enabled (char*)"mqttlog" +#define preference_gpio_configuration (char*)"gpiocfg" + +//TASKS RELATED +#define preference_task_size_network (char*)"tsksznetw" +#define preference_task_size_nuki (char*)"tsksznuki" + +//OTHER +#define preference_ota_main_url (char*)"otaMainUrl" +#define preference_ota_updater_url (char*)"otaUpdUrl" +#define preference_buffer_size (char*)"buffsize" // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT #define preference_find_best_rssi (char*)"nwbestrssi" @@ -149,7 +99,65 @@ #define preference_lock_force_keypad (char*)"lckForceKp" #define preference_opener_force_id (char*)"opForceId" #define preference_opener_force_keypad (char*)"opForceKp" +#define preference_admin_secret (char*)"adminsecret" + +//REQUIRE SERVICES RELOAD +#define preference_lock_enabled (char*)"lockena" +#define preference_opener_enabled (char*)"openerena" +#define preference_debug_connect (char*)"dbgConnect" +#define preference_debug_communication (char*)"dbgCommu" +#define preference_debug_readable_data (char*)"dbgReadData" +#define preference_debug_hex_data (char*)"dbgHexData" +#define preference_debug_command (char*)"dbgCommand" +#define preference_update_time (char*)"updateTime" +#define preference_cred_user (char*)"crdusr" +#define preference_cred_password (char*)"crdpass" +#define preference_lock_gemini_pin (char*)"geminiPin" +#define preference_lock_gemini_enabled (char*)"geminiena" +#define preference_https_fqdn (char*)"httpsFQDN" +#define preference_bypass_proxy (char*)"credBypass" +#define preference_http_auth_type (char*)"httpdAuthType" +#define preference_cred_duo_enabled (char*)"duoena" +#define preference_cred_duo_host (char*)"duoHost" +#define preference_cred_duo_ikey (char*)"duoIkey" +#define preference_cred_duo_skey (char*)"duoSkey" +#define preference_cred_duo_user (char*)"duoUser" +#define preference_cred_session_lifetime (char*)"credLf" +#define preference_cred_session_lifetime_remember (char*)"credLfRmbr" +#define preference_cred_session_lifetime_duo (char*)"credLfDuo" +#define preference_cred_session_lifetime_duo_remember (char*)"credLfDuoRmbr" +#define preference_cred_session_lifetime_totp (char*)"credLfTotp" +#define preference_cred_session_lifetime_totp_remember (char*)"credLfTotpRmbr" +#define preference_cred_duo_approval (char*)"duoApprove" +#define preference_cred_bypass_boot_btn_enabled (char*)"bypassBtBtn" +#define preference_cred_bypass_gpio_high (char*)"bypassHigh" +#define preference_cred_bypass_gpio_low (char*)"bypassLow" +#define preference_disable_network_not_connected (char*)"disNtwNoCon" +#define preference_webserver_enabled (char*)"websrvena" +#define preference_hass_device_discovery (char*)"hassdevdisc" +#define preference_webserial_enabled (char*)"weblog" +#define preference_config_from_mqtt (char*)"nhCntrlEnabled" +#define preference_publish_config (char*)"nhPubConfig" +#define preference_update_from_mqtt (char*)"updMqtt" +#define preference_mqtt_broker (char*)"mqttbroker" +#define preference_mqtt_broker_port (char*)"mqttport" +#define preference_mqtt_user (char*)"mqttuser" +#define preference_mqtt_password (char*)"mqttpass" +#define preference_mqtt_lock_path (char*)"mqttpath" +#define preference_mqtt_hass_enabled (char*)"hassena" +#define preference_mqtt_hass_discovery (char*)"hassdiscovery" +#define preference_disable_non_json (char*)"disnonjson" +#define preference_official_hybrid_enabled (char*)"offHybrid" +#define preference_auth_control_enabled (char*)"authCtrlEna" +#define preference_keypad_control_enabled (char*)"kpCntrlEnabled" +#define preference_timecontrol_control_enabled (char*)"tcCntrlEnabled" #define preference_hybrid_reboot_on_disconnect (char*)"hybridRbtLck" +#define preference_bypass_secret (char*)"bypassecret" +#define preference_totp_secret (char*)"totpsecret" +#define preference_mqtt_ssl_enabled (char*)"mqttSSLena" +#define preference_mqtt_ca (char*)"mqttca" +#define preference_mqtt_crt (char*)"mqttcrt" +#define preference_mqtt_key (char*)"mqttkey" //NOT USER CHANGABLE #define preference_mfa_reconfigure (char*)"mfaRECONF" @@ -242,7 +250,6 @@ inline void initPreferences(Preferences* preferences) preferences->putBool(preference_debug_readable_data, false); preferences->putBool(preference_debug_hex_data, false); preferences->putBool(preference_debug_command, false); - preferences->putBool(preference_connect_mode, true); preferences->putBool(preference_retain_gpio, false); preferences->putBool(preference_enable_debug_mode, false); preferences->putBool(preference_cred_duo_enabled, false); @@ -532,7 +539,7 @@ private: preference_network_custom_pwr, preference_network_custom_mdio, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, preference_wifi_ssid, preference_wifi_pass, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_mqtt_hass_enabled, preference_hass_device_discovery, preference_retain_gpio, - preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, + preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_nukihub_id, preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_cred_duo_enabled, preference_https_fqdn, preference_bypass_proxy, preference_cred_session_lifetime, preference_cred_session_lifetime_remember, preference_cred_session_lifetime_duo, preference_cred_session_lifetime_duo_remember, @@ -556,7 +563,7 @@ private: preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_webserial_enabled, preference_hass_device_discovery, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, preference_cred_bypass_boot_btn_enabled, - preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, + preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_mqtt_ssl_enabled, preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode, preference_cred_duo_enabled, preference_cred_duo_approval, preference_publish_config, preference_config_from_mqtt diff --git a/src/SerialReader.cpp b/src/SerialReader.cpp index ac949bd..eb88124 100644 --- a/src/SerialReader.cpp +++ b/src/SerialReader.cpp @@ -1,6 +1,7 @@ #include "SerialReader.h" #include "RestartReason.h" #include "EspMillis.h" +#include "hal/wdt_hal.h" SerialReader::SerialReader(ImportExport *importExport, NukiNetwork* network) : _importExport(importExport), @@ -11,6 +12,11 @@ SerialReader::SerialReader(ImportExport *importExport, NukiNetwork* network) void SerialReader::update() { + wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_feed(&rtc_wdt_ctx); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); + if(Serial.available()) { String line = Serial.readStringUntil('\n'); diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 1f98a11..ab9ab76 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -7,8 +7,10 @@ #include "FS.h" #include "SPIFFS.h" #include "esp_random.h" -#ifdef CONFIG_SOC_SPIRAM_SUPPORTED +#if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM) #include "esp_psram.h" +#endif +#ifdef NUKI_HUB_HTTPS_SERVER #include "util/SSLCert.hpp" #endif #ifndef CONFIG_IDF_TARGET_ESP32H2 @@ -26,6 +28,15 @@ extern bool timeSynced; #include #include #include "ArduinoJson.h" +#include + +typedef struct { + int socket; + char *buffer; + size_t len; +} WebsocketMessage; + +QueueHandle_t wsMessages; WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer, ImportExport* importExport) : _nuki(nuki), @@ -86,6 +97,7 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool } _confirmCode = generateConfirmCode(); + #ifndef NUKI_HUB_UPDATER _brokerConfigured = _preferences->getString(preference_mqtt_broker).length() > 0 && _preferences->getInt(preference_mqtt_broker_port) > 0; #endif @@ -116,8 +128,13 @@ bool WebCfgServer::isAuthenticated(PsychicRequest *request, int type) { struct timeval time; gettimeofday(&time, NULL); - int64_t time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec; - + int64_t time_us = 0; + + if (timeSynced) + { + time_us = (int64_t)time.tv_sec * 1000000L + (int64_t)time.tv_usec; + } + if ((type == 0 && _httpSessions[cookie].as() > time_us) || (type == 1 && _importExport->_duoSessions[cookie].as() > time_us) || (type == 2 && _importExport->_totpSessions[cookie].as() > time_us) || (type == 3 && _importExport->_bypassSessions[cookie].as() > time_us)) { return true; @@ -420,6 +437,96 @@ void WebCfgServer::initialize() { return sendCss(request, resp); }); + + + if(_preferences->getBool(preference_webserial_enabled, false)) + { + #ifndef NUKI_HUB_UPDATER + if (websocketHandler == nullptr) + { + websocketHandler = new PsychicWebSocketHandler; + } + + _psychicServer->on("/webserial", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) + { + int authReq = doAuthentication(request); + + switch (authReq) + { + case 0: + return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + break; + case 1: + return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in."); + break; + case 2: + resp->setCode(302); + resp->addHeader("Cache-Control", "no-cache"); + return resp->redirect("/get?page=login"); + break; + case 3: + case 5: + case 4: + default: + break; + } + + return sendWebSerial(request, resp); + }); + + //prepare our message queue of 10 messages + wsMessages = xQueueCreate(10, sizeof(WebsocketMessage)); + if (wsMessages == 0) { + Log->printf("Failed to create queue= %p\n", wsMessages); + } + + websocketHandler->onOpen([](PsychicWebSocketClient *client) { + Log->printf("[socket] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString()); + client->sendMessage("NukiHub WebSerial started"); + }); + websocketHandler->onFrame([](PsychicWebSocketRequest *request, httpd_ws_frame *frame) + { + if(strcmp((char *)frame->payload, "ping") == 0) + { + WebsocketMessage wm; + wm.socket = request->client()->socket(); + wm.len = frame->len; + wm.buffer = (char *)malloc(frame->len); + + if (wm.buffer == NULL) + { + Log->printf("Queue message: unable to allocate %d bytes\n", frame->len); + return ESP_FAIL; + } + + memcpy(wm.buffer, "pong", frame->len); + + if (xQueueSend(wsMessages, &wm, 1) != pdTRUE) + { + Log->printf("[socket] queue full #%d\n", wm.socket); + free(wm.buffer); + } + + if (!uxQueueSpacesAvailable(wsMessages)) + { + return request->reply("Queue Full"); + } + } + else + { + Log->printf("[socket] #%d sent: %s\n", request->client()->socket(), (char *)frame->payload); + } + return ESP_OK; + }); + websocketHandler->onClose([](PsychicWebSocketClient *client) { + Log->printf("[socket] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString()); + }); + + _psychicServer->on("/ws", websocketHandler); + #endif + + } + _psychicServer->on("/favicon.ico", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { return sendFavicon(request, resp); @@ -428,6 +535,110 @@ void WebCfgServer::initialize() if(_network->isApOpen()) { #ifndef CONFIG_IDF_TARGET_ESP32H2 + _psychicServer->on("/get", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) + { + String value = ""; + if(request->hasParam("page")) + { + const PsychicWebParameter* p = request->getParam("page"); + if(p->value() != "") + { + value = p->value(); + } + } + if (value != "login") + { + int authReq = doAuthentication(request); + + switch (authReq) + { + case 0: + return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + break; + case 1: + return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in."); + break; + case 2: + resp->setCode(302); + resp->addHeader("Cache-Control", "no-cache"); + return resp->redirect("/get?page=login"); + break; + case 4: + default: + break; + } + } + if (value == "login") + { + return buildLoginHtml(request, resp); + } + else + { + Log->println("Page not found, loading index"); + resp->setCode(302); + resp->addHeader("Cache-Control", "no-cache"); + return resp->redirect("/"); + } + }); + + _psychicServer->on("/post", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp) + { + String value = ""; + if(request->hasParam("page")) + { + const PsychicWebParameter* p = request->getParam("page"); + if(p->value() != "") + { + value = p->value(); + } + } + + if(value != "login") + { + int authReq = doAuthentication(request); + + switch (authReq) + { + case 0: + return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + break; + case 1: + return request->requestAuthentication(DIGEST_AUTH, "Nuki Hub", "You must log in."); + break; + case 2: + resp->setCode(302); + resp->addHeader("Cache-Control", "no-cache"); + return resp->redirect("/get?page=login"); + break; + break; + case 4: + default: + break; + } + } + + if (value == "login") + { + bool loggedIn = processLogin(request, resp); + if (loggedIn) + { + resp->setCode(302); + resp->addHeader("Cache-Control", "no-cache"); + return resp->redirect("/"); + } + else + { + resp->setCode(302); + resp->addHeader("Cache-Control", "no-cache"); + return resp->redirect("/get?page=login"); + } + } + else + { + return buildWifiConnectHtml(request, resp); + } + }); + _psychicServer->on("/ssidlist", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { return buildSSIDListHtml(request, resp); @@ -669,6 +880,34 @@ void WebCfgServer::initialize() return res; } #ifndef NUKI_HUB_UPDATER + else if (value == "restartservices") + { + String value2 = ""; + if(request->hasParam("CONFIRMTOKEN")) + { + const PsychicWebParameter* p = request->getParam("CONFIRMTOKEN"); + if(p->value() != "") + { + value2 = p->value(); + } + } + else + { + return buildConfirmHtml(request, resp, "No confirm code set.", 3, true); + } + + if(value2 != _confirmCode) + { + resp->setCode(302); + resp->addHeader("Cache-Control", "no-cache"); + return resp->redirect("/"); + } + esp_err_t res = buildConfirmHtml(request, resp, "Restarting services...", 2, true); + _network->setRestartServices(_restartServicesRequired == 1 ? false : true); + _restartServicesRequired = 0; + waitAndProcess(true, 1000); + return res; + } else if (value == "info") { return buildInfoHtml(request, resp); @@ -778,7 +1017,7 @@ void WebCfgServer::initialize() else if (value == "selfsignhttps") { return buildHttpSSLConfigHtml(request, resp, 3); - } + } else if (value == "nukicfg") { return buildNukiConfigHtml(request, resp); @@ -1482,29 +1721,41 @@ bool WebCfgServer::processWiFi(PsychicRequest *request, PsychicResponse* resp, S int loop = 0; while(!_network->isConnected() && loop < 150) { - delay(100); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); loop++; } if (!_network->isConnected()) { - message = "Failed to connect to the given SSID with the given secret key, credentials not saved
"; + message = "Failed to connect to the given SSID, settings not saved
"; return res; } else { - if(_network->isConnected()) + message = "Connection successful. Rebooting Nuki Hub.
Please connect this device to the wireless network with the SSID " + + ssid + " or a wired/wireless connection that has access to the network of the selected SSID now
and navigate to Nuki Hub at "; + + if (_isSSL) { - message = "Connection successful. Rebooting Nuki Hub.
"; - _preferences->putString(preference_wifi_ssid, ssid); - _preferences->putString(preference_wifi_pass, pass); - res = true; + if (_preferences->getString(preference_https_fqdn, "") != "") + { + message += "getString(preference_https_fqdn, "") + "\">https://" + _preferences->getString(preference_https_fqdn, "") + ""; + } + else + { + message += "localIP() + "\">https://" + _network->localIP() + ""; + } } else { - message = "Failed to connect to the given SSID, no IP received, credentials not saved
"; - return res; + message += "localIP() + "\">http://" + _network->localIP() + ""; } + _preferences->putString(preference_wifi_ssid, ssid); + _preferences->putString(preference_wifi_pass, pass); + res = true; } } else @@ -1604,16 +1855,30 @@ esp_err_t WebCfgServer::buildOtaHtml(PsychicRequest *request, PsychicResponse* r response.print("

"); response.print("

"); response.print("

"); - #if defined(CONFIG_IDF_TARGET_ESP32S3) + #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(CONFIG_SPIRAM) if(esp_psram_get_size() <= 0) { response.print("

"); + response.print("

"); } - #elif defined(CONFIG_IDF_TARGET_ESP32) && !defined(NUKI_TARGET_GL_S10) + #elif defined(CONFIG_IDF_TARGET_ESP32S3) + response.print("

"); + response.print("

"); + #elif defined(CONFIG_IDF_TARGET_ESP32) && defined(CONFIG_SPIRAM) && !defined(NUKI_TARGET_GL_S10) if(_preferences->getInt(preference_network_hardware) == 8) { response.print("

"); } + if(esp_psram_get_size() <= 0) + { + response.print("

"); + } + #elif defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_SPIRAM) + if(_preferences->getInt(preference_network_hardware) == 8) + { + response.print("

"); + } + response.print("

"); #endif response.print("

"); @@ -1773,7 +2038,7 @@ void WebCfgServer::waitAndProcess(const bool blocking, const uint32_t duration) } else { - vTaskDelay( 50 / portTICK_PERIOD_MS); + vTaskDelay(50 / portTICK_PERIOD_MS); } } } @@ -2393,6 +2658,34 @@ bool WebCfgServer::processTOTP(PsychicRequest *request, PsychicResponse* resp) } #ifndef NUKI_HUB_UPDATER +esp_err_t WebCfgServer::sendWebSerial(PsychicRequest* request, PsychicResponse* resp) +{ + // escaped by https://www.cescaper.com/ + resp->addHeader("Cache-Control", "public, max-age=3600"); + resp->setCode(200); + resp->setContentType("text/html"); + resp->setContent((const uint8_t *)WEBSERIAL_HTML, sizeof(WEBSERIAL_HTML)); + return resp->send(); +} + +void WebCfgServer::updateWebSerial() +{ + if (websocketHandler != nullptr) { + WebsocketMessage message; + while (xQueueReceive(wsMessages, &message, 0) == pdTRUE) + { + PsychicWebSocketClient *client = websocketHandler->getClient(message.socket); + if (client == NULL) { + Log->printf("[socket] client #%d bad, bailing\n", message.socket); + return; + } + + client->sendMessage(HTTPD_WS_TYPE_TEXT, message.buffer, message.len); + free(message.buffer); + } + } +} + esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* resp, bool adminKey) { JsonDocument json; @@ -2463,6 +2756,8 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S bool networkReconfigure = false; bool clearSession = false; bool newMFA = false; + bool restartServicesNoReconnect = false; + bool restartServicesReconnect = false; unsigned char currentBleAddress[6]; unsigned char authorizationId[4] = {0x00}; unsigned char secretKeyK[32] = {0x00}; @@ -2506,7 +2801,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_mqtt_broker, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "MQTTPORT") @@ -2516,7 +2811,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_mqtt_broker_port, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "MQTTUSER") @@ -2532,7 +2827,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_mqtt_user, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } } @@ -2545,7 +2840,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_mqtt_password, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } } @@ -2556,7 +2851,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_mqtt_lock_path, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "MQTTCA") @@ -2591,7 +2886,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } } @@ -2627,7 +2922,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } } @@ -2663,12 +2958,11 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } } - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - else if(key == "HTTPCRT") + else if(key == "HTTPCRT" && nuki_hub_https_server_enabled) { if (!SPIFFS.begin(true)) { Log->println("SPIFFS Mount Failed"); @@ -2700,11 +2994,11 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } } - else if(key == "HTTPKEY") + else if(key == "HTTPKEY" && nuki_hub_https_server_enabled) { if (!SPIFFS.begin(true)) { Log->println("SPIFFS Mount Failed"); @@ -2736,18 +3030,19 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } } - else if(key == "HTTPGEN") + else if(key == "HTTPGEN" && nuki_hub_https_server_enabled) { + #ifdef NUKI_HUB_HTTPS_SERVER createSSLCertificate(); + #endif Log->print("Setting changed: "); Log->println(key); - configChanged = true; + configChanged = true; } - #endif else if(key == "UPTIME") { if(_preferences->getBool(preference_update_time, false) != (value == "1")) @@ -2755,7 +3050,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_update_time, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "TIMESRV") @@ -2925,7 +3220,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_rssi_publish_interval, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "HTTPSFQDN") @@ -2935,7 +3229,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_https_fqdn, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DUOHOST") @@ -2947,7 +3241,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_duo_host, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; newMFA = true; } @@ -2962,7 +3256,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_duo_ikey, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; newMFA = true; } @@ -2977,7 +3271,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_duo_skey, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; newMFA = true; } @@ -2992,7 +3286,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_duo_user, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; newMFA = true; } @@ -3008,7 +3302,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; newMFA = true; } @@ -3020,7 +3314,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_cred_bypass_boot_btn_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DUOBYPASSHIGH") @@ -3030,7 +3324,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_bypass_gpio_high, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DUOBYPASSLOW") @@ -3040,7 +3334,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_bypass_gpio_low, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DUOAPPROVAL") @@ -3050,7 +3344,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_cred_duo_approval, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "CREDLFTM") @@ -3060,7 +3354,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_session_lifetime, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -3071,7 +3365,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_session_lifetime_remember, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -3082,7 +3376,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_session_lifetime_duo, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -3093,7 +3387,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_session_lifetime_duo_remember, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -3104,7 +3398,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_session_lifetime_totp, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -3115,7 +3409,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_cred_session_lifetime_totp_remember, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -3127,7 +3421,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_hass_device_discovery, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "ENHADISC") @@ -3138,7 +3432,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_mqtt_hass_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "HASSDISCOVERY") @@ -3149,7 +3443,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_mqtt_hass_discovery, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "OPENERCONT") @@ -3159,7 +3453,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_opener_continuous_mode, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "HASSCUURL") @@ -3169,7 +3462,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_mqtt_hass_cu_url, value); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "HOSTNAME") @@ -3189,7 +3481,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_network_timeout, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "FINDBESTRSSI") @@ -3199,7 +3490,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_find_best_rssi, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "RSTDISC") @@ -3209,7 +3499,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_restart_on_disconnect, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "MQTTLOG") @@ -3229,7 +3518,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_mqtt_ssl_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "WEBLOG") @@ -3239,7 +3528,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_webserial_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "CHECKUPDATE") @@ -3249,7 +3538,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_check_updates, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; + restartServicesReconnect = true; } } else if(key == "UPDATEMQTT") @@ -3259,7 +3548,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_update_from_mqtt, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "OFFHYBRID") @@ -3273,7 +3562,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "HYBRIDACT") @@ -3287,7 +3576,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "HYBRIDTIMER") @@ -3297,7 +3585,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_query_interval_hybrid_lockstate, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "HYBRIDRETRY") @@ -3307,7 +3594,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_official_hybrid_retry, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "HYBRIDREBOOT") @@ -3317,7 +3603,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_hybrid_reboot_on_disconnect, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; + restartServicesReconnect = true; } } else if(key == "DISNONJSON") @@ -3327,7 +3613,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_disable_non_json, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "DHCPENA") @@ -3387,7 +3673,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_query_interval_lockstate, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "CFGINT") @@ -3397,7 +3682,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_query_interval_configuration, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "BATINT") @@ -3407,7 +3691,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_query_interval_battery, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "KPINT") @@ -3417,7 +3700,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_query_interval_keypad, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "NRTRY") @@ -3427,7 +3709,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_command_nr_of_retries, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "TRYDLY") @@ -3437,7 +3718,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_command_retry_delay, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "TXPWR") @@ -3453,7 +3733,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_ble_tx_power, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } } @@ -3464,7 +3743,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_restart_ble_beacon_lost, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "TSKNTWK") @@ -3502,7 +3780,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_authlog_max_entries, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } } @@ -3515,7 +3792,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_keypad_max_entries, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } } @@ -3528,7 +3804,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_timecontrol_max_entries, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } } @@ -3541,7 +3816,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_auth_max_entries, value.toInt()); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } } @@ -3565,7 +3839,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_enable_bootloop_reset, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "DISNTWNOCON") @@ -3575,7 +3848,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_disable_network_not_connected, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "OTAUPD") @@ -3605,7 +3878,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_show_secrets, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "DBGCONN") @@ -3615,7 +3887,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_debug_connect, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DBGCOMMU") @@ -3625,7 +3897,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_debug_communication, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DBGHEAP") @@ -3635,7 +3907,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_publish_debug_info, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DBGREAD") @@ -3645,7 +3917,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_debug_readable_data, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DBGHEX") @@ -3655,7 +3927,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_debug_hex_data, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "DBGCOMM") @@ -3665,7 +3937,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_debug_command, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "LCKFORCEID") @@ -3724,7 +3996,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_conf_info_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "CONFNHPUB") @@ -3738,7 +4009,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_publish_config, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "CONFNHCTRL") @@ -3752,7 +4023,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_config_from_mqtt, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "KPPUB") @@ -3762,7 +4033,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_keypad_info_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "KPCODE") @@ -3772,7 +4042,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_keypad_publish_code, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "KPCHECK") @@ -3782,7 +4051,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_keypad_check_code_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "KPENA") @@ -3792,7 +4060,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_keypad_control_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "TCPUB") @@ -3802,7 +4070,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_timecontrol_info_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "AUTHPUB") @@ -3812,7 +4079,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_auth_info_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "KPPER") @@ -3822,7 +4088,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_keypad_topic_per_entry, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "TCPER") @@ -3832,7 +4097,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_timecontrol_topic_per_entry, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "TCENA") @@ -3842,7 +4106,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_timecontrol_control_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "AUTHPER") @@ -3852,7 +4116,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_auth_topic_per_entry, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "AUTHENA") @@ -3862,7 +4125,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_auth_control_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "PUBAUTH") @@ -3872,7 +4135,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_publish_authdata, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "CREDDIGEST") @@ -3882,7 +4144,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_http_auth_type, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -3895,7 +4157,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_bypass_proxy, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -4279,7 +4541,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_register_as_app, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "REGAPPOPN") @@ -4289,7 +4550,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_register_opener_as_app, (value == "1")); Log->print("Setting changed: "); Log->println(key); - //configChanged = true; } } else if(key == "LOCKENA") @@ -4299,7 +4559,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_lock_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "GEMINIENA") @@ -4316,7 +4576,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else if(key == "OPENA") @@ -4326,17 +4586,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBool(preference_opener_enabled, (value == "1")); Log->print("Setting changed: "); Log->println(key); - configChanged = true; - } - } - else if(key == "CONNMODE") - { - if(_preferences->getBool(preference_connect_mode, true) != (value == "1")) - { - _preferences->putBool(preference_connect_mode, (value == "1")); - Log->print("Setting changed: "); - Log->println(key); - configChanged = true; + restartServicesReconnect = true; } } else if(key == "CREDUSER") @@ -4352,7 +4602,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_user, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -4374,7 +4624,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_totp_secret, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; newMFA = true; _importExport->_sessionsOpts[request->client()->localIP().toString() + "totp"] = true; @@ -4390,7 +4640,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_bypass_secret, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } } @@ -4403,7 +4653,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_admin_secret, value); Log->print("Setting changed: "); Log->println(key); - configChanged = true; } } } @@ -4424,7 +4673,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } else { @@ -4437,7 +4686,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putInt(preference_lock_gemini_pin, value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } else @@ -4448,7 +4697,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _nuki->setPin(value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } } @@ -4461,7 +4710,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _nukiOpener->setPin(0xffff); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } else { @@ -4471,7 +4720,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _nukiOpener->setPin(value.toInt()); Log->print("Setting changed: "); Log->println(key); - configChanged = true; + restartServicesNoReconnect = true; } } } @@ -4551,7 +4800,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S nukiBlePref.end(); Log->print("Setting changed: "); Log->println("Lock pairing data"); - configChanged = true; + restartServicesNoReconnect = true; } if(manPairOpn) @@ -4566,7 +4815,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S nukiBlePref.end(); Log->print("Setting changed: "); Log->println("Opener pairing data"); - configChanged = true; + restartServicesNoReconnect = true; } if(pass1 != "" && pass1 != "*" && pass1 == pass2) @@ -4576,7 +4825,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_password, pass1); Log->print("Setting changed: "); Log->println("CREDPASS"); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -4588,14 +4837,14 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_mqtt_user, ""); Log->print("Setting changed: "); Log->println("MQTTUSER"); - configChanged = true; + restartServicesReconnect = true; } if(_preferences->getString(preference_mqtt_password, "") != "") { _preferences->putString(preference_mqtt_password, ""); Log->print("Setting changed: "); Log->println("MQTTPASS"); - configChanged = true; + restartServicesReconnect = true; } } @@ -4606,7 +4855,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_user, ""); Log->print("Setting changed: "); Log->println("CREDUSER"); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } if(_preferences->getString(preference_cred_password, "") != "") @@ -4614,7 +4863,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putString(preference_cred_password, ""); Log->print("Setting changed: "); Log->println("CREDPASS"); - configChanged = true; + restartServicesNoReconnect = true; clearSession = true; } } @@ -4639,7 +4888,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBytes(preference_acl, (byte*)(&aclPrefs), sizeof(aclPrefs)); Log->print("Setting changed: "); Log->println("ACLPREFS"); - //configChanged = true; break; } } @@ -4650,7 +4898,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); Log->print("Setting changed: "); Log->println("ACLCONFBASICLOCK"); - //configChanged = true; break; } } @@ -4661,7 +4908,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); Log->print("Setting changed: "); Log->println("ACLCONFADVANCEDLOCK"); - //configChanged = true; break; } @@ -4673,7 +4919,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); Log->print("Setting changed: "); Log->println("ACLCONFBASICOPENER"); - //configChanged = true; break; } } @@ -4684,7 +4929,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S _preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); Log->print("Setting changed: "); Log->println("ACLCONFADVANCEDOPENER"); - //configChanged = true; break; } } @@ -4694,21 +4938,34 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S { clearSessions(); } + if(newMFA) { _preferences->putBool(preference_mfa_reconfigure, true); - _importExport->readSettings(); } + if(configChanged) { - message = "Configuration saved, reboot required to apply"; + message = "Configuration saved, reboot required to apply some settings"; _rebootRequired = true; } + else if (restartServicesReconnect) + { + message = "Configuration saved, restart services required to apply some settings"; + _restartServicesRequired = 2; + } + else if (restartServicesNoReconnect) + { + message = "Configuration saved, restart services required to apply some settings"; + _restartServicesRequired = 1; + } else { message = "Configuration saved."; } + _importExport->readSettings(); + _network->readSettings(); if(_nuki != nullptr) { @@ -4807,12 +5064,10 @@ esp_err_t WebCfgServer::buildImportExportHtml(PsychicRequest *request, PsychicRe response.print(""); response.print("

"); response.print("

"); - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - if(esp_psram_get_size() > 0) + if(nuki_hub_https_server_enabled) { response.print("

"); } - #endif response.print("

"); response.print("

"); response.print(""); @@ -4858,11 +5113,11 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp buildHtmlHeader(&response, header); if(_rebootRequired) { - response.print("
REBOOT REQUIRED TO APPLY SETTINGS
"); + response.print("
REBOOT REQUIRED TO APPLY SOME SETTINGS
"); } - if(_preferences->getBool(preference_webserial_enabled, false)) + if(_restartServicesRequired > 0) { - response.print("
WEBSERIAL IS ENABLED, ONLY ENABLE WHEN DEBUGGING AND DISABLE ASAP
"); + response.print("
RESTART SERVICES REQUIRED TO APPLY SOME SETTINGS
"); } #ifdef DEBUG_NUKIHUB response.print("
RUNNING DEBUG BUILD, SWITCH TO RELEASE BUILD ASAP
"); @@ -4936,7 +5191,7 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp } if(_preferences->getBool(preference_webserial_enabled, false)) { - buildNavigationMenuEntry(&response, "Open Webserial", "/get?page=webserial"); + buildNavigationMenuEntry(&response, "Open Webserial", "/webserial"); } #ifndef CONFIG_IDF_TARGET_ESP32H2 if(_allowRestartToPortal) @@ -4945,8 +5200,10 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp } #endif buildNavigationMenuEntry(&response, "Info page", "/get?page=info"); - String rebooturl = "/get?page=reboot&CONFIRMTOKEN=" + _confirmCode; - buildNavigationMenuEntry(&response, "Reboot Nuki Hub", rebooturl.c_str()); + String rebootUrl = "/get?page=reboot&CONFIRMTOKEN=" + _confirmCode; + buildNavigationMenuEntry(&response, "Reboot Nuki Hub", rebootUrl.c_str()); + String restartServicesUrl = "/get?page=restartservices&CONFIRMTOKEN=" + _confirmCode; + buildNavigationMenuEntry(&response, "Restart Services", restartServicesUrl.c_str()); if (_preferences->getInt(preference_http_auth_type, 0) == 2) { buildNavigationMenuEntry(&response, "Logout", "/get?page=logout"); @@ -5113,15 +5370,13 @@ esp_err_t WebCfgServer::buildNetworkConfigHtml(PsychicRequest *request, PsychicR printCheckBox(&response, "RSTDISC", "Restart on disconnect", _preferences->getBool(preference_restart_on_disconnect), ""); printCheckBox(&response, "CHECKUPDATE", "Check for Firmware Updates every 24h", _preferences->getBool(preference_check_updates), ""); printCheckBox(&response, "FINDBESTRSSI", "Find WiFi AP with strongest signal", _preferences->getBool(preference_find_best_rssi, false), ""); - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - if(esp_psram_get_size() > 0) + if(nuki_hub_https_server_enabled) { response.print("Set HTTP SSL Certificate"); response.print("Set HTTP SSL Key"); response.print("Generate self-signed HTTP SSL Certificate and key"); printInputField(&response, "HTTPSFQDN", "Nuki Hub FQDN for HTTP redirect", _preferences->getString(preference_https_fqdn, "").c_str(), 255, ""); } - #endif response.print(""); response.print("

IP Address assignment

"); response.print(""); @@ -5318,7 +5573,6 @@ esp_err_t WebCfgServer::buildHttpSSLConfigHtml(PsychicRequest *request, PsychicR { bool found = false; - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED if (!SPIFFS.begin(true)) { Log->println("SPIFFS Mount Failed"); } @@ -5342,7 +5596,7 @@ esp_err_t WebCfgServer::buildHttpSSLConfigHtml(PsychicRequest *request, PsychicR found = true; } } - #endif + if (!found) { printTextarea(&response, "HTTPCRT", "HTTP SSL Certificate (*, optional)", "", 4400, true, true); @@ -5352,7 +5606,6 @@ esp_err_t WebCfgServer::buildHttpSSLConfigHtml(PsychicRequest *request, PsychicR { bool found = false; - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED if (!SPIFFS.begin(true)) { Log->println("SPIFFS Mount Failed"); } @@ -5376,7 +5629,7 @@ esp_err_t WebCfgServer::buildHttpSSLConfigHtml(PsychicRequest *request, PsychicR found = true; } } - #endif + if (!found) { printTextarea(&response, "HTTPKEY", "HTTP SSL Key (*, optional)", "", 2200, true, true); @@ -5409,7 +5662,7 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic response.print(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Enabled" : "Disabled"); response.print(""); printCheckBox(&response, "DISNTWNOCON", "Disable Network if not connected within 60s", _preferences->getBool(preference_disable_network_not_connected, false), ""); - //printCheckBox(&response, "WEBLOG", "Enable WebSerial logging", _preferences->getBool(preference_webserial_enabled), ""); + printCheckBox(&response, "WEBLOG", "Enable WebSerial logging", _preferences->getBool(preference_webserial_enabled), ""); printCheckBox(&response, "BTLPRST", "Enable Bootloop prevention (Try to reset these settings to default on bootloop)", true, ""); printInputField(&response, "BUFFSIZE", "Char buffer size (min 4096, max 65536)", _preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE), 6, ""); response.print(""); @@ -5784,7 +6037,6 @@ esp_err_t WebCfgServer::buildNukiConfigHtml(PsychicRequest *request, PsychicResp printCheckBox(&response, "LOCKENA", "Nuki Lock enabled", _preferences->getBool(preference_lock_enabled, true), ""); printCheckBox(&response, "GEMINIENA", "Nuki Smartlock Ultra/Go/5th gen enabled", _preferences->getBool(preference_lock_gemini_enabled, false), ""); printCheckBox(&response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled, false), ""); - printCheckBox(&response, "CONNMODE", "New Nuki Bluetooth connection mode (disable if there are connection issues)", _preferences->getBool(preference_connect_mode, true), ""); response.print("
Advised minimum char buffer size based on current settings

"); response.print("

Advanced Nuki Configuration

"); response.print(""); @@ -5933,7 +6185,7 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print(ESP.getFreeHeap()); response.print("\nTotal internal heap: "); response.print(ESP.getHeapSize()); -#ifdef CONFIG_SOC_SPIRAM_SUPPORTED + #if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM) if(esp_psram_get_size() > 0) { response.print("\nPSRAM Available: Yes"); @@ -5950,9 +6202,9 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* { response.print("\nPSRAM Available: No"); } -#else + #else response.print("\nPSRAM Available: No"); -#endif + #endif response.print("\nNetwork task stack high watermark: "); response.print(uxTaskGetStackHighWaterMark(networkTaskHandle)); response.print("\nNuki task stack high watermark: "); @@ -6020,30 +6272,19 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print("\nWeb configurator enabled: "); response.print(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No"); response.print("\nHTTP SSL: "); - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - if(esp_psram_get_size() > 0) - { - if (!SPIFFS.begin(true)) { - response.print("Disabled"); - } - else - { - File file = SPIFFS.open("/http_ssl.crt"); - File file2 = SPIFFS.open("/http_ssl.key"); - response.print((!file || file.isDirectory() || !file2 || file2.isDirectory()) ? "Disabled" : "Enabled"); - file.close(); - file2.close(); - response.print("\nNuki Hub FQDN for HTTP redirect: "); - response.print(_preferences->getString(preference_https_fqdn, "").length() > 0 ? "***" : "Not set"); - } + if (!SPIFFS.begin(true) || !nuki_hub_https_server_enabled) { + response.print("Disabled"); } else { - response.print("Disabled"); + File file = SPIFFS.open("/http_ssl.crt"); + File file2 = SPIFFS.open("/http_ssl.key"); + response.print((!file || file.isDirectory() || !file2 || file2.isDirectory()) ? "Disabled" : "Enabled"); + file.close(); + file2.close(); + response.print("\nNuki Hub FQDN for HTTP redirect: "); + response.print(_preferences->getString(preference_https_fqdn, "").length() > 0 ? "***" : "Not set"); } - #else - response.print("Disabled"); - #endif response.print("\nAdvanced menu enabled: "); response.print(_preferences->getBool(preference_enable_debug_mode, false) ? "Yes" : "No"); response.print("\nPublish free heap over MQTT: "); @@ -6190,8 +6431,6 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print("Disabled"); } response.print("\n\n------------ BLUETOOTH ------------"); - response.print("\nBluetooth connection mode: "); - response.print(_preferences->getBool(preference_connect_mode, true) ? "New" : "Old"); response.print("\nBluetooth TX power (dB): "); response.print(_preferences->getInt(preference_ble_tx_power, 9)); response.print("\nBluetooth command nr of retries: "); @@ -6298,6 +6537,12 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print(_preferences->getInt(preference_lock_max_auth_entry_count, 0)); response.print("\nRegister as: "); response.print(_preferences->getBool(preference_register_as_app, false) ? "App" : "Bridge"); + response.print("\nForce Lock ID: "); + response.print(_preferences->getBool(preference_lock_force_id, false) ? "Yes" : "No"); + response.print("\nForce Lock Keypad: "); + response.print(_preferences->getBool(preference_lock_force_keypad, false) ? "Yes" : "No"); + response.print("\nForce Lock Doorsensor: "); + response.print(_preferences->getBool(preference_lock_force_doorsensor, false) ? "Yes" : "No"); response.print("\n\n------------ HYBRID MODE ------------"); if(!_preferences->getBool(preference_official_hybrid_enabled, false)) { @@ -6320,12 +6565,6 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print("\nTime between status updates when official MQTT is offline (s): "); response.print(_preferences->getInt(preference_query_interval_hybrid_lockstate, 600)); } - response.print("\nForce Lock ID: "); - response.print(_preferences->getBool(preference_lock_force_id, false) ? "Yes" : "No"); - response.print("\nForce Lock Keypad: "); - response.print(_preferences->getBool(preference_lock_force_keypad, false) ? "Yes" : "No"); - response.print("\nForce Lock Doorsensor: "); - response.print(_preferences->getBool(preference_lock_force_doorsensor, false) ? "Yes" : "No"); uint32_t basicLockConfigAclPrefs[16]; _preferences->getBytes(preference_conf_lock_basic_acl, &basicLockConfigAclPrefs, sizeof(basicLockConfigAclPrefs)); uint32_t advancedLockConfigAclPrefs[25]; @@ -6667,7 +6906,7 @@ esp_err_t WebCfgServer::processUnpair(PsychicRequest *request, PsychicResponse* return buildConfirmHtml(request, resp, "Confirm code is invalid.", 3, true); } - esp_err_t res = buildConfirmHtml(request, resp, opener ? "Unpairing Nuki Opener and restarting." : "Unpairing Nuki Lock and restarting.", 3, true); + esp_err_t res = buildConfirmHtml(request, resp, opener ? "Unpairing Nuki Opener." : "Unpairing Nuki Lock.", 3, true); if(!opener && _nuki != nullptr) { @@ -6682,7 +6921,7 @@ esp_err_t WebCfgServer::processUnpair(PsychicRequest *request, PsychicResponse* _network->disableHASS(); waitAndProcess(false, 1000); - restartEsp(RestartReason::DeviceUnpaired); + _network->setRestartServices(true); return res; } @@ -7008,7 +7247,7 @@ const std::vector> WebCfgServer::getNetworkCustomCLKOp { std::vector> options; options.push_back(std::make_pair("0", "GPIO0 IN")); - options.push_back(std::make_pair("1", "GPIO0 OUT")); + options.push_back(std::make_pair("1", "GPIO0 OUT")); options.push_back(std::make_pair("2", "GPIO16 OUT")); options.push_back(std::make_pair("3", "GPIO17 OUT")); return options; @@ -7044,7 +7283,7 @@ const String WebCfgServer::getPreselectionForGpio(const uint8_t &pin) const return String((int8_t)PinRole::Disabled); } -#ifdef CONFIG_SOC_SPIRAM_SUPPORTED +#ifdef NUKI_HUB_HTTPS_SERVER void WebCfgServer::createSSLCertificate() { SSLCert* cert; diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index 4a97f34..08b724e 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -18,6 +18,7 @@ #include "ImportExport.h" extern TaskHandle_t nukiTaskHandle; +extern bool nuki_hub_https_server_enabled; enum class TokenType { @@ -43,6 +44,7 @@ class WebCfgServer public: #ifndef NUKI_HUB_UPDATER WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer, ImportExport* importExport); + void updateWebSerial(); #else WebCfgServer(NukiNetwork* network, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer, ImportExport* importExport); #endif @@ -86,7 +88,7 @@ private: #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32P4) const std::vector> getNetworkCustomCLKOptions() const; #endif - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED + #ifdef NUKI_HUB_HTTPS_SERVER void createSSLCertificate(); #endif const String getPreselectionForGpio(const uint8_t& pin) const; @@ -99,6 +101,7 @@ private: Gpio* _gpio = nullptr; bool _brokerConfigured = false; bool _rebootRequired = false; + int _restartServicesRequired = 0; #endif std::vector _ssidList; @@ -125,6 +128,7 @@ private: esp_err_t buildConfirmHtml(PsychicRequest *request, PsychicResponse* resp, const String &message, uint32_t redirectDelay = 5, bool redirect = false, String redirectTo = "/"); esp_err_t buildOtaHtml(PsychicRequest *request, PsychicResponse* resp, bool debug = false); esp_err_t sendCss(PsychicRequest *request, PsychicResponse* resp); + esp_err_t sendWebSerial(PsychicRequest *request, PsychicResponse* resp); esp_err_t sendFavicon(PsychicRequest *request, PsychicResponse* resp); void createSsidList(); void buildHtmlHeader(PsychicStreamResponse *response, String additionalHeader = ""); @@ -143,7 +147,7 @@ private: NukiNetwork* _network = nullptr; Preferences* _preferences = nullptr; ImportExport* _importExport; - + char _credUser[31] = {0}; char _credPassword[31] = {0}; bool _allowRestartToPortal = false; diff --git a/src/WebCfgServerConstants.h b/src/WebCfgServerConstants.h index 96f5389..f1d19aa 100644 --- a/src/WebCfgServerConstants.h +++ b/src/WebCfgServerConstants.h @@ -1,62 +1,17 @@ #pragma once -// escaped by https://www.cescaper.com/ -// source: https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css - -const char stylecss[] = ":root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000;--nc-tx-2:#1a1a1a;--nc-bg-1:#fff;--nc-bg-2:#f6f8fa;--nc-bg-3:#e5e7eb;--nc-lk-1:#0070f3;--nc-lk-2:#0366d6;--nc-lk-tx:#fff;--nc-ac-1:#79ffe1;--nc-ac-tx:#0c4047}@media(prefers-color-scheme:dark){:root{--nc-tx-1:#fff;--nc-tx-2:#eee;--nc-bg-1:#000;--nc-bg-2:#111;--nc-bg-3:#222;--nc-lk-1:#3291ff;--nc-lk-2:#0070f3;--nc-lk-tx:#fff;--nc-ac-1:#7928ca;--nc-ac-tx:#fff}}*{margin:0;padding:0}img,input,option,p,table,textarea,ul{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:normal;overflow-wrap:anywhere;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2) !important;}abbr{cursor:help}abbr:hover{cursor:help}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr:nth-child(even){background:var(--nc-bg-2)}textarea{max-width:100%}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}td>input{margin-top:0;margin-bottom:0}td>textarea{margin-top:0;margin-bottom:0}td>select{margin-top:0;margin-bottom:0}.warning{color:red}@media only screen and (max-width:600px){.adapt td{display:block}.adapt input[type=text],.adapt input[type=password],.adapt input[type=submit],.adapt textarea,.adapt select{width:100%}.adapt td:has(input[type=checkbox]){text-align:center}.adapt input[type=checkbox]{width:1.5em;height:1.5em}.adapt table td:first-child{border-bottom:0}.adapt table td:last-child{border-top:0}#tblnav a li>span{max-width:140px}}#tblnav a{border:0;border-bottom:1px solid;display:block;font-size:1rem;font-weight:bold;padding:.6rem 0;line-height:1;color:var(--nc-tx-1);text-decoration:none;background:linear-gradient(to left,transparent 50%,rgba(255,255,255,0.4) 50%) right;background-size:200% 100%;transition:all .2s ease}#tblnav a{background:linear-gradient(to left,var(--nc-bg-2) 50%,rgba(255,255,255,0.4) 50%) right;background-size:200% 100%}#tblnav a:hover{background-position:left;transition:all .45s ease}#tblnav a:active{background:var(--nc-lk-1);transition:all .15s ease}#tblnav a li{list-style:none;padding:.5rem;display:inline-block;width:100%}#tblnav a li>span{float:right;text-align:right;margin-right:10px;color:#f70;font-weight:100;font-style:italic;display:block}.tdbtn{text-align:center;vertical-align:middle}.naventry{float:left;max-width:375px;width:100%}"; - -// converted to char array by https://notisrac.github.io/FileToCArray/ -const uint8_t favicon_32x32[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7a, 0x7a, - 0xf4, 0x00, 0x00, 0x02, 0xfb, 0x49, 0x44, 0x41, 0x54, 0x58, 0x47, 0xcd, 0x97, 0x4b, 0x4c, 0x53, - 0x41, 0x14, 0x86, 0xff, 0x69, 0xa5, 0x3c, 0x8a, 0x12, 0x14, 0x84, 0xa6, 0x96, 0x60, 0x6d, 0xd0, - 0x50, 0x14, 0x51, 0xd1, 0x6a, 0x4c, 0x51, 0x63, 0x88, 0x8a, 0x06, 0x63, 0x42, 0xc0, 0x85, 0x01, - 0xc3, 0xae, 0xe8, 0x0a, 0x0d, 0x4a, 0x30, 0x31, 0x18, 0xd1, 0x84, 0x18, 0x83, 0x46, 0x5d, 0xa9, - 0x09, 0xb8, 0x01, 0xa2, 0x31, 0x82, 0x31, 0x82, 0x6e, 0x48, 0x78, 0xb6, 0x74, 0xe1, 0x8b, 0x47, - 0xb0, 0x3e, 0x0a, 0xb4, 0x05, 0xa1, 0xe1, 0x25, 0x14, 0x68, 0xeb, 0x9d, 0x22, 0xb7, 0x40, 0x1f, - 0x1b, 0xef, 0xa5, 0xce, 0xea, 0xde, 0x99, 0xce, 0x39, 0xdf, 0xfc, 0x7f, 0x67, 0xee, 0x1c, 0xe2, - 0xbc, 0x0e, 0x81, 0xe3, 0x84, 0xba, 0x58, 0x40, 0x88, 0xc6, 0x09, 0x48, 0xb0, 0x0a, 0x8d, 0x00, - 0x26, 0x07, 0x71, 0x3e, 0x14, 0xd4, 0x37, 0x95, 0x11, 0x7b, 0x87, 0xba, 0x84, 0x10, 0x72, 0x63, - 0x15, 0xf2, 0x7a, 0xa4, 0x70, 0x12, 0xe7, 0x35, 0xe2, 0xe8, 0x48, 0x33, 0x81, 0x20, 0x36, 0x10, - 0x00, 0x4c, 0x4e, 0x33, 0x71, 0x68, 0xd3, 0x18, 0xe5, 0x03, 0xd7, 0xfe, 0x6f, 0x80, 0x86, 0xb6, - 0x51, 0xd4, 0xbe, 0x1b, 0x66, 0xe5, 0xd1, 0x64, 0x49, 0x91, 0xb2, 0x35, 0xdc, 0xaf, 0x5c, 0x25, - 0x8f, 0xbe, 0xc1, 0x36, 0xeb, 0x60, 0x7f, 0x93, 0x79, 0x28, 0x0a, 0x07, 0x93, 0x23, 0x7c, 0xce, - 0xf1, 0xab, 0xc0, 0x9d, 0x67, 0x46, 0x5c, 0xae, 0x30, 0xb0, 0x93, 0x93, 0xb6, 0x88, 0xa1, 0xad, - 0xdc, 0x85, 0x60, 0x91, 0xc0, 0x67, 0xc0, 0xc8, 0xc3, 0xcd, 0x18, 0x9b, 0x9c, 0x67, 0xc7, 0x2b, - 0x2e, 0x29, 0x70, 0x31, 0x5b, 0xca, 0x0d, 0x00, 0x8d, 0x72, 0xf5, 0x7c, 0x1c, 0x6e, 0x6a, 0x36, - 0xfb, 0x0c, 0xb8, 0xe1, 0x48, 0x33, 0xac, 0x13, 0x3c, 0x02, 0xac, 0x11, 0x12, 0xb4, 0x3c, 0x49, - 0xc1, 0x9e, 0xc4, 0xb5, 0x5e, 0x21, 0xa2, 0x8f, 0xb6, 0x60, 0x64, 0x6c, 0x8e, 0x3f, 0x05, 0x68, - 0x64, 0xa5, 0x5c, 0x0c, 0x5d, 0x95, 0x77, 0x2b, 0x62, 0xd2, 0x5b, 0x30, 0x6c, 0xe5, 0x19, 0xc0, - 0x9f, 0x15, 0x92, 0x63, 0xad, 0xb0, 0x8c, 0xcc, 0xf2, 0xab, 0x00, 0x8d, 0x4e, 0xad, 0x68, 0x66, - 0xac, 0x48, 0x5d, 0x61, 0xc5, 0xa6, 0xe3, 0xad, 0x18, 0xfc, 0xc5, 0x13, 0xc0, 0x3a, 0xb1, 0x10, - 0xe3, 0x53, 0x76, 0x76, 0x75, 0x4a, 0x79, 0x18, 0x63, 0xc5, 0xee, 0x65, 0xbb, 0x22, 0x2e, 0xa3, - 0x0d, 0xfd, 0x43, 0x36, 0x7e, 0x14, 0x50, 0xa7, 0x44, 0xb8, 0x92, 0x35, 0xb6, 0x5b, 0xd9, 0x04, - 0x57, 0xf2, 0x64, 0x28, 0x2b, 0x90, 0xb3, 0xef, 0xf1, 0xa7, 0xda, 0xf0, 0xd3, 0xcc, 0x13, 0x00, - 0x3d, 0x84, 0x6a, 0x6e, 0x27, 0x62, 0x47, 0x8e, 0x0e, 0xd3, 0xb6, 0x85, 0xc3, 0xc6, 0x65, 0xc5, - 0x63, 0xc6, 0x0a, 0xe5, 0xc2, 0xae, 0x90, 0x67, 0xb6, 0xe3, 0xfb, 0xe0, 0x0c, 0x3f, 0x0a, 0x50, - 0xc9, 0x3f, 0x56, 0xa7, 0xa2, 0xbc, 0xd2, 0x88, 0xa2, 0xfb, 0xee, 0x03, 0x2a, 0xf1, 0xaf, 0x15, - 0x21, 0x8c, 0x3a, 0x8a, 0xd3, 0xed, 0x30, 0x0c, 0xf0, 0x04, 0xb0, 0x2d, 0x3e, 0x0c, 0x5f, 0x6a, - 0x53, 0x31, 0x6f, 0x77, 0x42, 0x95, 0xab, 0x87, 0xbe, 0x67, 0x92, 0x5d, 0x69, 0x51, 0xae, 0x0c, - 0xb7, 0x2e, 0xc8, 0x91, 0x70, 0xa6, 0x03, 0x7d, 0xc6, 0x69, 0x7e, 0x14, 0x48, 0x88, 0x0b, 0x45, - 0xf7, 0xf3, 0xbd, 0xae, 0xe0, 0xfa, 0xee, 0x49, 0xa8, 0xf2, 0xf4, 0x2e, 0x18, 0xda, 0x84, 0x02, - 0xba, 0x2b, 0x76, 0x22, 0xbf, 0xb4, 0x17, 0x9f, 0x0d, 0x53, 0xfc, 0x00, 0x28, 0x64, 0xa1, 0xe8, - 0x7d, 0xb1, 0x00, 0x40, 0x5b, 0xd1, 0x3d, 0x03, 0xca, 0xab, 0x8c, 0xec, 0xfb, 0x76, 0x85, 0x18, - 0xa2, 0x20, 0x01, 0x3a, 0xbb, 0x26, 0xf8, 0x01, 0x90, 0x4b, 0x43, 0xd0, 0xf7, 0x72, 0x1f, 0x1b, - 0xfc, 0xf7, 0x8c, 0x1d, 0xc9, 0x67, 0x3b, 0xf1, 0xb5, 0xdf, 0x2d, 0x39, 0x23, 0x04, 0x1c, 0x4b, - 0x6e, 0x18, 0x9c, 0x7e, 0x8c, 0xe2, 0x25, 0x21, 0x30, 0xbc, 0x72, 0x03, 0x50, 0x92, 0xf7, 0x5a, - 0x2b, 0xd2, 0x35, 0x1f, 0xe0, 0xeb, 0x56, 0xc3, 0x29, 0x80, 0x2c, 0x36, 0x18, 0x3f, 0xea, 0x54, - 0xac, 0x02, 0x8b, 0x0f, 0xf9, 0xa5, 0x3d, 0x78, 0x5a, 0x67, 0xf6, 0xe8, 0xa7, 0x1d, 0x9c, 0x02, - 0x48, 0x37, 0x06, 0xc3, 0xf8, 0xda, 0x13, 0x60, 0x74, 0x7c, 0x0e, 0xca, 0x2c, 0x1d, 0x2c, 0xa3, - 0xee, 0x23, 0x78, 0x91, 0x86, 0x53, 0x00, 0x49, 0x94, 0x08, 0x03, 0x6f, 0xf6, 0x7b, 0x5d, 0x69, - 0x4d, 0xe3, 0x10, 0x72, 0x8a, 0xbb, 0x3c, 0xc6, 0x38, 0x05, 0x88, 0x59, 0x2f, 0x82, 0xe9, 0xad, - 0x77, 0x00, 0x9a, 0x39, 0xb3, 0xf0, 0x13, 0xea, 0x9a, 0x46, 0x96, 0x41, 0x70, 0x0a, 0x10, 0x1d, - 0x19, 0x04, 0x4b, 0xc3, 0x01, 0xaf, 0x0a, 0xd0, 0xce, 0x7e, 0x8b, 0x0d, 0x49, 0xd9, 0xda, 0x65, - 0x1f, 0xac, 0x7f, 0x02, 0x58, 0x79, 0x29, 0x0d, 0x0f, 0x15, 0xe2, 0x6e, 0xa1, 0xc2, 0x27, 0x00, - 0x1d, 0xa8, 0x66, 0xac, 0xa8, 0x5f, 0xa2, 0xc2, 0xb9, 0x8c, 0x58, 0xa4, 0xab, 0x22, 0x7d, 0xce, - 0xf9, 0xbf, 0xaf, 0xe5, 0x7e, 0x97, 0xca, 0xd1, 0x20, 0x55, 0xc0, 0xc4, 0xc4, 0x0a, 0x5c, 0x69, - 0x66, 0xd7, 0x31, 0xc5, 0xa9, 0x33, 0x30, 0xc5, 0x29, 0xa1, 0xc5, 0xa9, 0xab, 0x3c, 0x3f, 0xa9, - 0x2e, 0x66, 0x20, 0x0a, 0x56, 0x51, 0x09, 0x33, 0x93, 0xfc, 0x01, 0xf3, 0x6f, 0x2d, 0xfb, 0x03, - 0xed, 0x06, 0xb0, 0xce, 0xb5, 0xc4, 0xb4, 0x59, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, - 0xae, 0x42, 0x60, 0x82 +// converted to char array by bin2array +const char stylecss[] = { + #include "webServerConstants/style.h" }; + +// converted to char array by bin2array +const uint8_t favicon_32x32[] = { + #include "webServerConstants/favicon-32x32.h" +}; + +#ifndef NUKI_HUB_UPDATER +const uint8_t WEBSERIAL_HTML[] = { + #include "webServerConstants/webSerial.h" +}; +#endif \ No newline at end of file diff --git a/src/idf_component.yml b/src/idf_component.yml index dc50d1f..269b8c2 100644 --- a/src/idf_component.yml +++ b/src/idf_component.yml @@ -1,20 +1,22 @@ dependencies: # Required IDF version - idf: ">=5.2" + idf: ">=5.5" esp-nimble-cpp: git: https://github.com/h2zero/esp-nimble-cpp.git - version: 2.3.0 - + version: 8af38e7eb9ae779bf54708c029f33a875d5e8d62 + espressif/libsodium: "^1.0.20~2" espressif/esp_hosted: version: "*" + #override_path: "../resources/espressif__esp_hosted" rules: - if: "target in [esp32p4]" espressif/esp_wifi_remote: version: "*" + #override_path: "../resources/espressif__esp_wifi_remote" rules: - if: "target in [esp32p4]" diff --git a/src/main.cpp b/src/main.cpp index ae1692e..24505f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,10 @@ #include "esp_http_client.h" #include "esp_https_ota.h" #include "esp_task_wdt.h" +#ifdef CONFIG_HEAP_TASK_TRACKING +#include "esp_heap_task_info.h" +#include "esp_heap_caps.h" +#endif #include "Config.h" #include "esp32-hal-log.h" #include "hal/wdt_hal.h" @@ -15,7 +19,12 @@ #include "FS.h" #include "SPIFFS.h" //#include -#ifdef CONFIG_SOC_SPIRAM_SUPPORTED +#ifdef NUKI_HUB_HTTPS_SERVER +bool nuki_hub_https_server_enabled = true; +#else +bool nuki_hub_https_server_enabled = false; +#endif +#if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM) #include "esp_psram.h" #endif @@ -25,6 +34,7 @@ #include "NukiNetworkLock.h" #include "NukiOpenerWrapper.h" #include "Gpio.h" +#include "Gpio.h" #include "CharBuffer.h" #include "NukiDeviceId.h" #include "WebCfgServer.h" @@ -35,13 +45,6 @@ #include "NimBLEDevice.h" #include "ImportExport.h" -/* -#ifdef DEBUG_NUKIHUB -#include -#include -#endif -*/ - NukiNetworkLock* networkLock = nullptr; NukiNetworkOpener* networkOpener = nullptr; BleScanner::Scanner* bleScanner = nullptr; @@ -53,10 +56,14 @@ NukiDeviceId* deviceIdOpener = nullptr; Gpio* gpio = nullptr; SerialReader* serialReader = nullptr; +bool bleDone = false; bool lockEnabled = false; bool openerEnabled = false; bool wifiConnected = false; bool rebootLock = false; +uint8_t lockRestartControllerCount = 0; +uint8_t openerRestartControllerCount = 0; +char16_t buffer_size = CHAR_BUFFER_SIZE; TaskHandle_t nukiTaskHandle = nullptr; @@ -78,7 +85,10 @@ int64_t restartTs = 10 * 60 * 1000; char log_print_buffer[1024]; PsychicHttpServer* psychicServer = nullptr; +PsychicHttpServer* psychicServerRedirect = nullptr; PsychicHttpsServer* psychicSSLServer = nullptr; +PsychicWebSocketHandler* websocketHandler = nullptr; + NukiNetwork* network = nullptr; WebCfgServer* webCfgServer = nullptr; WebCfgServer* webCfgServerSSL = nullptr; @@ -95,10 +105,16 @@ RTC_NOINIT_ATTR bool forceEnableWebServer; RTC_NOINIT_ATTR bool disableNetwork; RTC_NOINIT_ATTR bool wifiFallback; RTC_NOINIT_ATTR bool ethCriticalFailure; + bool coredumpPrinted = true; bool timeSynced = false; bool webStarted = false; bool webSSLStarted = false; +bool lockStarted = false; +bool openerStarted = false; +bool bleScannerStarted = false; +bool webSerialEnabled = false; +uint8_t partitionType = -1; int lastHTTPeventId = -1; bool doOta = false; @@ -176,11 +192,15 @@ uint8_t checkPartition() Log->print("Partition subtype: "); Log->println(running_partition->subtype); + + #if !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32P4) if(running_partition->size == 1966080) { return 0; //OLD PARTITION TABLE } - else if(running_partition->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0) + #endif + + if(running_partition->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0) { return 1; //NEW PARTITION TABLE, RUNNING MAIN APP } @@ -232,6 +252,286 @@ void cbSyncTime(struct timeval *tv) { timeSynced = true; } +#ifndef NUKI_HUB_UPDATER +void startWebServer() +{ + bool failed = true; + + webSerialEnabled = preferences->getBool(preference_webserial_enabled, false); + + if (!nuki_hub_https_server_enabled) + { + Log->println("Not running on PSRAM enabled device"); + } + else + { + if (!SPIFFS.begin(true)) + { + Log->println("SPIFFS Mount Failed"); + } + else + { + File file = SPIFFS.open("/http_ssl.crt"); + if (!file || file.isDirectory()) { + Log->println("http_ssl.crt not found"); + } + else + { + Log->println("Reading http_ssl.crt"); + size_t filesize = file.size(); + char cert[filesize + 1]; + + file.read((uint8_t *)cert, sizeof(cert)); + file.close(); + cert[filesize] = '\0'; + + File file2 = SPIFFS.open("/http_ssl.key"); + if (!file2 || file2.isDirectory()) + { + Log->println("http_ssl.key not found"); + } + else + { + Log->println("Reading http_ssl.key"); + size_t filesize2 = file2.size(); + char key[filesize2 + 1]; + + file2.read((uint8_t *)key, sizeof(key)); + file2.close(); + key[filesize2] = '\0'; + + psychicServerRedirect = new PsychicHttpServer(); + psychicServerRedirect->config.ctrl_port = 20424; + psychicServerRedirect->onNotFound([](PsychicRequest* request, PsychicResponse* response) { + String url = "https://" + request->host() + request->url(); + if (preferences->getString(preference_https_fqdn, "") != "") + { + url = "https://" + preferences->getString(preference_https_fqdn) + request->url(); + } + + response->setCode(301); + response->addHeader("Cache-Control", "no-cache"); + return response->redirect(url.c_str()); + }); + psychicServerRedirect->begin(); + psychicSSLServer = new PsychicHttpsServer; + psychicSSLServer->ssl_config.httpd.max_open_sockets = 8; + psychicSSLServer->setCertificate(cert, key); + psychicSSLServer->config.stack_size = HTTPD_TASK_SIZE; + webCfgServerSSL = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer, importExport); + webCfgServerSSL->initialize(); + psychicSSLServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) { + return response->redirect("/"); + }); + psychicSSLServer->begin(); + webSSLStarted = true; + failed = false; + } + } + } + } + + if (failed) + { + psychicServer = new PsychicHttpServer; + psychicServer->config.stack_size = HTTPD_TASK_SIZE; + webCfgServer = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer, importExport); + webCfgServer->initialize(); + psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) { + return response->redirect("/"); + }); + psychicServer->begin(); + webStarted = true; + } +} + +void startNuki(bool lock) +{ + if (lock) + { + nukiOfficial = new NukiOfficial(preferences); + networkLock = new NukiNetworkLock(network, nukiOfficial, preferences, CharBuffer::get(), buffer_size); + + if(!disableNetwork) + { + networkLock->initialize(); + } + + lockStarted = true; + } + else + { + networkOpener = new NukiNetworkOpener(network, preferences, CharBuffer::get(), buffer_size); + + if(!disableNetwork) + { + networkOpener->initialize(); + } + + openerStarted = true; + } +} + +void restartServices(bool reconnect) +{ + bleDone = false; + lockEnabled = preferences->getBool(preference_lock_enabled); + openerEnabled = preferences->getBool(preference_opener_enabled); + importExport->readSettings(); + network->readSettings(); + gpio->setPins(); + + if (reconnect) + { + network->reconnect(true); + } + + if(webSSLStarted) + { + Log->println("Reset Psychic SSL server"); + psychicSSLServer->reset(); + Log->println("Reset Psychic SSL server done"); + Log->println("Deleting Psychic SSL server"); + delete psychicSSLServer; + psychicSSLServer = nullptr; + Log->println("Deleting Psychic SSL server done"); + } + + if(webStarted) + { + Log->println("Reset Psychic server"); + psychicServer->reset(); + Log->println("Reset Psychic server done"); + Log->println("Deleting Psychic server"); + delete psychicServer; + psychicServer = nullptr; + Log->println("Deleting Psychic server done"); + } + + if(webStarted || webSSLStarted) + { + Log->println("Deleting webCfgServer"); + delete webCfgServer; + webCfgServer = nullptr; + Log->println("Deleting webCfgServer done"); + } + + if(lockStarted) + { + Log->println("Deleting nuki"); + delete nuki; + nuki = nullptr; + if (reconnect) + { + lockStarted = false; + delete networkLock; + networkLock = nullptr; + delete nukiOfficial; + nukiOfficial = nullptr; + } + Log->println("Deleting nuki done"); + } + + if(openerStarted) + { + Log->println("Deleting nukiOpener"); + delete nukiOpener; + nukiOpener = nullptr; + if (reconnect) + { + openerStarted = false; + delete networkOpener; + networkOpener = nullptr; + } + Log->println("Deleting nukiOpener done"); + } + + if (bleScannerStarted) + { + bleScannerStarted = false; + Log->println("Destroying scanner from main"); + delete bleScanner; + Log->println("Scanner deleted"); + bleScanner = nullptr; + Log->println("Scanner nulled from main"); + } + + if (BLEDevice::isInitialized()) { + Log->println("Deinit BLE device"); + BLEDevice::deinit(false); + Log->println("Deinit BLE device done"); + } + + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(2000 / portTICK_PERIOD_MS); + + if(lockEnabled || openerEnabled) + { + Log->println("Restarting BLE Scanner"); + bleScanner = new BleScanner::Scanner(); + bleScanner->initialize("NukiHub", true, 40, 40); + bleScanner->setScanDuration(0); + bleScannerStarted = true; + Log->println("Restarting BLE Scanner done"); + } + + if(lockEnabled) + { + Log->println("Restarting Nuki lock"); + + if (reconnect) + { + startNuki(true); + } + + nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences, CharBuffer::get(), buffer_size); + nuki->initialize(); + bleScanner->whitelist(nuki->getBleAddress()); + Log->println("Restarting Nuki lock done"); + } + + if(openerEnabled) + { + Log->println("Restarting Nuki opener"); + + if (reconnect) + { + startNuki(false); + } + + nukiOpener = new NukiOpenerWrapper("NukiHub", deviceIdOpener, bleScanner, networkOpener, gpio, preferences, CharBuffer::get(), buffer_size); + nukiOpener->initialize(); + bleScanner->whitelist(nukiOpener->getBleAddress()); + Log->println("Restarting Nuki opener done"); + } + + + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(2000 / portTICK_PERIOD_MS); + bleDone = true; + + if(webStarted || webSSLStarted) + { + Log->println("Restarting web server"); + startWebServer(); + Log->println("Restarting web server done"); + } + else if(!doOta && !disableNetwork && (forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true) || preferences->getBool(preference_webserial_enabled, false))) + { + if(forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true)) + { + Log->println("Starting web server"); + startWebServer(); + Log->println("Starting web server done"); + } + } +} +#endif + void networkTask(void *pvParameters) { int64_t networkLoopTs = 0; @@ -259,6 +559,10 @@ void networkTask(void *pvParameters) } #endif network->update(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(50 / portTICK_PERIOD_MS); bool connected = network->isConnected(); if(connected && reroute) @@ -267,7 +571,7 @@ void networkTask(void *pvParameters) { esp_netif_sntp_start(); } - + /* MDNS currently disabled for causing issues (9.10 / 2025-04-01) if(webSSLStarted) { if (MDNS.begin(preferences->getString(preference_hostname, "nukihub").c_str())) { @@ -287,15 +591,44 @@ void networkTask(void *pvParameters) #ifndef NUKI_HUB_UPDATER wifiConnected = network->wifiConnected(); + int restartServ = network->getRestartServices(); - if(connected && lockEnabled) + if (restartServ == 1) { - rebootLock = networkLock->update(); + restartServices(false); } - - if(connected && openerEnabled) + else if (restartServ == 2) { - networkOpener->update(); + restartServices(true); + } + else + { + if(connected && webSerialEnabled && (webSSLStarted || webStarted)) + { + webCfgServerSSL->updateWebSerial(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(50 / portTICK_PERIOD_MS); + } + + if(connected && lockStarted) + { + rebootLock = networkLock->update(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(50 / portTICK_PERIOD_MS); + } + + if(connected && openerStarted) + { + networkOpener->update(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(50 / portTICK_PERIOD_MS); + } } #endif @@ -307,7 +640,7 @@ void networkTask(void *pvParameters) if(espMillis() > restartTs) { - uint8_t partitionType = checkPartition(); + partitionType = checkPartition(); if(partitionType!=1) { @@ -316,40 +649,104 @@ void networkTask(void *pvParameters) restartEsp(RestartReason::RestartTimer); } - esp_task_wdt_reset(); + + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(50 / portTICK_PERIOD_MS); } } #ifndef NUKI_HUB_UPDATER +#ifdef CONFIG_HEAP_TASK_TRACKING +static void print_all_tasks_info(void) +{ + heap_all_tasks_stat_t tasks_stat; + /* call API to dynamically allocate the memory necessary to store the + * information collected while calling heap_caps_get_all_task_stat */ + const esp_err_t ret_val = heap_caps_alloc_all_task_stat_arrays(&tasks_stat); + assert(ret_val == ESP_OK); + + /* collect the information */ + heap_caps_get_all_task_stat(&tasks_stat); + + /* process the information retrieved */ + Log->printf("\n--------------------------------------------------------------------------------\n"); + Log->printf("PRINTING ALL TASKS INFO\n"); + Log->printf("--------------------------------------------------------------------------------\n"); + for (size_t task_idx = 0; task_idx < tasks_stat.task_count; task_idx++) { + task_stat_t task_stat = tasks_stat.stat_arr[task_idx]; + Log->printf("%s: %s: Peak Usage %" PRIu16 ", Current Usage %" PRIu16 "\n", task_stat.name, + task_stat.is_alive ? "ALIVE " : "DELETED", + task_stat.overall_peak_usage, + task_stat.overall_current_usage); + + for (size_t heap_idx = 0; heap_idx < task_stat.heap_count; heap_idx++) { + heap_stat_t heap_stat = task_stat.heap_stat[heap_idx]; + Log->printf(" %s: Caps: %" PRIu32 ". Size %" PRIu16 ", Current Usage %" PRIu16 ", Peak Usage %" PRIu16 ", alloc count %" PRIu16 "\n", heap_stat.name, + heap_stat.caps, + heap_stat.size, + heap_stat.current_usage, + heap_stat.peak_usage, + heap_stat.alloc_count); + + for (size_t alloc_idx = 0; alloc_idx < heap_stat.alloc_count; alloc_idx++) { + heap_task_block_t alloc_stat = heap_stat.alloc_stat[alloc_idx]; + Log->printf(" %p: Size: %" PRIu32 "\n", alloc_stat.address, alloc_stat.size); + } + } + } + + /* delete the memory dynamically allocated while calling heap_caps_alloc_all_task_stat_arrays */ + heap_caps_free_all_task_stat_arrays(&tasks_stat); +} +#endif void nukiTask(void *pvParameters) { + esp_task_wdt_add(NULL); + if (preferences->getBool(preference_mqtt_ssl_enabled, false)) { - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED + #if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM) if (esp_psram_get_size() <= 0) { Log->println("Waiting 20 seconds to start BLE because of MQTT SSL"); - delay(20000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(20000 / portTICK_PERIOD_MS); } #else Log->println("Waiting 20 seconds to start BLE because of MQTT SSL"); - delay(20000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(20000 / portTICK_PERIOD_MS); #endif } int64_t nukiLoopTs = 0; bool whiteListed = false; while(true) { - if(disableNetwork || wifiConnected) + if((disableNetwork || wifiConnected) && bleDone) { - bleScanner->update(); - delay(20); - - bool needsPairing = (lockEnabled && !nuki->isPaired()) || (openerEnabled && !nukiOpener->isPaired()); + if(bleScannerStarted) + { + bleScanner->update(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(20 / portTICK_PERIOD_MS); + } + + bool needsPairing = (lockStarted && !nuki->isPaired()) || (openerStarted && !nukiOpener->isPaired()); if (needsPairing) { - delay(2500); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(2500 / portTICK_PERIOD_MS); } else if (!whiteListed) { @@ -364,14 +761,70 @@ void nukiTask(void *pvParameters) } } - if(lockEnabled) + if(lockStarted) { - nuki->update(rebootLock); - rebootLock = false; + if (nuki->restartController() > 0) + { + if (lockRestartControllerCount > 3) + { + if (nuki->restartController() == 1) + { + restartEsp(RestartReason::BLEError); + } + else if (nuki->restartController() == 2) + { + restartEsp(RestartReason::BLEBeaconWatchdog); + } + } + else + { + lockRestartControllerCount += 1; + restartServices(false); + continue; + } + } + else + { + if (lockRestartControllerCount > 0 && nuki->hasConnected()) + { + lockRestartControllerCount = 0; + } + + nuki->update(rebootLock); + rebootLock = false; + } } - if(openerEnabled) + if(openerStarted) { - nukiOpener->update(); + if (nukiOpener->restartController() > 0) + { + if (openerRestartControllerCount > 3) + { + if (nukiOpener->restartController() == 1) + { + restartEsp(RestartReason::BLEError); + } + else if (nukiOpener->restartController() == 2) + { + restartEsp(RestartReason::BLEBeaconWatchdog); + } + } + else + { + openerRestartControllerCount += 1; + restartServices(false); + continue; + } + } + else + { + if (openerRestartControllerCount > 0 && nukiOpener->hasConnected()) + { + openerRestartControllerCount = 0; + } + + nukiOpener->update(); + } } } @@ -380,8 +833,10 @@ void nukiTask(void *pvParameters) Log->println("nukiTask is running"); nukiLoopTs = espMillis(); } - - esp_task_wdt_reset(); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(50 / portTICK_PERIOD_MS); } } @@ -472,7 +927,9 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt) void otaTask(void *pvParameter) { - uint8_t partitionType = checkPartition(); + esp_task_wdt_add(NULL); + + partitionType = checkPartition(); String updateUrl; if(partitionType==1) @@ -525,12 +982,17 @@ void otaTask(void *pvParameter) { Log->println("Firmware upgrade failed, retrying in 5 seconds"); retryCount++; - esp_task_wdt_reset(); - delay(5000); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(5000 / portTICK_PERIOD_MS); continue; } while (1) { + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } vTaskDelay(1000 / portTICK_PERIOD_MS); } } @@ -545,7 +1007,7 @@ void setupTasks(bool ota) esp_task_wdt_config_t twdt_config = { .timeout_ms = 300000, - .idle_core_mask = 0, + .idle_core_mask = (1 << CONFIG_FREERTOS_NUMBER_OF_CORES) - 1, .trigger_panic = true, }; esp_task_wdt_reconfigure(&twdt_config); @@ -559,20 +1021,17 @@ void setupTasks(bool ota) if(ota) { xTaskCreatePinnedToCore(otaTask, "ota", 8192, NULL, 2, &otaTaskHandle, (espCores > 1) ? 1 : 0); - esp_task_wdt_add(otaTaskHandle); } else { if(!disableNetwork) { xTaskCreatePinnedToCore(networkTask, "ntw", preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), NULL, 3, &networkTaskHandle, (espCores > 1) ? 1 : 0); - esp_task_wdt_add(networkTaskHandle); } #ifndef NUKI_HUB_UPDATER if(!network->isApOpen() && (lockEnabled || openerEnabled)) { xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 0); - esp_task_wdt_add(nukiTaskHandle); } #endif } @@ -581,7 +1040,10 @@ void setupTasks(bool ota) void logCoreDump() { coredumpPrinted = false; - delay(500); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(500 / portTICK_PERIOD_MS); Log->println("Printing coredump and saving to coredump.hex on SPIFFS"); size_t size = 0; size_t address = 0; @@ -670,6 +1132,15 @@ void logCoreDump() void setup() { + #if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM) + #ifndef FORCE_NUKI_HUB_HTTPS_SERVER + if(esp_psram_get_size() <= 0) + { + nuki_hub_https_server_enabled = false; + } + #endif + #endif + //Set Log level to error for all TAGS esp_log_level_set("*", ESP_LOG_ERROR); //Set Log level to none for mqtt TAG @@ -678,14 +1149,13 @@ void setup() Serial.begin(115200); Log = &Serial; -#ifndef NUKI_HUB_UPDATER - // + #if !defined(NUKI_HUB_UPDATER) && !defined(CONFIG_IDF_TARGET_ESP32C5) stdout = funopen(NULL, NULL, &write_fn, NULL, NULL); static char linebuf[1024]; setvbuf(stdout, linebuf, _IOLBF, sizeof(linebuf)); esp_rom_install_channel_putc(1, &ets_putc_handler); //ets_install_putc1(&ets_putc_handler); -#endif + #endif preferences = new Preferences(); preferences->begin("nukihub", false); @@ -694,8 +1164,8 @@ void setup() if(esp_reset_reason() == esp_reset_reason_t::ESP_RST_PANIC || esp_reset_reason() == esp_reset_reason_t::ESP_RST_INT_WDT || - esp_reset_reason() == esp_reset_reason_t::ESP_RST_TASK_WDT || - esp_reset_reason() == esp_reset_reason_t::ESP_RST_WDT) + esp_reset_reason() == esp_reset_reason_t::ESP_RST_TASK_WDT) + //|| esp_reset_reason() == esp_reset_reason_t::ESP_RST_WDT) { logCoreDump(); } @@ -705,7 +1175,7 @@ void setup() listDir(SPIFFS, "/", 1); } - uint8_t partitionType = checkPartition(); + partitionType = checkPartition(); //default disableNetwork RTC_ATTR to false on power-on if(espRunning != 1) @@ -749,24 +1219,20 @@ void setup() if(!doOta) { - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - bool failed = false; + bool failed = true; - if (esp_psram_get_size() <= 0) { - Log->println("Not running on PSRAM enabled device"); - failed = true; + if (!nuki_hub_https_server_enabled) { + Log->println("Not running on HTTPS server enabled device"); } else { if (!SPIFFS.begin(true)) { Log->println("SPIFFS Mount Failed"); - failed = true; } else { File file = SPIFFS.open("/http_ssl.crt"); if (!file || file.isDirectory()) { - failed = true; Log->println("http_ssl.crt not found"); } else @@ -781,7 +1247,6 @@ void setup() File file2 = SPIFFS.open("/http_ssl.key"); if (!file2 || file2.isDirectory()) { - failed = true; Log->println("http_ssl.key not found"); } else @@ -819,6 +1284,7 @@ void setup() }); psychicSSLServer->begin(); webSSLStarted = true; + failed = false; } } } @@ -826,7 +1292,6 @@ void setup() if (failed) { - #endif psychicServer = new PsychicHttpServer; psychicServer->config.stack_size = HTTPD_TASK_SIZE; webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer, importExport); @@ -836,9 +1301,7 @@ void setup() }); psychicServer->begin(); webStarted = true; - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED } - #endif } #else if(preferences->getBool(preference_enable_bootloop_reset, false)) @@ -850,7 +1313,7 @@ void setup() Log->println(NUKI_HUB_VERSION); Log->print("Nuki Hub build "); Log->println(NUKI_HUB_BUILD); - + uint32_t devIdOpener = preferences->getUInt(preference_device_id_opener); deviceIdLock = new NukiDeviceId(preferences, preference_device_id_lock); @@ -861,7 +1324,7 @@ void setup() deviceIdOpener->assignId(deviceIdLock->get()); } - char16_t buffer_size = preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE); + buffer_size = preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE); CharBuffer::initialize(buffer_size); gpio = new Gpio(preferences); @@ -869,11 +1332,9 @@ void setup() gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r"); Log->print(gpioDesc.c_str()); - const String mqttLockPath = preferences->getString(preference_mqtt_lock_path); - importExport = new ImportExport(preferences); - network = new NukiNetwork(preferences, gpio, mqttLockPath, CharBuffer::get(), buffer_size, importExport); + network = new NukiNetwork(preferences, gpio, CharBuffer::get(), buffer_size, importExport); network->initialize(); lockEnabled = preferences->getBool(preference_lock_enabled); @@ -897,18 +1358,13 @@ void setup() // https://developer.nuki.io/t/bluetooth-specification-questions/1109/27 bleScanner->initialize("NukiHub", true, 40, 40); bleScanner->setScanDuration(0); + bleScannerStarted = true; } Log->println(lockEnabled ? F("Nuki Lock enabled") : F("Nuki Lock disabled")); if(lockEnabled) { - nukiOfficial = new NukiOfficial(preferences); - networkLock = new NukiNetworkLock(network, nukiOfficial, preferences, CharBuffer::get(), buffer_size); - - if(!disableNetwork) - { - networkLock->initialize(); - } + startNuki(true); nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences, CharBuffer::get(), buffer_size); nuki->initialize(); @@ -917,124 +1373,20 @@ void setup() Log->println(openerEnabled ? F("Nuki Opener enabled") : F("Nuki Opener disabled")); if(openerEnabled) { - networkOpener = new NukiNetworkOpener(network, preferences, CharBuffer::get(), buffer_size); - - if(!disableNetwork) - { - networkOpener->initialize(); - } + startNuki(false); nukiOpener = new NukiOpenerWrapper("NukiHub", deviceIdOpener, bleScanner, networkOpener, gpio, preferences, CharBuffer::get(), buffer_size); nukiOpener->initialize(); } + bleDone = true; + if(!doOta && !disableNetwork && (forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true) || preferences->getBool(preference_webserial_enabled, false))) { if(forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true)) { - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - bool failed = false; - - if (esp_psram_get_size() <= 0) { - Log->println("Not running on PSRAM enabled device"); - failed = true; - } - else - { - if (!SPIFFS.begin(true)) { - Log->println("SPIFFS Mount Failed"); - failed = true; - } - else - { - File file = SPIFFS.open("/http_ssl.crt"); - if (!file || file.isDirectory()) { - failed = true; - Log->println("http_ssl.crt not found"); - } - else - { - Log->println("Reading http_ssl.crt"); - size_t filesize = file.size(); - char cert[filesize + 1]; - - file.read((uint8_t *)cert, sizeof(cert)); - file.close(); - cert[filesize] = '\0'; - - File file2 = SPIFFS.open("/http_ssl.key"); - if (!file2 || file2.isDirectory()) { - failed = true; - Log->println("http_ssl.key not found"); - } - else - { - Log->println("Reading http_ssl.key"); - size_t filesize2 = file2.size(); - char key[filesize2 + 1]; - - file2.read((uint8_t *)key, sizeof(key)); - file2.close(); - key[filesize2] = '\0'; - - psychicServer = new PsychicHttpServer(); - psychicServer->config.ctrl_port = 20424; - psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) { - String url = "https://" + request->host() + request->url(); - if (preferences->getString(preference_https_fqdn, "") != "") - { - url = "https://" + preferences->getString(preference_https_fqdn) + request->url(); - } - - response->setCode(301); - response->addHeader("Cache-Control", "no-cache"); - return response->redirect(url.c_str()); - }); - psychicServer->begin(); - psychicSSLServer = new PsychicHttpsServer; - psychicSSLServer->ssl_config.httpd.max_open_sockets = 8; - psychicSSLServer->setCertificate(cert, key); - psychicSSLServer->config.stack_size = HTTPD_TASK_SIZE; - webCfgServerSSL = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer, importExport); - webCfgServerSSL->initialize(); - psychicSSLServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) { - return response->redirect("/"); - }); - psychicSSLServer->begin(); - webSSLStarted = true; - } - } - } - } - - if (failed) - { - #endif - psychicServer = new PsychicHttpServer; - psychicServer->config.stack_size = HTTPD_TASK_SIZE; - webCfgServer = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer, importExport); - webCfgServer->initialize(); - psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) { - return response->redirect("/"); - }); - psychicServer->begin(); - webStarted = true; - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - } - #endif + startWebServer(); } - /* -#ifdef DEBUG_NUKIHUB - else psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/webserial"); }); - - if(preferences->getBool(preference_webserial_enabled, false)) - { - WebSerial.setAuthentication(preferences->getString(preference_cred_user), preferences->getString(preference_cred_password)); - WebSerial.begin(psychicServer); - WebSerial.setBuffer(1024); - } -#endif - */ } #endif @@ -1065,12 +1417,7 @@ void setup() setupTasks(false); } -#ifdef DEBUG_NUKIHUB - Log->print("Task Name\tStatus\tPrio\tHWM\tTask\tAffinity\n"); - char stats_buffer[1024]; - vTaskList(stats_buffer); - Log->println(stats_buffer); -#endif + //print_all_tasks_info(); } void loop() diff --git a/src/networkDevices/EthernetDevice.cpp b/src/networkDevices/EthernetDevice.cpp index 83ae97a..04df634 100644 --- a/src/networkDevices/EthernetDevice.cpp +++ b/src/networkDevices/EthernetDevice.cpp @@ -1,3 +1,4 @@ +#include "esp_task_wdt.h" #include "EthernetDevice.h" #include "../PreferencesKeys.h" #include "../Logger.h" @@ -68,14 +69,20 @@ const String EthernetDevice::deviceName() const void EthernetDevice::initialize() { - delay(250); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(250 / portTICK_PERIOD_MS); if(ethCriticalFailure) { ethCriticalFailure = false; Log->println("Failed to initialize ethernet hardware"); Log->println("Network device has a critical failure, enable fallback to Wi-Fi and reboot."); wifiFallback = true; - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::NetworkDeviceCriticalFailure); return; } @@ -101,7 +108,11 @@ void EthernetDevice::initialize() // https://github.com/arendst/Tasmota/commit/f8fbe153000591727e40b5007e0de78c33833131 // https://github.com/arendst/Tasmota/commit/f8fbe153000591727e40b5007e0de78c33833131#diff-32fc0eefbf488dd507b3bef52189bbe37158737aba6f96fe98a8746dc5021955R417 uint32_t pkg_version = bootloader_common_get_chip_ver_pkg(); + #if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM) if(esp_psram_get_size() <= 0 && pkg_version <= 3) + #else + if(pkg_version <= 3) + #endif { esp_gpio_revoke(0xFFFFFFFFFFFFFFFF); } @@ -136,7 +147,10 @@ void EthernetDevice::initialize() Log->println("Failed to initialize ethernet hardware"); Log->println("Network device has a critical failure, enable fallback to Wi-Fi and reboot."); wifiFallback = true; - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::NetworkDeviceCriticalFailure); return; } @@ -218,7 +232,10 @@ void EthernetDevice::onNetworkEvent(arduino_event_id_t event, arduino_event_info void EthernetDevice::reconfigure() { - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::ReconfigureETH); } diff --git a/src/networkDevices/NetworkDevice.cpp b/src/networkDevices/NetworkDevice.cpp index 447a17c..5b85f3f 100644 --- a/src/networkDevices/NetworkDevice.cpp +++ b/src/networkDevices/NetworkDevice.cpp @@ -7,19 +7,10 @@ #include "SPIFFS.h" #include "../MqttTopics.h" #include "PreferencesKeys.h" -#ifdef CONFIG_SOC_SPIRAM_SUPPORTED -#include "esp_psram.h" -#endif void NetworkDevice::init() { - #ifdef CONFIG_SOC_SPIRAM_SUPPORTED - if(esp_psram_get_size() > 0) - { - //_mqttInternal = true; - _mqttInternal = false; - } - #endif + _useEncryption = false; if(_preferences->getBool(preference_mqtt_ssl_enabled, false)) { if (!SPIFFS.begin(true)) { @@ -276,6 +267,21 @@ void NetworkDevice::mqttDisable() _mqttEnabled = false; } +void NetworkDevice::mqttRestart() +{ + if (_useEncryption) + { + delete _mqttClientSecure; + _mqttClientSecure = nullptr; + } + else + { + delete _mqttClient; + _mqttClient = nullptr; + } + init(); +} + bool NetworkDevice::isEncrypted() { return _useEncryption; diff --git a/src/networkDevices/NetworkDevice.h b/src/networkDevices/NetworkDevice.h index 4bf949e..16993a7 100644 --- a/src/networkDevices/NetworkDevice.h +++ b/src/networkDevices/NetworkDevice.h @@ -33,6 +33,7 @@ public: virtual bool mqttConnect(); virtual bool mqttDisconnect(bool force); virtual void mqttDisable(); + virtual void mqttRestart(); virtual bool mqttConnected() const; virtual uint16_t mqttPublish(const char* topic, uint8_t qos, bool retain, const char* payload); diff --git a/src/networkDevices/WifiDevice.cpp b/src/networkDevices/WifiDevice.cpp index f518b46..4f1ff93 100644 --- a/src/networkDevices/WifiDevice.cpp +++ b/src/networkDevices/WifiDevice.cpp @@ -1,3 +1,4 @@ +#include "esp_task_wdt.h" #include "WifiDevice.h" #include "../PreferencesKeys.h" #include "../Logger.h" @@ -36,21 +37,38 @@ void WifiDevice::initialize() { Log->println(String("Attempting to connect to saved SSID ") + String(ssid)); _openAP = false; + if(_preferences->getBool(preference_find_best_rssi, false)) + { + scan(false, true); + } + else + { + WiFi.mode(WIFI_STA); + connect(); + } } else { Log->println("No SSID or Wifi password saved, opening AP"); _openAP = true; + scan(false, true); } - - scan(false, true); } else { WiFi.disconnect(true); WiFi.mode(WIFI_STA); WiFi.disconnect(); - delay(5000); + + int loop = 0; + while (!_wifiClientStarted && loop < 50) { + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); + loop++; + } + Log->println("Dummy WiFi device for Hosted on P4 done"); } return; @@ -60,11 +78,21 @@ void WifiDevice::scan(bool passive, bool async) { if (!_openAP) { + _wifiClientStarted = false; WiFi.disconnect(true); WiFi.mode(WIFI_STA); WiFi.disconnect(); } + int loop = 0; + while (!_wifiClientStarted && loop < 50) { + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); + loop++; + } + WiFi.scanDelete(); WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); @@ -94,9 +122,15 @@ void WifiDevice::openAP() Log->println("Starting AP with SSID NukiHub and Password NukiHubESP32"); _startAP = false; WiFi.mode(WIFI_AP); - delay(500); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(500 / portTICK_PERIOD_MS); WiFi.softAPsetHostname(_hostname.c_str()); - delay(500); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(500 / portTICK_PERIOD_MS); WiFi.softAP("NukiHub", "NukiHubESP32"); //if(MDNS.begin(_hostname.c_str())){ @@ -107,9 +141,14 @@ void WifiDevice::openAP() bool WifiDevice::connect() { - WiFi.mode(WIFI_STA); - WiFi.setHostname(_hostname.c_str()); - delay(500); + int loop = 0; + while (!_wifiClientStarted && loop < 50) { + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); + loop++; + } int bestConnection = -1; @@ -158,14 +197,24 @@ bool WifiDevice::connect() WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet()); } - WiFi.begin(ssid, pass); + if (bestConnection == -1) + { + WiFi.begin(ssid, pass); + } + else + { + WiFi.begin(ssid, pass, WiFi.channel(bestConnection), WiFi.BSSID(bestConnection), 1); + } Log->print("WiFi connecting"); - int loop = 0; - while(!isConnected() && loop < 150) + loop = 0; + while(!isConnected() && loop < 600) { Log->print("."); - delay(100); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(25 / portTICK_PERIOD_MS); loop++; } Log->println(""); @@ -177,7 +226,10 @@ bool WifiDevice::connect() if(_preferences->getBool(preference_restart_on_disconnect, false) && (espMillis() > 60000)) { Log->println("Restart on disconnect watchdog triggered, rebooting"); - delay(100); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(100 / portTICK_PERIOD_MS); restartEsp(RestartReason::RestartOnDisconnectWatchdog); } else @@ -187,7 +239,7 @@ bool WifiDevice::connect() } return false; - } + } return true; } @@ -201,7 +253,10 @@ void WifiDevice::reconfigure() { _preferences->putString(preference_wifi_ssid, ""); _preferences->putString(preference_wifi_pass, ""); - delay(200); + if (esp_task_wdt_status(NULL) == ESP_OK) { + esp_task_wdt_reset(); + } + vTaskDelay(200 / portTICK_PERIOD_MS); restartEsp(RestartReason::ReconfigureWifi); } @@ -253,10 +308,10 @@ void WifiDevice::onWifiEvent(const WiFiEvent_t &event, const WiFiEventInfo_t &in Log->printf("[WiFi-event] event: %d\n", event); switch (event) { - case ARDUINO_EVENT_WIFI_READY: - Log->println("WiFi interface ready"); + case ARDUINO_EVENT_WIFI_READY: + Log->println("WiFi interface ready"); break; - case ARDUINO_EVENT_WIFI_SCAN_DONE: + case ARDUINO_EVENT_WIFI_SCAN_DONE: Log->println("Completed scan for access points"); _foundNetworks = WiFi.scanComplete(); @@ -284,28 +339,29 @@ void WifiDevice::onWifiEvent(const WiFiEvent_t &event, const WiFiEventInfo_t &in scan(false, true); } break; - case ARDUINO_EVENT_WIFI_STA_START: - Log->println("WiFi client started"); + case ARDUINO_EVENT_WIFI_STA_START: + Log->println("WiFi client started"); + _wifiClientStarted = true; break; - case ARDUINO_EVENT_WIFI_STA_STOP: - Log->println("WiFi clients stopped"); + case ARDUINO_EVENT_WIFI_STA_STOP: + Log->println("WiFi clients stopped"); if(!_openAP) { onDisconnected(); } break; - case ARDUINO_EVENT_WIFI_STA_CONNECTED: + case ARDUINO_EVENT_WIFI_STA_CONNECTED: Log->println("Connected to access point"); break; - case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: - Log->println("Disconnected from WiFi access point"); + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: + Log->println("Disconnected from WiFi access point"); if(!_openAP) { onDisconnected(); } break; - case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: - Log->println("Authentication mode of access point has changed"); + case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: + Log->println("Authentication mode of access point has changed"); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP: Log->print("Obtained IP address: "); @@ -315,38 +371,38 @@ void WifiDevice::onWifiEvent(const WiFiEvent_t &event, const WiFiEventInfo_t &in onConnected(); } break; - case ARDUINO_EVENT_WIFI_STA_LOST_IP: + case ARDUINO_EVENT_WIFI_STA_LOST_IP: Log->println("Lost IP address and IP address is reset to 0"); if(!_openAP) { onDisconnected(); } break; - case ARDUINO_EVENT_WIFI_AP_START: + case ARDUINO_EVENT_WIFI_AP_START: Log->println("WiFi access point started"); break; - case ARDUINO_EVENT_WIFI_AP_STOP: - Log->println("WiFi access point stopped"); + case ARDUINO_EVENT_WIFI_AP_STOP: + Log->println("WiFi access point stopped"); break; - case ARDUINO_EVENT_WIFI_AP_STACONNECTED: - Log->println("Client connected"); + case ARDUINO_EVENT_WIFI_AP_STACONNECTED: + Log->println("Client connected"); break; - case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: - Log->println("Client disconnected"); + case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: + Log->println("Client disconnected"); break; - case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: - Log->println("Assigned IP address to client"); + case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: + Log->println("Assigned IP address to client"); break; - case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED: - Log->println("Received probe request"); + case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED: + Log->println("Received probe request"); break; - case ARDUINO_EVENT_WIFI_AP_GOT_IP6: - Log->println("AP IPv6 is preferred"); + case ARDUINO_EVENT_WIFI_AP_GOT_IP6: + Log->println("AP IPv6 is preferred"); break; - case ARDUINO_EVENT_WIFI_STA_GOT_IP6: - Log->println("STA IPv6 is preferred"); + case ARDUINO_EVENT_WIFI_STA_GOT_IP6: + Log->println("STA IPv6 is preferred"); break; - default: + default: break; } } \ No newline at end of file diff --git a/src/networkDevices/WifiDevice.h b/src/networkDevices/WifiDevice.h index 9c29c08..1d7225d 100644 --- a/src/networkDevices/WifiDevice.h +++ b/src/networkDevices/WifiDevice.h @@ -43,4 +43,5 @@ private: bool _openAP = false; bool _startAP = true; bool _connected = false; + bool _wifiClientStarted = false; }; \ No newline at end of file diff --git a/src/webServerConstants/favicon-32x32.h b/src/webServerConstants/favicon-32x32.h new file mode 100644 index 0000000..08358ab --- /dev/null +++ b/src/webServerConstants/favicon-32x32.h @@ -0,0 +1,52 @@ +0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7a, 0x7a, +0xf4, 0x00, 0x00, 0x02, 0xfb, 0x49, 0x44, 0x41, 0x54, 0x58, 0x47, 0xcd, 0x97, 0x4b, 0x4c, 0x53, +0x41, 0x14, 0x86, 0xff, 0x69, 0xa5, 0x3c, 0x8a, 0x12, 0x14, 0x84, 0xa6, 0x96, 0x60, 0x6d, 0xd0, +0x50, 0x14, 0x51, 0xd1, 0x6a, 0x4c, 0x51, 0x63, 0x88, 0x8a, 0x06, 0x63, 0x42, 0xc0, 0x85, 0x01, +0xc3, 0xae, 0xe8, 0x0a, 0x0d, 0x4a, 0x30, 0x31, 0x18, 0xd1, 0x84, 0x18, 0x83, 0x46, 0x5d, 0xa9, +0x09, 0xb8, 0x01, 0xa2, 0x31, 0x82, 0x31, 0x82, 0x6e, 0x48, 0x78, 0xb6, 0x74, 0xe1, 0x8b, 0x47, +0xb0, 0x3e, 0x0a, 0xb4, 0x05, 0xa1, 0xe1, 0x25, 0x14, 0x68, 0xeb, 0x9d, 0x22, 0xb7, 0x40, 0x1f, +0x1b, 0xef, 0xa5, 0xce, 0xea, 0xde, 0x99, 0xce, 0x39, 0xdf, 0xfc, 0x7f, 0x67, 0xee, 0x1c, 0xe2, +0xbc, 0x0e, 0x81, 0xe3, 0x84, 0xba, 0x58, 0x40, 0x88, 0xc6, 0x09, 0x48, 0xb0, 0x0a, 0x8d, 0x00, +0x26, 0x07, 0x71, 0x3e, 0x14, 0xd4, 0x37, 0x95, 0x11, 0x7b, 0x87, 0xba, 0x84, 0x10, 0x72, 0x63, +0x15, 0xf2, 0x7a, 0xa4, 0x70, 0x12, 0xe7, 0x35, 0xe2, 0xe8, 0x48, 0x33, 0x81, 0x20, 0x36, 0x10, +0x00, 0x4c, 0x4e, 0x33, 0x71, 0x68, 0xd3, 0x18, 0xe5, 0x03, 0xd7, 0xfe, 0x6f, 0x80, 0x86, 0xb6, +0x51, 0xd4, 0xbe, 0x1b, 0x66, 0xe5, 0xd1, 0x64, 0x49, 0x91, 0xb2, 0x35, 0xdc, 0xaf, 0x5c, 0x25, +0x8f, 0xbe, 0xc1, 0x36, 0xeb, 0x60, 0x7f, 0x93, 0x79, 0x28, 0x0a, 0x07, 0x93, 0x23, 0x7c, 0xce, +0xf1, 0xab, 0xc0, 0x9d, 0x67, 0x46, 0x5c, 0xae, 0x30, 0xb0, 0x93, 0x93, 0xb6, 0x88, 0xa1, 0xad, +0xdc, 0x85, 0x60, 0x91, 0xc0, 0x67, 0xc0, 0xc8, 0xc3, 0xcd, 0x18, 0x9b, 0x9c, 0x67, 0xc7, 0x2b, +0x2e, 0x29, 0x70, 0x31, 0x5b, 0xca, 0x0d, 0x00, 0x8d, 0x72, 0xf5, 0x7c, 0x1c, 0x6e, 0x6a, 0x36, +0xfb, 0x0c, 0xb8, 0xe1, 0x48, 0x33, 0xac, 0x13, 0x3c, 0x02, 0xac, 0x11, 0x12, 0xb4, 0x3c, 0x49, +0xc1, 0x9e, 0xc4, 0xb5, 0x5e, 0x21, 0xa2, 0x8f, 0xb6, 0x60, 0x64, 0x6c, 0x8e, 0x3f, 0x05, 0x68, +0x64, 0xa5, 0x5c, 0x0c, 0x5d, 0x95, 0x77, 0x2b, 0x62, 0xd2, 0x5b, 0x30, 0x6c, 0xe5, 0x19, 0xc0, +0x9f, 0x15, 0x92, 0x63, 0xad, 0xb0, 0x8c, 0xcc, 0xf2, 0xab, 0x00, 0x8d, 0x4e, 0xad, 0x68, 0x66, +0xac, 0x48, 0x5d, 0x61, 0xc5, 0xa6, 0xe3, 0xad, 0x18, 0xfc, 0xc5, 0x13, 0xc0, 0x3a, 0xb1, 0x10, +0xe3, 0x53, 0x76, 0x76, 0x75, 0x4a, 0x79, 0x18, 0x63, 0xc5, 0xee, 0x65, 0xbb, 0x22, 0x2e, 0xa3, +0x0d, 0xfd, 0x43, 0x36, 0x7e, 0x14, 0x50, 0xa7, 0x44, 0xb8, 0x92, 0x35, 0xb6, 0x5b, 0xd9, 0x04, +0x57, 0xf2, 0x64, 0x28, 0x2b, 0x90, 0xb3, 0xef, 0xf1, 0xa7, 0xda, 0xf0, 0xd3, 0xcc, 0x13, 0x00, +0x3d, 0x84, 0x6a, 0x6e, 0x27, 0x62, 0x47, 0x8e, 0x0e, 0xd3, 0xb6, 0x85, 0xc3, 0xc6, 0x65, 0xc5, +0x63, 0xc6, 0x0a, 0xe5, 0xc2, 0xae, 0x90, 0x67, 0xb6, 0xe3, 0xfb, 0xe0, 0x0c, 0x3f, 0x0a, 0x50, +0xc9, 0x3f, 0x56, 0xa7, 0xa2, 0xbc, 0xd2, 0x88, 0xa2, 0xfb, 0xee, 0x03, 0x2a, 0xf1, 0xaf, 0x15, +0x21, 0x8c, 0x3a, 0x8a, 0xd3, 0xed, 0x30, 0x0c, 0xf0, 0x04, 0xb0, 0x2d, 0x3e, 0x0c, 0x5f, 0x6a, +0x53, 0x31, 0x6f, 0x77, 0x42, 0x95, 0xab, 0x87, 0xbe, 0x67, 0x92, 0x5d, 0x69, 0x51, 0xae, 0x0c, +0xb7, 0x2e, 0xc8, 0x91, 0x70, 0xa6, 0x03, 0x7d, 0xc6, 0x69, 0x7e, 0x14, 0x48, 0x88, 0x0b, 0x45, +0xf7, 0xf3, 0xbd, 0xae, 0xe0, 0xfa, 0xee, 0x49, 0xa8, 0xf2, 0xf4, 0x2e, 0x18, 0xda, 0x84, 0x02, +0xba, 0x2b, 0x76, 0x22, 0xbf, 0xb4, 0x17, 0x9f, 0x0d, 0x53, 0xfc, 0x00, 0x28, 0x64, 0xa1, 0xe8, +0x7d, 0xb1, 0x00, 0x40, 0x5b, 0xd1, 0x3d, 0x03, 0xca, 0xab, 0x8c, 0xec, 0xfb, 0x76, 0x85, 0x18, +0xa2, 0x20, 0x01, 0x3a, 0xbb, 0x26, 0xf8, 0x01, 0x90, 0x4b, 0x43, 0xd0, 0xf7, 0x72, 0x1f, 0x1b, +0xfc, 0xf7, 0x8c, 0x1d, 0xc9, 0x67, 0x3b, 0xf1, 0xb5, 0xdf, 0x2d, 0x39, 0x23, 0x04, 0x1c, 0x4b, +0x6e, 0x18, 0x9c, 0x7e, 0x8c, 0xe2, 0x25, 0x21, 0x30, 0xbc, 0x72, 0x03, 0x50, 0x92, 0xf7, 0x5a, +0x2b, 0xd2, 0x35, 0x1f, 0xe0, 0xeb, 0x56, 0xc3, 0x29, 0x80, 0x2c, 0x36, 0x18, 0x3f, 0xea, 0x54, +0xac, 0x02, 0x8b, 0x0f, 0xf9, 0xa5, 0x3d, 0x78, 0x5a, 0x67, 0xf6, 0xe8, 0xa7, 0x1d, 0x9c, 0x02, +0x48, 0x37, 0x06, 0xc3, 0xf8, 0xda, 0x13, 0x60, 0x74, 0x7c, 0x0e, 0xca, 0x2c, 0x1d, 0x2c, 0xa3, +0xee, 0x23, 0x78, 0x91, 0x86, 0x53, 0x00, 0x49, 0x94, 0x08, 0x03, 0x6f, 0xf6, 0x7b, 0x5d, 0x69, +0x4d, 0xe3, 0x10, 0x72, 0x8a, 0xbb, 0x3c, 0xc6, 0x38, 0x05, 0x88, 0x59, 0x2f, 0x82, 0xe9, 0xad, +0x77, 0x00, 0x9a, 0x39, 0xb3, 0xf0, 0x13, 0xea, 0x9a, 0x46, 0x96, 0x41, 0x70, 0x0a, 0x10, 0x1d, +0x19, 0x04, 0x4b, 0xc3, 0x01, 0xaf, 0x0a, 0xd0, 0xce, 0x7e, 0x8b, 0x0d, 0x49, 0xd9, 0xda, 0x65, +0x1f, 0xac, 0x7f, 0x02, 0x58, 0x79, 0x29, 0x0d, 0x0f, 0x15, 0xe2, 0x6e, 0xa1, 0xc2, 0x27, 0x00, +0x1d, 0xa8, 0x66, 0xac, 0xa8, 0x5f, 0xa2, 0xc2, 0xb9, 0x8c, 0x58, 0xa4, 0xab, 0x22, 0x7d, 0xce, +0xf9, 0xbf, 0xaf, 0xe5, 0x7e, 0x97, 0xca, 0xd1, 0x20, 0x55, 0xc0, 0xc4, 0xc4, 0x0a, 0x5c, 0x69, +0x66, 0xd7, 0x31, 0xc5, 0xa9, 0x33, 0x30, 0xc5, 0x29, 0xa1, 0xc5, 0xa9, 0xab, 0x3c, 0x3f, 0xa9, +0x2e, 0x66, 0x20, 0x0a, 0x56, 0x51, 0x09, 0x33, 0x93, 0xfc, 0x01, 0xf3, 0x6f, 0x2d, 0xfb, 0x03, +0xed, 0x06, 0xb0, 0xce, 0xb5, 0xc4, 0xb4, 0x59, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, +0xae, 0x42, 0x60, 0x82, \ No newline at end of file diff --git a/src/webServerConstants/style.h b/src/webServerConstants/style.h new file mode 100644 index 0000000..f5b039f --- /dev/null +++ b/src/webServerConstants/style.h @@ -0,0 +1,363 @@ +0x2f, 0x2a, 0x0d, 0x0a, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x68, +0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x73, 0x63, 0x61, +0x70, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x0d, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, +0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x6e, 0x2e, 0x6a, 0x73, +0x64, 0x65, 0x6c, 0x69, 0x76, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x70, 0x6d, 0x2f, 0x40, +0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x64, 0x65, 0x76, 0x2f, 0x6e, 0x65, 0x77, 0x2e, 0x63, +0x73, 0x73, 0x40, 0x31, 0x2e, 0x31, 0x2e, 0x32, 0x2f, 0x6e, 0x65, 0x77, 0x2e, 0x6d, 0x69, 0x6e, +0x2e, 0x63, 0x73, 0x73, 0x0d, 0x0a, 0x2a, 0x2f, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2a, 0x0d, 0x0a, +0x20, 0x2a, 0x20, 0x55, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x0d, 0x0a, 0x20, 0x2a, 0x20, 0x43, 0x6f, +0x6d, 0x70, 0x61, 0x63, 0x74, 0x20, 0x2f, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x66, 0x79, 0x20, 0x74, +0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6e, +0x79, 0x20, 0x74, 0x6f, 0x6f, 0x6c, 0x0d, 0x0a, 0x20, 0x2a, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x20, +0x6f, 0x6e, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, +0x74, 0x6f, 0x20, 0x73, 0x72, 0x63, 0x2f, 0x57, 0x65, 0x62, 0x43, 0x66, 0x67, 0x53, 0x65, 0x72, +0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x73, 0x2e, 0x68, 0x20, 0x61, +0x73, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x63, 0x73, 0x73, 0x0d, 0x0a, 0x20, 0x2a, 0x20, 0x54, +0x4f, 0x44, 0x4f, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x65, 0x20, 0x74, 0x68, +0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x20, 0x75, 0x70, 0x6f, 0x6e, 0x20, +0x62, 0x75, 0x69, 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x3a, 0x29, 0x0d, 0x0a, 0x2a, 0x2f, 0x0d, +0x0a, 0x0d, 0x0a, 0x3a, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x61, 0x6e, 0x73, 0x3a, 0x20, +0x27, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x27, 0x2c, 0x2d, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2d, 0x73, +0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x42, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x61, 0x63, 0x53, 0x79, +0x73, 0x74, 0x65, 0x6d, 0x46, 0x6f, 0x6e, 0x74, 0x2c, 0x27, 0x53, 0x65, 0x67, 0x6f, 0x65, 0x20, +0x55, 0x49, 0x27, 0x2c, 0x52, 0x6f, 0x62, 0x6f, 0x74, 0x6f, 0x2c, 0x4f, 0x78, 0x79, 0x67, 0x65, +0x6e, 0x2c, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x2c, 0x43, 0x61, 0x6e, 0x74, 0x61, 0x72, 0x65, +0x6c, 0x6c, 0x2c, 0x27, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x53, 0x61, 0x6e, 0x73, 0x27, 0x2c, 0x27, +0x48, 0x65, 0x6c, 0x76, 0x65, 0x74, 0x69, 0x63, 0x61, 0x20, 0x4e, 0x65, 0x75, 0x65, 0x27, 0x2c, +0x73, 0x61, 0x6e, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x69, 0x66, 0x2c, 0x27, 0x41, 0x70, 0x70, 0x6c, +0x65, 0x20, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x27, 0x2c, 0x27, +0x53, 0x65, 0x67, 0x6f, 0x65, 0x20, 0x55, 0x49, 0x20, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x27, 0x2c, +0x27, 0x53, 0x65, 0x67, 0x6f, 0x65, 0x20, 0x55, 0x49, 0x20, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, +0x27, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x66, 0x6f, 0x6e, +0x74, 0x2d, 0x6d, 0x6f, 0x6e, 0x6f, 0x3a, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x61, 0x73, +0x2c, 0x6d, 0x6f, 0x6e, 0x61, 0x63, 0x6f, 0x2c, 0x27, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, +0x4d, 0x6f, 0x6e, 0x6f, 0x27, 0x2c, 0x27, 0x4c, 0x69, 0x62, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, +0x6e, 0x20, 0x4d, 0x6f, 0x6e, 0x6f, 0x27, 0x2c, 0x27, 0x43, 0x6f, 0x75, 0x72, 0x69, 0x65, 0x72, +0x20, 0x4e, 0x65, 0x77, 0x27, 0x2c, 0x43, 0x6f, 0x75, 0x72, 0x69, 0x65, 0x72, 0x2c, 0x6d, 0x6f, +0x6e, 0x6f, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, +0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x31, 0x3a, 0x20, 0x23, 0x30, 0x30, 0x30, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x32, 0x3a, 0x20, 0x23, +0x31, 0x61, 0x31, 0x61, 0x31, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, +0x63, 0x2d, 0x62, 0x67, 0x2d, 0x31, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, 0x32, 0x3a, 0x20, 0x23, 0x66, +0x36, 0x66, 0x38, 0x66, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, +0x2d, 0x62, 0x67, 0x2d, 0x33, 0x3a, 0x20, 0x23, 0x65, 0x35, 0x65, 0x37, 0x65, 0x62, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x31, 0x3a, 0x20, +0x23, 0x30, 0x30, 0x37, 0x30, 0x66, 0x33, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, +0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x32, 0x3a, 0x20, 0x23, 0x30, 0x33, 0x36, 0x36, 0x64, 0x36, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x74, +0x78, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, +0x6e, 0x63, 0x2d, 0x61, 0x63, 0x2d, 0x31, 0x3a, 0x20, 0x23, 0x37, 0x39, 0x66, 0x66, 0x65, 0x31, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x61, 0x63, 0x2d, 0x74, +0x78, 0x3a, 0x20, 0x23, 0x30, 0x63, 0x34, 0x30, 0x34, 0x37, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x40, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x28, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x73, 0x2d, +0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x3a, 0x20, 0x64, 0x61, +0x72, 0x6b, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3a, 0x72, 0x6f, 0x6f, 0x74, +0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, +0x2d, 0x74, 0x78, 0x2d, 0x31, 0x3a, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x32, 0x3a, 0x20, +0x23, 0x65, 0x65, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, +0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, 0x31, 0x3a, 0x20, 0x23, 0x30, 0x30, 0x30, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, +0x2d, 0x32, 0x3a, 0x20, 0x23, 0x31, 0x31, 0x31, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, 0x33, 0x3a, 0x20, 0x23, 0x32, +0x32, 0x32, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, +0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x31, 0x3a, 0x20, 0x23, 0x33, 0x32, 0x39, 0x31, 0x66, 0x66, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, +0x6b, 0x2d, 0x32, 0x3a, 0x20, 0x23, 0x30, 0x30, 0x37, 0x30, 0x66, 0x33, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x74, +0x78, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x61, 0x63, 0x2d, 0x31, 0x3a, 0x20, 0x23, 0x37, 0x39, +0x32, 0x38, 0x63, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, +0x2d, 0x6e, 0x63, 0x2d, 0x61, 0x63, 0x2d, 0x74, 0x78, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2a, 0x20, 0x7b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x30, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x30, +0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x69, 0x6d, 0x67, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, +0x2c, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x70, 0x2c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2c, +0x74, 0x65, 0x78, 0x74, 0x61, 0x72, 0x65, 0x61, 0x2c, 0x75, 0x6c, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, +0x3a, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x62, 0x75, 0x74, +0x74, 0x6f, 0x6e, 0x2c, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x73, +0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, +0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, +0x6e, 0x63, 0x2d, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x61, 0x6e, 0x73, 0x29, 0x0d, 0x0a, 0x7d, +0x0d, 0x0a, 0x0d, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x30, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, +0x37, 0x35, 0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, +0x69, 0x6e, 0x67, 0x3a, 0x20, 0x32, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x36, +0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, +0x77, 0x2d, 0x78, 0x3a, 0x20, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x2d, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3a, 0x20, 0x6e, 0x6f, +0x72, 0x6d, 0x61, 0x6c, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x66, +0x6c, 0x6f, 0x77, 0x2d, 0x77, 0x72, 0x61, 0x70, 0x3a, 0x20, 0x61, 0x6e, 0x79, 0x77, 0x68, 0x65, +0x72, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, +0x75, 0x6e, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, +0x2d, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, +0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x32, 0x29, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, +0x20, 0x31, 0x2e, 0x30, 0x33, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, +0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x2e, 0x35, 0x0d, +0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x3a, 0x3a, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, +0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, +0x75, 0x6e, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x61, 0x63, +0x2d, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, +0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x61, 0x63, 0x2d, 0x74, 0x78, 0x29, +0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x68, 0x31, 0x2c, 0x68, 0x32, 0x2c, 0x68, 0x33, 0x2c, +0x68, 0x34, 0x2c, 0x68, 0x35, 0x2c, 0x68, 0x36, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, +0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x2e, 0x38, +0x37, 0x35, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x68, 0x31, 0x2c, 0x68, +0x32, 0x2c, 0x68, 0x33, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, +0x72, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x31, +0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2d, +0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x32, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, +0x20, 0x38, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, +0x72, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6f, +0x6c, 0x69, 0x64, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, +0x32, 0x29, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x68, 0x34, 0x2c, 0x68, 0x35, 0x2c, 0x68, +0x36, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, +0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x2e, 0x33, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, +0x0d, 0x0a, 0x0d, 0x0a, 0x68, 0x31, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, +0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x32, 0x2e, 0x32, 0x35, 0x72, 0x65, 0x6d, +0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x68, 0x32, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x2e, 0x38, 0x35, +0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x68, 0x33, 0x20, 0x7b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, +0x2e, 0x35, 0x35, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x68, 0x34, 0x20, +0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, +0x3a, 0x20, 0x31, 0x2e, 0x32, 0x35, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, +0x68, 0x35, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, +0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, +0x68, 0x36, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, +0x69, 0x7a, 0x65, 0x3a, 0x20, 0x2e, 0x38, 0x37, 0x35, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x61, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, +0x72, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x31, +0x29, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x61, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x20, +0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x61, +0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x32, 0x29, 0x20, 0x21, 0x69, 0x6d, +0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x74, 0x3b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x61, +0x62, 0x62, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, +0x72, 0x3a, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x61, 0x62, +0x62, 0x72, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x0d, 0x0a, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x61, 0x20, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x2c, 0x62, 0x75, 0x74, 0x74, +0x6f, 0x6e, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x62, 0x75, +0x74, 0x74, 0x6f, 0x6e, 0x5d, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, +0x3d, 0x72, 0x65, 0x73, 0x65, 0x74, 0x5d, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, +0x70, 0x65, 0x3d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5d, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x72, 0x65, +0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, +0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x36, 0x70, 0x78, +0x20, 0x31, 0x32, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, +0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x64, 0x65, 0x63, 0x6f, 0x72, 0x61, +0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x77, 0x68, 0x69, 0x74, 0x65, 0x2d, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x20, 0x6e, 0x6f, +0x77, 0x72, 0x61, 0x70, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, +0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, +0x6c, 0x6b, 0x2d, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, +0x72, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x74, +0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, +0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, +0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x62, 0x6f, 0x78, 0x2d, 0x73, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x62, 0x6f, +0x72, 0x64, 0x65, 0x72, 0x2d, 0x62, 0x6f, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, +0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, +0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x74, 0x78, 0x29, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x61, 0x20, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x5b, 0x64, 0x69, 0x73, 0x61, 0x62, +0x6c, 0x65, 0x64, 0x5d, 0x2c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x5b, 0x64, 0x69, 0x73, 0x61, +0x62, 0x6c, 0x65, 0x64, 0x5d, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, +0x3d, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x5d, 0x5b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, +0x64, 0x5d, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x72, 0x65, +0x73, 0x65, 0x74, 0x5d, 0x5b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5d, 0x2c, 0x69, +0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, +0x5d, 0x5b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5d, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, +0x6c, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, +0x3a, 0x20, 0x2e, 0x35, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, +0x72, 0x3a, 0x20, 0x6e, 0x6f, 0x74, 0x2d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x0d, 0x0a, +0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2e, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3a, 0x66, 0x6f, 0x63, +0x75, 0x73, 0x2c, 0x2e, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, +0x2c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3a, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x2c, 0x62, 0x75, +0x74, 0x74, 0x6f, 0x6e, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, +0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x5d, 0x3a, 0x66, 0x6f, +0x63, 0x75, 0x73, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x62, +0x75, 0x74, 0x74, 0x6f, 0x6e, 0x5d, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x2c, 0x69, 0x6e, 0x70, +0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x72, 0x65, 0x73, 0x65, 0x74, 0x5d, 0x3a, 0x66, +0x6f, 0x63, 0x75, 0x73, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, +0x72, 0x65, 0x73, 0x65, 0x74, 0x5d, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x2c, 0x69, 0x6e, 0x70, +0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5d, 0x3a, +0x66, 0x6f, 0x63, 0x75, 0x73, 0x2c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, +0x3d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5d, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x7b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, +0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x6c, 0x6b, 0x2d, 0x32, 0x29, +0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x7b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6c, 0x6c, 0x61, +0x70, 0x73, 0x65, 0x3a, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x0d, +0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x64, 0x2c, 0x74, 0x68, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, +0x6f, 0x6c, 0x69, 0x64, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, +0x2d, 0x33, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, +0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x2e, 0x35, 0x72, 0x65, 0x6d, 0x0d, +0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x68, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, +0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, 0x32, 0x29, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x74, 0x72, 0x3a, 0x6e, 0x74, 0x68, 0x2d, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x65, 0x76, +0x65, 0x6e, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, +0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, +0x62, 0x67, 0x2d, 0x32, 0x29, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x65, 0x78, 0x74, +0x61, 0x72, 0x65, 0x61, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, +0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x2c, 0x74, +0x65, 0x78, 0x74, 0x61, 0x72, 0x65, 0x61, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, +0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x36, 0x70, 0x78, 0x20, 0x31, 0x32, 0x70, 0x78, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, +0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x2e, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x76, 0x61, +0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, +0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, +0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, +0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, 0x33, 0x29, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, +0x69, 0x75, 0x73, 0x3a, 0x20, 0x34, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, +0x6f, 0x78, 0x2d, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x78, 0x2d, 0x73, 0x69, 0x7a, 0x69, 0x6e, 0x67, +0x3a, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x62, 0x6f, 0x78, 0x0d, 0x0a, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x69, 0x6d, 0x67, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, +0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x0d, 0x0a, 0x7d, +0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x64, 0x3e, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x7b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, +0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, +0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x30, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, +0x64, 0x3e, 0x74, 0x65, 0x78, 0x74, 0x61, 0x72, 0x65, 0x61, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, +0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x30, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x74, 0x64, 0x3e, +0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, +0x72, 0x67, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, +0x20, 0x30, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, +0x67, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, +0x72, 0x65, 0x64, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x6d, 0x65, 0x64, 0x69, 0x61, +0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x6e, 0x64, +0x20, 0x28, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x36, 0x30, 0x30, +0x70, 0x78, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x61, 0x64, 0x61, 0x70, +0x74, 0x20, 0x74, 0x64, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x61, 0x64, 0x61, +0x70, 0x74, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x74, 0x65, +0x78, 0x74, 0x5d, 0x2c, 0x2e, 0x61, 0x64, 0x61, 0x70, 0x74, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, +0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5d, 0x2c, +0x2e, 0x61, 0x64, 0x61, 0x70, 0x74, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, +0x65, 0x3d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5d, 0x2c, 0x2e, 0x61, 0x64, 0x61, 0x70, 0x74, +0x20, 0x74, 0x65, 0x78, 0x74, 0x61, 0x72, 0x65, 0x61, 0x2c, 0x2e, 0x61, 0x64, 0x61, 0x70, 0x74, +0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x61, 0x64, +0x61, 0x70, 0x74, 0x20, 0x74, 0x64, 0x3a, 0x68, 0x61, 0x73, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, +0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x6f, 0x78, 0x5d, 0x29, +0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, +0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x61, 0x64, +0x61, 0x70, 0x74, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x63, +0x68, 0x65, 0x63, 0x6b, 0x62, 0x6f, 0x78, 0x5d, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x2e, 0x35, 0x65, 0x6d, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, +0x74, 0x3a, 0x20, 0x31, 0x2e, 0x35, 0x65, 0x6d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x61, 0x64, 0x61, 0x70, 0x74, 0x20, 0x74, 0x61, +0x62, 0x6c, 0x65, 0x20, 0x74, 0x64, 0x3a, 0x66, 0x69, 0x72, 0x73, 0x74, 0x2d, 0x63, 0x68, 0x69, +0x6c, 0x64, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, +0x72, 0x64, 0x65, 0x72, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x30, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x61, 0x64, +0x61, 0x70, 0x74, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x64, 0x3a, 0x6c, 0x61, 0x73, +0x74, 0x2d, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x74, 0x6f, 0x70, 0x3a, 0x20, 0x30, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, +0x74, 0x62, 0x6c, 0x6e, 0x61, 0x76, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x3e, 0x73, 0x70, 0x61, 0x6e, +0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, +0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x34, 0x30, 0x70, 0x78, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x74, 0x62, 0x6c, 0x6e, 0x61, +0x76, 0x20, 0x61, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, +0x72, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, +0x72, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6f, +0x6c, 0x69, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, +0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, +0x3a, 0x20, 0x62, 0x6f, 0x6c, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, +0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x2e, 0x36, 0x72, 0x65, 0x6d, 0x20, 0x30, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, +0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, +0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x74, 0x78, 0x2d, 0x31, 0x29, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x64, 0x65, 0x63, 0x6f, 0x72, 0x61, +0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x6c, 0x69, 0x6e, +0x65, 0x61, 0x72, 0x2d, 0x67, 0x72, 0x61, 0x64, 0x69, 0x65, 0x6e, 0x74, 0x28, 0x74, 0x6f, 0x20, +0x6c, 0x65, 0x66, 0x74, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, +0x20, 0x35, 0x30, 0x25, 0x2c, 0x72, 0x67, 0x62, 0x61, 0x28, 0x32, 0x35, 0x35, 0x2c, 0x32, 0x35, +0x35, 0x2c, 0x32, 0x35, 0x35, 0x2c, 0x30, 0x2e, 0x34, 0x29, 0x20, 0x35, 0x30, 0x25, 0x29, 0x20, +0x72, 0x69, 0x67, 0x68, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, +0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x32, 0x30, 0x30, +0x25, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x61, +0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x2e, 0x32, 0x73, +0x20, 0x65, 0x61, 0x73, 0x65, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x74, 0x62, 0x6c, +0x6e, 0x61, 0x76, 0x20, 0x61, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, +0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x2d, +0x67, 0x72, 0x61, 0x64, 0x69, 0x65, 0x6e, 0x74, 0x28, 0x74, 0x6f, 0x20, 0x6c, 0x65, 0x66, 0x74, +0x2c, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, 0x62, 0x67, 0x2d, 0x32, 0x29, 0x20, +0x35, 0x30, 0x25, 0x2c, 0x72, 0x67, 0x62, 0x61, 0x28, 0x32, 0x35, 0x35, 0x2c, 0x32, 0x35, 0x35, +0x2c, 0x32, 0x35, 0x35, 0x2c, 0x30, 0x2e, 0x34, 0x29, 0x20, 0x35, 0x30, 0x25, 0x29, 0x20, 0x72, +0x69, 0x67, 0x68, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, +0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x32, 0x30, 0x30, 0x25, +0x20, 0x31, 0x30, 0x30, 0x25, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x74, 0x62, 0x6c, +0x6e, 0x61, 0x76, 0x20, 0x61, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x70, 0x6f, +0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x61, +0x6c, 0x6c, 0x20, 0x2e, 0x34, 0x35, 0x73, 0x20, 0x65, 0x61, 0x73, 0x65, 0x0d, 0x0a, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x23, 0x74, 0x62, 0x6c, 0x6e, 0x61, 0x76, 0x20, 0x61, 0x3a, 0x61, 0x63, 0x74, +0x69, 0x76, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, +0x72, 0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x76, 0x61, 0x72, 0x28, 0x2d, 0x2d, 0x6e, 0x63, 0x2d, +0x6c, 0x6b, 0x2d, 0x31, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x61, 0x6e, +0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x2e, 0x31, 0x35, 0x73, +0x20, 0x65, 0x61, 0x73, 0x65, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x74, 0x62, 0x6c, +0x6e, 0x61, 0x76, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x6c, 0x69, 0x73, 0x74, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, +0x2e, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, +0x6c, 0x61, 0x79, 0x3a, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x62, 0x6c, 0x6f, 0x63, +0x6b, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, +0x30, 0x30, 0x25, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x23, 0x74, 0x62, 0x6c, 0x6e, 0x61, +0x76, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x3e, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, +0x3a, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, +0x72, 0x67, 0x69, 0x6e, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x30, 0x70, 0x78, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x66, +0x37, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x77, 0x65, +0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3a, 0x20, 0x69, 0x74, 0x61, 0x6c, +0x69, 0x63, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, +0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2e, 0x74, +0x64, 0x62, 0x74, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, +0x2d, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x2d, 0x61, 0x6c, +0x69, 0x67, 0x6e, 0x3a, 0x20, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x2e, 0x6e, 0x61, 0x76, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, +0x33, 0x37, 0x35, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, +0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x0d, 0x0a, 0x7d, \ No newline at end of file diff --git a/src/webServerConstants/webSerial.h b/src/webServerConstants/webSerial.h new file mode 100644 index 0000000..d24f203 --- /dev/null +++ b/src/webServerConstants/webSerial.h @@ -0,0 +1,706 @@ +0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0x0d, +0x0a, 0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0x0d, 0x0a, 0x0d, 0x0a, 0x3c, 0x68, 0x65, 0x61, 0x64, +0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x63, 0x68, 0x61, +0x72, 0x73, 0x65, 0x74, 0x3d, 0x22, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x22, 0x3e, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x68, 0x74, 0x74, 0x70, 0x2d, 0x65, 0x71, +0x75, 0x69, 0x76, 0x3d, 0x22, 0x58, 0x2d, 0x55, 0x41, 0x2d, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, +0x69, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x49, +0x45, 0x3d, 0x65, 0x64, 0x67, 0x65, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, +0x69, 0x74, 0x6c, 0x65, 0x3e, 0x4e, 0x75, 0x6b, 0x69, 0x48, 0x75, 0x62, 0x20, 0x57, 0x65, 0x62, +0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x3c, 0x2f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, +0x76, 0x69, 0x65, 0x77, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, +0x74, 0x3d, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, +0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x2d, 0x73, +0x63, 0x61, 0x6c, 0x65, 0x3d, 0x31, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, +0x74, 0x79, 0x6c, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, +0x63, 0x73, 0x73, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, +0x69, 0x76, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, +0x20, 0x30, 0x2e, 0x34, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x64, 0x65, 0x63, 0x6f, 0x72, +0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x2a, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3a, +0x3a, 0x61, 0x66, 0x74, 0x65, 0x72, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x3a, 0x3a, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x78, 0x2d, 0x73, 0x69, 0x7a, +0x69, 0x6e, 0x67, 0x3a, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x62, 0x6f, 0x78, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, +0x72, 0x64, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, +0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x64, +0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x2d, 0x62, 0x65, 0x68, 0x61, +0x76, 0x69, 0x6f, 0x72, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, +0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, +0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x30, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x3a, 0x20, 0x2d, 0x61, 0x70, 0x70, +0x6c, 0x65, 0x2d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, +0x6d, 0x2d, 0x75, 0x69, 0x2c, 0x20, 0x42, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x61, 0x63, 0x53, 0x79, +0x73, 0x74, 0x65, 0x6d, 0x46, 0x6f, 0x6e, 0x74, 0x2c, 0x20, 0x22, 0x53, 0x65, 0x67, 0x6f, 0x65, +0x20, 0x55, 0x49, 0x22, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x6f, 0x62, 0x6f, 0x74, 0x6f, 0x2c, 0x20, 0x22, +0x48, 0x65, 0x6c, 0x76, 0x65, 0x74, 0x69, 0x63, 0x61, 0x20, 0x4e, 0x65, 0x75, 0x65, 0x22, 0x2c, +0x20, 0x73, 0x61, 0x6e, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x69, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, +0x6f, 0x75, 0x6e, 0x64, 0x3a, 0x20, 0x23, 0x30, 0x30, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, +0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x61, 0x70, +0x70, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, +0x6c, 0x61, 0x79, 0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x65, 0x78, 0x2d, 0x64, 0x69, 0x72, +0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x67, 0x72, 0x69, 0x64, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, +0x61, 0x79, 0x3a, 0x20, 0x67, 0x72, 0x69, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x2e, 0x67, 0x61, 0x70, 0x2d, 0x32, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x61, 0x70, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x72, 0x65, +0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, +0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x65, 0x78, 0x3a, 0x20, 0x31, 0x20, 0x31, 0x20, 0x30, 0x25, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6c, 0x69, 0x67, +0x6e, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, 0x2d, +0x65, 0x6e, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, +0x6f, 0x72, 0x3a, 0x20, 0x23, 0x64, 0x34, 0x64, 0x34, 0x64, 0x38, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, +0x61, 0x6d, 0x69, 0x6c, 0x79, 0x3a, 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x73, 0x70, 0x61, 0x63, 0x65, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, +0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2d, 0x78, 0x3a, 0x20, 0x68, 0x69, 0x64, 0x64, 0x65, +0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2d, 0x79, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x31, 0x34, 0x70, 0x78, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x70, +0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, +0x2d, 0x77, 0x72, 0x61, 0x70, 0x3a, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x2d, 0x77, 0x6f, 0x72, +0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x74, 0x65, 0x78, 0x74, 0x2d, 0x77, 0x72, 0x61, 0x70, 0x3a, 0x20, 0x77, 0x72, 0x61, 0x70, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, +0x69, 0x74, 0x65, 0x2d, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x20, 0x70, 0x72, 0x65, 0x2d, 0x6c, +0x69, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x70, 0x61, 0x6e, 0x65, +0x6c, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, +0x69, 0x76, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x20, 0x30, +0x2e, 0x35, 0x72, 0x65, 0x6d, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, +0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x2d, +0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x34, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, +0x3a, 0x20, 0x63, 0x61, 0x6c, 0x63, 0x28, 0x31, 0x30, 0x30, 0x25, 0x20, 0x2d, 0x20, 0x31, 0x72, +0x65, 0x6d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x6d, 0x65, 0x64, +0x69, 0x75, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x75, 0x74, 0x74, +0x6f, 0x6e, 0x73, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, +0x6c, 0x69, 0x67, 0x6e, 0x2d, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, +0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x79, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, +0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, 0x2d, 0x65, 0x6e, 0x64, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, +0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, +0x72, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, +0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, +0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x65, 0x78, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, +0x20, 0x72, 0x6f, 0x77, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x2d, 0x67, 0x61, 0x70, 0x3a, 0x20, 0x31, +0x2e, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x6f, 0x70, 0x3a, +0x20, 0x30, 0x2e, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, +0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, +0x6e, 0x67, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x72, 0x65, 0x6d, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, +0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x31, 0x61, 0x31, 0x61, 0x61, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x73, 0x20, 0x62, 0x75, +0x74, 0x74, 0x6f, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, +0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x38, 0x70, 0x78, 0x20, 0x31, +0x30, 0x70, 0x78, 0x20, 0x38, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, +0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x73, 0x74, +0x79, 0x6c, 0x65, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, +0x30, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x61, 0x31, 0x61, 0x31, 0x61, 0x61, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, +0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, +0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x73, 0x20, 0x62, 0x75, 0x74, +0x74, 0x6f, 0x6e, 0x20, 0x73, 0x76, 0x67, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x2e, +0x34, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x2e, 0x34, 0x72, 0x65, +0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, +0x73, 0x20, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3a, 0x68, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x7b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, +0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, +0x23, 0x31, 0x38, 0x31, 0x38, 0x31, 0x62, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, +0x77, 0x2d, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, +0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x65, +0x64, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, +0x30, 0x2e, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x66, +0x6c, 0x65, 0x78, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x67, 0x72, 0x6f, 0x77, 0x20, 0x7b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x65, +0x78, 0x2d, 0x67, 0x72, 0x6f, 0x77, 0x3a, 0x20, 0x31, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x3a, 0x20, +0x64, 0x72, 0x6f, 0x70, 0x2d, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x28, 0x30, 0x20, 0x34, 0x70, +0x78, 0x20, 0x33, 0x70, 0x78, 0x20, 0x72, 0x67, 0x62, 0x28, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, +0x2f, 0x20, 0x30, 0x2e, 0x30, 0x37, 0x29, 0x29, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x2d, 0x73, 0x68, +0x61, 0x64, 0x6f, 0x77, 0x28, 0x30, 0x20, 0x32, 0x70, 0x78, 0x20, 0x32, 0x70, 0x78, 0x20, 0x72, +0x67, 0x62, 0x28, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2f, 0x20, 0x30, 0x2e, 0x30, 0x36, 0x29, +0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2d, +0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x2d, 0x69, 0x74, 0x65, 0x6d, +0x73, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, +0x6e, 0x65, 0x72, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x77, 0x69, +0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x62, 0x6f, +0x74, 0x74, 0x6f, 0x6d, 0x3a, 0x20, 0x31, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x63, +0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x31, 0x38, 0x31, 0x38, 0x31, 0x62, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, +0x6c, 0x61, 0x79, 0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x2d, 0x69, 0x74, +0x65, 0x6d, 0x73, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, +0x79, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, +0x2d, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, +0x31, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, +0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, +0x74, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, +0x3a, 0x20, 0x2e, 0x33, 0x37, 0x35, 0x72, 0x65, 0x6d, 0x20, 0x2e, 0x37, 0x35, 0x72, 0x65, 0x6d, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, +0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30, 0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x20, 0x23, +0x36, 0x62, 0x37, 0x32, 0x38, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, +0x75, 0x73, 0x3a, 0x20, 0x2e, 0x32, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x66, 0x61, +0x6d, 0x69, 0x6c, 0x79, 0x3a, 0x20, 0x6d, 0x6f, 0x6e, 0x6f, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, +0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x2e, 0x38, 0x37, 0x35, 0x72, 0x65, 0x6d, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, +0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x2e, 0x35, 0x72, +0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, +0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x30, 0x64, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, +0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, +0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, 0x30, +0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, +0x2d, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x30, +0x20, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x20, 0x23, 0x65, 0x35, 0x65, 0x37, 0x65, 0x62, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, +0x67, 0x69, 0x6e, 0x3a, 0x20, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, +0x6f, 0x72, 0x6d, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, +0x6e, 0x64, 0x2d, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x75, 0x72, +0x73, 0x6f, 0x72, 0x3a, 0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, +0x61, 0x79, 0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x31, 0x30, +0x30, 0x25, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x2d, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x3a, 0x20, 0x63, 0x65, +0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x79, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, +0x6e, 0x74, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2d, +0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x2e, 0x32, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, +0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x2e, 0x35, 0x72, 0x65, 0x6d, 0x20, 0x31, 0x2e, 0x35, 0x72, +0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, 0x2e, 0x38, 0x37, 0x35, +0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, +0x2e, 0x32, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x66, 0x66, 0x66, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, +0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, +0x20, 0x72, 0x67, 0x62, 0x28, 0x32, 0x39, 0x20, 0x37, 0x38, 0x20, 0x32, 0x31, 0x36, 0x29, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x2d, 0x62, +0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x73, 0x76, 0x67, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, +0x31, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x6d, 0x6c, 0x2d, 0x34, 0x20, 0x7b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x72, 0x67, +0x69, 0x6e, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x3a, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, +0x2d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x3a, 0x20, +0x66, 0x6c, 0x65, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x79, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, +0x6e, 0x74, 0x3a, 0x20, 0x66, 0x6c, 0x65, 0x78, 0x2d, 0x65, 0x6e, 0x64, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, 0x64, 0x65, +0x72, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, +0x31, 0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x72, 0x65, +0x6d, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x20, +0x2e, 0x37, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, +0x3a, 0x20, 0x31, 0x72, 0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x72, 0x67, 0x62, 0x28, +0x31, 0x31, 0x33, 0x20, 0x31, 0x31, 0x33, 0x20, 0x31, 0x32, 0x32, 0x29, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, +0x2d, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x3a, 0x20, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, +0x64, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x72, 0x67, 0x62, 0x28, 0x32, +0x34, 0x20, 0x32, 0x34, 0x20, 0x32, 0x37, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x61, 0x70, 0x3a, 0x20, 0x30, 0x2e, 0x35, 0x72, +0x65, 0x6d, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x61, 0x64, 0x67, 0x65, +0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x30, 0x2e, 0x33, 0x37, 0x35, 0x72, 0x65, 0x6d, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, +0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x30, 0x2e, 0x33, 0x37, 0x35, 0x72, 0x65, 0x6d, 0x3b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x72, +0x64, 0x65, 0x72, 0x2d, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x20, 0x39, 0x39, 0x39, 0x39, +0x70, 0x78, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x61, 0x64, 0x67, 0x65, +0x2e, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, +0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x32, 0x32, 0x63, 0x35, 0x35, 0x65, 0x3b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x61, 0x64, 0x67, 0x65, 0x2e, 0x6f, 0x72, +0x61, 0x6e, 0x67, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, +0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x23, 0x66, 0x35, 0x39, 0x65, 0x30, 0x62, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x62, 0x61, 0x64, 0x67, 0x65, 0x2e, 0x72, 0x65, 0x64, 0x20, +0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, +0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, +0x20, 0x23, 0x65, 0x66, 0x34, 0x34, 0x34, 0x34, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x73, 0x74, 0x79, 0x6c, +0x65, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x3e, 0x0d, 0x0a, 0x0d, 0x0a, 0x3c, +0x62, 0x6f, 0x64, 0x79, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, +0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x61, 0x70, 0x70, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x3e, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76, +0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x73, 0x22, +0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42, 0x75, +0x66, 0x66, 0x65, 0x72, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x3c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, +0x74, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x20, 0x63, +0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x73, 0x68, +0x61, 0x64, 0x6f, 0x77, 0x22, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, +0x72, 0x3d, 0x22, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x20, +0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x31, 0x30, 0x30, 0x30, 0x22, 0x3e, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, +0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x63, +0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x73, 0x68, +0x61, 0x64, 0x6f, 0x77, 0x22, 0x20, 0x6f, 0x6e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x74, +0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x28, 0x29, 0x22, 0x3e, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x76, 0x67, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, +0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, +0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x73, 0x76, 0x67, 0x22, 0x20, 0x77, 0x69, +0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x34, 0x22, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, +0x22, 0x32, 0x34, 0x22, 0x20, 0x76, 0x69, 0x65, 0x77, 0x42, 0x6f, 0x78, 0x3d, 0x22, 0x30, 0x20, +0x30, 0x20, 0x32, 0x34, 0x20, 0x32, 0x34, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x6e, +0x6f, 0x6e, 0x65, 0x22, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, +0x72, 0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6c, +0x6f, 0x72, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, +0x3d, 0x22, 0x32, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, +0x63, 0x61, 0x70, 0x3d, 0x22, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, +0x6b, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x6a, 0x6f, 0x69, 0x6e, 0x3d, 0x22, 0x72, 0x6f, 0x75, +0x6e, 0x64, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, +0x61, 0x74, 0x68, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, +0x22, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x30, 0x20, 0x30, 0x68, 0x32, 0x34, 0x76, 0x32, 0x34, 0x48, +0x30, 0x7a, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, +0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, +0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x34, 0x20, 0x37, 0x6c, 0x31, 0x36, 0x20, 0x30, 0x22, 0x20, +0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, +0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x31, 0x30, 0x20, 0x31, 0x31, 0x6c, 0x30, 0x20, 0x36, 0x22, +0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, +0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x31, 0x34, 0x20, 0x31, 0x31, 0x6c, 0x30, 0x20, 0x36, +0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, +0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x35, 0x20, 0x37, 0x6c, 0x31, 0x20, 0x31, 0x32, +0x61, 0x32, 0x20, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x32, 0x20, 0x32, 0x68, 0x38, +0x61, 0x32, 0x20, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x32, 0x20, 0x2d, 0x32, 0x6c, +0x31, 0x20, 0x2d, 0x31, 0x32, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x39, 0x20, 0x37, +0x76, 0x2d, 0x33, 0x61, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, +0x2d, 0x31, 0x68, 0x34, 0x61, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, +0x20, 0x31, 0x76, 0x33, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, +0x73, 0x76, 0x67, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x3c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, +0x72, 0x6f, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x22, 0x20, +0x6f, 0x6e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, +0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x3d, 0x21, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x63, 0x72, +0x6f, 0x6c, 0x6c, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x76, 0x67, 0x20, +0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, +0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x73, 0x76, +0x67, 0x22, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x34, 0x22, 0x20, 0x68, 0x65, +0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x32, 0x34, 0x22, 0x20, 0x76, 0x69, 0x65, 0x77, 0x42, 0x6f, +0x78, 0x3d, 0x22, 0x30, 0x20, 0x30, 0x20, 0x32, 0x34, 0x20, 0x32, 0x34, 0x22, 0x20, 0x66, 0x69, +0x6c, 0x6c, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x63, 0x75, 0x72, 0x72, 0x65, +0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x2d, +0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, +0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x70, 0x3d, 0x22, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, +0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x6a, 0x6f, 0x69, 0x6e, +0x3d, 0x22, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x3d, +0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x30, 0x20, 0x30, 0x68, 0x32, +0x34, 0x76, 0x32, 0x34, 0x48, 0x30, 0x7a, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x6e, +0x6f, 0x6e, 0x65, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x35, 0x20, 0x31, 0x33, 0x61, +0x32, 0x20, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x2d, 0x32, 0x68, 0x31, +0x30, 0x61, 0x32, 0x20, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x32, 0x76, +0x36, 0x61, 0x32, 0x20, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x2d, 0x32, 0x20, 0x32, +0x68, 0x2d, 0x31, 0x30, 0x61, 0x32, 0x20, 0x32, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x2d, +0x32, 0x20, 0x2d, 0x32, 0x76, 0x2d, 0x36, 0x7a, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, +0x31, 0x31, 0x20, 0x31, 0x36, 0x61, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, +0x32, 0x20, 0x30, 0x61, 0x31, 0x20, 0x31, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x32, +0x20, 0x30, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x38, 0x20, 0x31, 0x31, 0x76, 0x2d, +0x34, 0x61, 0x34, 0x20, 0x34, 0x20, 0x30, 0x20, 0x31, 0x20, 0x31, 0x20, 0x38, 0x20, 0x30, 0x76, +0x34, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x73, 0x76, 0x67, +0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x3c, 0x2f, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x62, +0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x72, 0x6f, 0x75, +0x6e, 0x64, 0x65, 0x64, 0x20, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x22, 0x20, 0x6f, 0x6e, 0x63, +0x6c, 0x69, 0x63, 0x6b, 0x3d, 0x22, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, +0x73, 0x74, 0x61, 0x6d, 0x70, 0x3d, 0x21, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x69, 0x6d, +0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, +0x76, 0x67, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, +0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, +0x2f, 0x73, 0x76, 0x67, 0x22, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x34, 0x22, +0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x32, 0x34, 0x22, 0x20, 0x76, 0x69, 0x65, +0x77, 0x42, 0x6f, 0x78, 0x3d, 0x22, 0x30, 0x20, 0x30, 0x20, 0x32, 0x34, 0x20, 0x32, 0x34, 0x22, +0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x63, 0x75, +0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, +0x6b, 0x65, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x22, 0x20, 0x73, 0x74, 0x72, +0x6f, 0x6b, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x63, 0x61, 0x70, 0x3d, 0x22, 0x72, 0x6f, 0x75, +0x6e, 0x64, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x6a, +0x6f, 0x69, 0x6e, 0x3d, 0x22, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x73, 0x74, 0x72, 0x6f, +0x6b, 0x65, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x30, 0x20, +0x30, 0x68, 0x32, 0x34, 0x76, 0x32, 0x34, 0x48, 0x30, 0x7a, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, +0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x33, 0x20, +0x31, 0x32, 0x61, 0x39, 0x20, 0x39, 0x20, 0x30, 0x20, 0x31, 0x20, 0x30, 0x20, 0x31, 0x38, 0x20, +0x30, 0x61, 0x39, 0x20, 0x39, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, 0x2d, 0x31, 0x38, 0x20, +0x30, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, +0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x31, 0x32, 0x20, 0x37, 0x76, 0x35, 0x6c, +0x33, 0x20, 0x33, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x73, +0x76, 0x67, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x69, +0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x68, 0x65, +0x61, 0x64, 0x65, 0x72, 0x3e, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, +0x74, 0x65, 0x6e, 0x74, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x66, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x3e, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, +0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, +0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x3e, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, +0x66, 0x6f, 0x72, 0x6d, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x66, 0x6c, 0x65, 0x78, +0x20, 0x77, 0x2d, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2d, 0x63, 0x65, +0x6e, 0x74, 0x65, 0x72, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x69, 0x6e, 0x70, +0x75, 0x74, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x20, +0x61, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x3d, 0x22, 0x6f, 0x66, +0x66, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22, 0x20, 0x72, +0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x3d, 0x22, 0x22, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, +0x3d, 0x22, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x3d, +0x22, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x68, +0x65, 0x72, 0x65, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x69, 0x76, 0x20, +0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x6d, 0x6c, 0x2d, 0x34, 0x22, 0x3e, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x69, +0x64, 0x3d, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x2d, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, +0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x22, 0x3e, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, +0x76, 0x67, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, +0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, +0x2f, 0x73, 0x76, 0x67, 0x22, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x32, 0x34, 0x22, +0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x32, 0x34, 0x22, 0x20, 0x76, 0x69, 0x65, +0x77, 0x42, 0x6f, 0x78, 0x3d, 0x22, 0x30, 0x20, 0x30, 0x20, 0x32, 0x34, 0x20, 0x32, 0x34, 0x22, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x73, 0x74, +0x72, 0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6c, +0x6f, 0x72, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, +0x3d, 0x22, 0x32, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, +0x63, 0x61, 0x70, 0x3d, 0x22, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, +0x6f, 0x6b, 0x65, 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x6a, 0x6f, 0x69, 0x6e, 0x3d, 0x22, 0x72, 0x6f, +0x75, 0x6e, 0x64, 0x22, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x73, 0x74, 0x72, +0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x30, +0x20, 0x30, 0x68, 0x32, 0x34, 0x76, 0x32, 0x34, 0x48, 0x30, 0x7a, 0x22, 0x20, 0x66, 0x69, 0x6c, +0x6c, 0x3d, 0x22, 0x6e, 0x6f, 0x6e, 0x65, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, +0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x31, 0x30, 0x20, 0x31, 0x34, 0x6c, 0x31, 0x31, 0x20, +0x2d, 0x31, 0x31, 0x22, 0x20, 0x2f, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, +0x3d, 0x22, 0x4d, 0x32, 0x31, 0x20, 0x33, 0x6c, 0x2d, 0x36, 0x2e, 0x35, 0x20, 0x31, 0x38, 0x61, +0x2e, 0x35, 0x35, 0x20, 0x2e, 0x35, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, 0x20, 0x2d, 0x31, +0x20, 0x30, 0x6c, 0x2d, 0x33, 0x2e, 0x35, 0x20, 0x2d, 0x37, 0x6c, 0x2d, 0x37, 0x20, 0x2d, 0x33, +0x2e, 0x35, 0x61, 0x2e, 0x35, 0x35, 0x20, 0x2e, 0x35, 0x35, 0x20, 0x30, 0x20, 0x30, 0x20, 0x31, +0x20, 0x30, 0x20, 0x2d, 0x31, 0x6c, 0x31, 0x38, 0x20, 0x2d, 0x36, 0x2e, 0x35, 0x22, 0x20, 0x2f, +0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, +0x2f, 0x73, 0x76, 0x67, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, +0x2f, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, +0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x69, 0x76, +0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, +0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x63, 0x6f, 0x6e, 0x6e, 0x65, +0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3e, 0x3c, 0x2f, +0x64, 0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, +0x66, 0x6f, 0x6f, 0x74, 0x65, 0x72, 0x3e, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, +0x69, 0x76, 0x3e, 0x0d, 0x0a, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e, 0x0d, 0x0a, 0x0d, 0x0a, +0x3c, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x74, 0x65, +0x78, 0x74, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x3e, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, +0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x69, 0x6d, +0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0d, 0x0a, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x75, 0x72, 0x6c, 0x20, 0x3d, 0x20, +0x60, 0x77, 0x73, 0x3a, 0x2f, 0x2f, 0x24, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x6c, +0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, +0x7d, 0x2f, 0x77, 0x73, 0x60, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, +0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x6c, 0x65, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x65, 0x61, 0x20, +0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, +0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, +0x6e, 0x74, 0x27, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x63, +0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, +0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, +0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x28, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, +0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x27, 0x29, 0x3b, 0x0d, +0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x41, 0x70, 0x70, 0x28, 0x29, +0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, +0x6e, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x41, 0x70, 0x70, 0x28, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, +0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, +0x4c, 0x20, 0x3d, 0x20, 0x27, 0x27, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x69, 0x6e, 0x69, 0x74, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x28, 0x29, +0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x63, +0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, +0x74, 0x6f, 0x72, 0x28, 0x27, 0x66, 0x6f, 0x72, 0x6d, 0x27, 0x29, 0x2e, 0x61, 0x64, 0x64, 0x45, +0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x27, 0x73, 0x75, +0x62, 0x6d, 0x69, 0x74, 0x27, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, +0x28, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x65, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, +0x75, 0x6c, 0x74, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, +0x3d, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, +0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x63, 0x6f, 0x6d, 0x6d, 0x61, +0x6e, 0x64, 0x27, 0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, +0x65, 0x74, 0x2e, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x29, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, +0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, +0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x27, +0x29, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x27, 0x27, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, +0x6e, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x28, +0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, +0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x69, 0x6e, +0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x27, 0x3c, 0x64, 0x69, 0x76, 0x20, +0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x61, 0x64, 0x67, 0x65, 0x20, 0x6f, 0x72, 0x61, +0x6e, 0x67, 0x65, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x20, 0x43, 0x6f, 0x6e, 0x6e, +0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x2e, 0x2e, 0x27, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x3d, +0x20, 0x6e, 0x65, 0x77, 0x20, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x28, 0x75, +0x72, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x65, +0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6f, 0x6e, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x3d, +0x20, 0x6f, 0x6e, 0x4f, 0x70, 0x65, 0x6e, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6f, 0x6e, 0x63, 0x6c, +0x6f, 0x73, 0x65, 0x20, 0x3d, 0x20, 0x6f, 0x6e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x3b, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, +0x74, 0x2e, 0x6f, 0x6e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x3d, 0x20, 0x6f, 0x6e, +0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, +0x6f, 0x6e, 0x4f, 0x70, 0x65, 0x6e, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0x0d, +0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, +0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, +0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, 0x27, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, +0x73, 0x3d, 0x22, 0x62, 0x61, 0x64, 0x67, 0x65, 0x20, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x3e, +0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x20, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, +0x27, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x72, 0x6d, +0x69, 0x6e, 0x61, 0x6c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x28, 0x27, 0x43, 0x6f, 0x6e, 0x6e, 0x65, +0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x27, 0x20, 0x2b, 0x20, 0x75, 0x72, 0x6c, 0x29, +0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, +0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x43, 0x6c, 0x6f, 0x73, 0x65, +0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, +0x74, 0x75, 0x73, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, 0x3d, 0x20, +0x27, 0x3c, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x22, 0x62, 0x61, 0x64, +0x67, 0x65, 0x20, 0x72, 0x65, 0x64, 0x22, 0x3e, 0x3c, 0x2f, 0x64, 0x69, 0x76, 0x3e, 0x20, 0x44, +0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x27, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, +0x74, 0x28, 0x69, 0x6e, 0x69, 0x74, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2c, +0x20, 0x32, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, +0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x20, +0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x69, +0x6e, 0x61, 0x6c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x64, +0x61, 0x74, 0x61, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, +0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x65, 0x72, +0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x57, 0x72, 0x69, 0x74, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, +0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, +0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x29, +0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x6c, 0x65, 0x74, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x44, 0x61, +0x74, 0x65, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x22, 0x5b, 0x22, 0x20, 0x2b, 0x20, +0x6e, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, +0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x20, 0x2b, 0x20, 0x22, 0x5d, 0x20, 0x22, 0x20, +0x2b, 0x20, 0x64, 0x61, 0x74, 0x61, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, +0x65, 0x6e, 0x74, 0x41, 0x72, 0x65, 0x61, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, +0x4c, 0x20, 0x2b, 0x3d, 0x20, 0x27, 0x3c, 0x70, 0x3e, 0x27, 0x20, 0x2b, 0x20, 0x64, 0x61, 0x74, +0x61, 0x20, 0x2b, 0x20, 0x27, 0x3c, 0x2f, 0x70, 0x3e, 0x27, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, +0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x65, +0x61, 0x2e, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x6f, 0x70, 0x20, 0x3d, 0x20, 0x63, 0x6f, +0x6e, 0x74, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x65, 0x61, 0x2e, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, +0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, +0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x73, 0x69, +0x7a, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x65, 0x6d, 0x6f, +0x72, 0x79, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, +0x20, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, +0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x49, 0x6e, 0x74, 0x28, 0x64, 0x6f, 0x63, 0x75, +0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, +0x79, 0x49, 0x64, 0x28, 0x27, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x27, 0x29, 0x2e, 0x76, 0x61, +0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, +0x66, 0x20, 0x28, 0x69, 0x73, 0x4e, 0x61, 0x4e, 0x28, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, +0x69, 0x7a, 0x65, 0x29, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x20, +0x3d, 0x20, 0x31, 0x30, 0x30, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, +0x74, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, +0x74, 0x41, 0x72, 0x65, 0x61, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x6c, 0x65, 0x63, +0x74, 0x6f, 0x72, 0x41, 0x6c, 0x6c, 0x28, 0x27, 0x70, 0x27, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, +0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, +0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6c, 0x65, 0x74, 0x20, 0x69, 0x20, 0x3d, +0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x6c, 0x65, +0x6e, 0x67, 0x74, 0x68, 0x20, 0x2d, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, +0x65, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, +0x6e, 0x74, 0x41, 0x72, 0x65, 0x61, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x68, 0x69, +0x6c, 0x64, 0x28, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x29, 0x3b, 0x0d, 0x0a, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, +0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x28, 0x29, 0x20, 0x7b, +0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, +0x74, 0x41, 0x72, 0x65, 0x61, 0x2e, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x48, 0x54, 0x4d, 0x4c, 0x20, +0x3d, 0x20, 0x27, 0x27, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x3c, 0x2f, +0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3e, 0x0d, 0x0a, 0x0d, 0x0a, 0x3c, 0x2f, 0x68, 0x74, 0x6d, +0x6c, 0x3e, \ No newline at end of file diff --git a/stacktrace/esp_exception_decoder_rs.js b/stacktrace/esp_exception_decoder_rs.js deleted file mode 100644 index ff3fe8d..0000000 --- a/stacktrace/esp_exception_decoder_rs.js +++ /dev/null @@ -1,302 +0,0 @@ - -let wasm; - -const heap = new Array(32).fill(undefined); - -heap.push(undefined, null, true, false); - -function getObject(idx) { return heap[idx]; } - -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 36) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - -cachedTextDecoder.decode(); - -let cachegetUint8Memory0 = null; -function getUint8Memory0() { - if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { - cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); - } - return cachegetUint8Memory0; -} - -function getStringFromWasm0(ptr, len) { - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); -} - -let cachegetInt32Memory0 = null; -function getInt32Memory0() { - if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { - cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachegetInt32Memory0; -} - -const u32CvtShim = new Uint32Array(2); - -const uint64CvtShim = new BigUint64Array(u32CvtShim.buffer); - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -let WASM_VECTOR_LEN = 0; - -let cachedTextEncoder = new TextEncoder('utf-8'); - -const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' - ? function (arg, view) { - return cachedTextEncoder.encodeInto(arg, view); -} - : function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; -}); - -function passStringToWasm0(arg, malloc, realloc) { - - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length); - getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len); - - const mem = getUint8Memory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3); - const view = getUint8Memory0().subarray(ptr + offset, ptr + len); - const ret = encodeString(arg, view); - - offset += ret.written; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} - -function passArray8ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 1); - getUint8Memory0().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; -} -/** -* @param {Uint8Array} bin -* @param {string} dump -* @returns {Array} -*/ -export function decode(bin, dump) { - var ptr0 = passArray8ToWasm0(bin, wasm.__wbindgen_malloc); - var len0 = WASM_VECTOR_LEN; - var ptr1 = passStringToWasm0(dump, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - var ret = wasm.decode(ptr0, len0, ptr1, len1); - return takeObject(ret); -} - -/** -*/ -export class DecodedAddress { - - static __wrap(ptr) { - const obj = Object.create(DecodedAddress.prototype); - obj.ptr = ptr; - - return obj; - } - - __destroy_into_raw() { - const ptr = this.ptr; - this.ptr = 0; - - return ptr; - } - - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_decodedaddress_free(ptr); - } - /** - */ - get address() { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.__wbg_get_decodedaddress_address(retptr, this.ptr); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - u32CvtShim[0] = r0; - u32CvtShim[1] = r1; - const n0 = uint64CvtShim[0]; - return n0; - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - } - } - /** - * @param {BigInt} arg0 - */ - set address(arg0) { - uint64CvtShim[0] = arg0; - const low0 = u32CvtShim[0]; - const high0 = u32CvtShim[1]; - wasm.__wbg_set_decodedaddress_address(this.ptr, low0, high0); - } - /** - * @returns {string} - */ - get function_name() { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.decodedaddress_function_name(retptr, this.ptr); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - return getStringFromWasm0(r0, r1); - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_free(r0, r1); - } - } - /** - * @param {string} function_name - */ - set function_name(function_name) { - var ptr0 = passStringToWasm0(function_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - wasm.decodedaddress_set_function_name(this.ptr, ptr0, len0); - } - /** - * @returns {string} - */ - get location() { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.decodedaddress_location(retptr, this.ptr); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - return getStringFromWasm0(r0, r1); - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_free(r0, r1); - } - } - /** - * @param {string} location - */ - set location(location) { - var ptr0 = passStringToWasm0(location, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - wasm.decodedaddress_set_location(this.ptr, ptr0, len0); - } -} - -async function load(module, imports) { - if (typeof Response === 'function' && module instanceof Response) { - if (typeof WebAssembly.instantiateStreaming === 'function') { - try { - return await WebAssembly.instantiateStreaming(module, imports); - - } catch (e) { - if (module.headers.get('Content-Type') != 'application/wasm') { - console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); - - } else { - throw e; - } - } - } - - const bytes = await module.arrayBuffer(); - return await WebAssembly.instantiate(bytes, imports); - - } else { - const instance = await WebAssembly.instantiate(module, imports); - - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; - - } else { - return instance; - } - } -} - -async function init(input) { - if (typeof input === 'undefined') { - input = new URL('esp_exception_decoder_rs_bg.wasm', import.meta.url); - } - const imports = {}; - imports.wbg = {}; - imports.wbg.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); - }; - imports.wbg.__wbg_decodedaddress_new = function(arg0) { - var ret = DecodedAddress.__wrap(arg0); - return addHeapObject(ret); - }; - imports.wbg.__wbg_new_949bbc1147195c4e = function() { - var ret = new Array(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_push_284486ca27c6aa8b = function(arg0, arg1) { - var ret = getObject(arg0).push(getObject(arg1)); - return ret; - }; - imports.wbg.__wbindgen_throw = function(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); - }; - - if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { - input = fetch(input); - } - - - - const { instance, module } = await load(await input, imports); - - wasm = instance.exports; - init.__wbindgen_wasm_module = module; - - return wasm; -} - -export default init; - diff --git a/stacktrace/esp_exception_decoder_rs_bg.wasm b/stacktrace/esp_exception_decoder_rs_bg.wasm deleted file mode 100644 index e19f193..0000000 Binary files a/stacktrace/esp_exception_decoder_rs_bg.wasm and /dev/null differ diff --git a/stacktrace/esp_stacktrace_decoder_rs.js b/stacktrace/esp_stacktrace_decoder_rs.js new file mode 100644 index 0000000..20d35ed --- /dev/null +++ b/stacktrace/esp_stacktrace_decoder_rs.js @@ -0,0 +1,314 @@ +let wasm; + +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +let WASM_VECTOR_LEN = 0; + +const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1, 1) >>> 0; + getUint8ArrayMemory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; +} +/** + * @param {Uint8Array} bin + * @param {string} dump + * @returns {Array} + */ +export function decode(bin, dump) { + const ptr0 = passArray8ToWasm0(bin, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(dump, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.decode(ptr0, len0, ptr1, len1); + return ret; +} + +const DecodedAddressFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_decodedaddress_free(ptr >>> 0, 1)); + +export class DecodedAddress { + + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(DecodedAddress.prototype); + obj.__wbg_ptr = ptr; + DecodedAddressFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + DecodedAddressFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_decodedaddress_free(ptr, 0); + } + /** + * @returns {bigint} + */ + get address() { + const ret = wasm.__wbg_get_decodedaddress_address(this.__wbg_ptr); + return BigInt.asUintN(64, ret); + } + /** + * @param {bigint} arg0 + */ + set address(arg0) { + wasm.__wbg_set_decodedaddress_address(this.__wbg_ptr, arg0); + } + /** + * @returns {string} + */ + get function_name() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.decodedaddress_function_name(this.__wbg_ptr); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } + } + /** + * @param {string} function_name + */ + set function_name(function_name) { + const ptr0 = passStringToWasm0(function_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.decodedaddress_set_function_name(this.__wbg_ptr, ptr0, len0); + } + /** + * @returns {string} + */ + get location() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.decodedaddress_location(this.__wbg_ptr); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } + } + /** + * @param {string} location + */ + set location(location) { + const ptr0 = passStringToWasm0(location, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.decodedaddress_set_location(this.__wbg_ptr, ptr0, len0); + } +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbg_decodedaddress_new = function(arg0) { + const ret = DecodedAddress.__wrap(arg0); + return ret; + }; + imports.wbg.__wbg_new_78feb108b6472713 = function() { + const ret = new Array(); + return ret; + }; + imports.wbg.__wbg_push_737cfc8c1432c2c6 = function(arg0, arg1) { + const ret = arg0.push(arg1); + return ret; + }; + imports.wbg.__wbindgen_init_externref_table = function() { + const table = wasm.__wbindgen_export_0; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + ; + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + + return imports; +} + +function __wbg_init_memory(imports, memory) { + +} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedUint8ArrayMemory0 = null; + + + wasm.__wbindgen_start(); + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (typeof module !== 'undefined') { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (typeof module_or_path !== 'undefined') { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (typeof module_or_path === 'undefined') { + module_or_path = new URL('esp_stacktrace_decoder_rs_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync }; +export default __wbg_init; diff --git a/stacktrace/esp_stacktrace_decoder_rs_bg.wasm b/stacktrace/esp_stacktrace_decoder_rs_bg.wasm new file mode 100644 index 0000000..325ae93 Binary files /dev/null and b/stacktrace/esp_stacktrace_decoder_rs_bg.wasm differ diff --git a/stacktrace/index.html b/stacktrace/index.html index c4f2a2a..7e1f196 100644 --- a/stacktrace/index.html +++ b/stacktrace/index.html @@ -91,6 +91,7 @@ + @@ -101,6 +102,7 @@ + @@ -112,6 +114,7 @@ + @@ -134,7 +137,7 @@