diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 9b45ded..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(~/.platformio/penv/Scripts/platformio.exe run:*)", - "Bash(grep -n \"emitSockets\" \"c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src\"/*.cpp)" - ] - } -} diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 44054fa..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,526 +0,0 @@ -// -// !!! WARNING !!! AUTO-GENERATED FILE! -// PLEASE DO NOT MODIFY IT AND USE "platformio.ini": -// https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags -// -{ - "configurations": [ - { - "name": "PlatformIO", - "includePath": [ - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/include", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/LittleFS/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ESPAsyncWebServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/AsyncTCP/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/PubSubClient/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/SmartRC-CC1101-Driver-Lib", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ArduinoJson/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/newlib/platform_include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions/freertos", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/port/xtensa/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32/private_include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/heap/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/log/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps/sntp", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/lwip/src/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include/arch", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/platform_port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/soc", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/public_compat", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_pm/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ringbuf/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/vfs/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_wifi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_event/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_netif/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_eth/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcpip_adapter/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ipc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_trace/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_timer/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/mbedtls/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/esp_crt_bundle/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_update/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spi_flash/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bootloader_support/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nvs_flash/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/pthread/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/xtensa", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include/port/xtensa", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/esp_supplicant/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ieee802154/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/console", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/asio/asio/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/osi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/include/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/api/include/api", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/blufi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/host/bluedroid/api/include/api", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/tinycrypt/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/storage", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/btc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/client/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/server/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/core/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/models/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cbor/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/unity/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cmock/CMock/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/libcoap/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/nghttp2/lib/includes", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls/esp-tls-crypto", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_adc_cal/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hid/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcp_transport/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_client/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_server/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_ota/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_server/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/interface", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protobuf-c/protobuf-c", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/common", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/security", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/transports", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mdns/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_local_ctrl/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/sdmmc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_serial_slave_link/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_websocket_client/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/expat/expat/lib", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wear_levelling/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/diskio", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/vfs", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freemodbus/freemodbus/common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/jsmn/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json/cJSON", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/libsodium/src/libsodium/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/port_include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mqtt/esp-mqtt/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/openssl/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/perfmon/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spiffs/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ulp/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wifi_provisioning/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rmaker_common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_diagnostics/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rtc_store/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_insights/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_generator/upstream", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_schedule/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp_secure_cert_mgr/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rainmaker/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/gpio_button/button/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/qrcode/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ws2812_led", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_littlefs/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/tool", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/typedef", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/image", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/math", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/nn", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/layer", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/detect", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/model_zoo", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/driver/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/conversions/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dotprod/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/mem/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/hann/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_harris/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_nuttall/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/nuttall/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/flat_top/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/iir/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fir/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/add/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sub/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mul/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/addc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mulc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sqrt/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/add/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/addc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mulc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/sub/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fft/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dct/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/conv/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/test/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf_imu13states/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fb_gfx/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/dio_qspi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/cores/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/variants/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/BLE/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/BluetoothSerial/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/DNSServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/EEPROM/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ESP32/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/FFat/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdate/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdateServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/I2S/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Insights/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/RainMaker/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/USB/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", - "" - ], - "browse": { - "limitSymbolsToIncludedHeaders": true, - "path": [ - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/include", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WebServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/AsyncUDP/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ethernet/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ESPmDNS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPClient/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiClientSecure/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/LittleFS/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ESPAsyncWebServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/AsyncTCP/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/PubSubClient/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/SmartRC-CC1101-Driver-Lib", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", - "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/libdeps/esp32dev/ArduinoJson/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/newlib/platform_include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions/freertos", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/port/xtensa/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/include/soc/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hw_support/port/esp32/private_include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/heap/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/log/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/include/apps/sntp", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/lwip/src/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/lwip/port/esp32/include/arch", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/soc/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/hal/platform_port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/include/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rom/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/soc", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_system/port/public_compat", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/xtensa/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/driver/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_pm/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ringbuf/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/efuse/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/vfs/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_wifi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_event/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_netif/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_eth/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcpip_adapter/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_phy/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_ipc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_trace/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_timer/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/mbedtls/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mbedtls/esp_crt_bundle/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/app_update/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spi_flash/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bootloader_support/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nvs_flash/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/pthread/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/xtensa", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_gdbstub/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espcoredump/include/port/xtensa", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wpa_supplicant/esp_supplicant/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ieee802154/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/console", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/asio/asio/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/asio/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/osi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/include/esp32/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/api/include/api", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/blufi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/common/btc/profile/esp/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/host/bluedroid/api/include/api", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_common/tinycrypt/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_core/storage", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/btc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/client/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/mesh_models/server/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/core/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api/models/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/bt/esp_ble_mesh/api", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cbor/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/unity/unity/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/cmock/CMock/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/coap/libcoap/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/nghttp/nghttp2/lib/includes", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-tls/esp-tls-crypto", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_adc_cal/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_hid/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/tcp_transport/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_client/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_http_server/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_ota/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_https_server/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_lcd/interface", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protobuf-c/protobuf-c", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/common", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/security", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/protocomm/include/transports", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mdns/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_local_ctrl/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/sdmmc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_serial_slave_link/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_websocket_client/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/expat/expat/lib", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/expat/port/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wear_levelling/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/diskio", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/vfs", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fatfs/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freemodbus/freemodbus/common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/idf_test/include/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/jsmn/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json/cJSON", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/libsodium/src/libsodium/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/libsodium/port_include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/mqtt/esp-mqtt/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/openssl/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/perfmon/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/spiffs/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ulp/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/wifi_provisioning/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rmaker_common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_diagnostics/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/rtc_store/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_insights/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_parser/upstream", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/json_generator/upstream", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_schedule/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp_secure_cert_mgr/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_rainmaker/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/gpio_button/button/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/qrcode/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/ws2812_led", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp_littlefs/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/tool", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/typedef", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/image", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/math", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/nn", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/layer", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/detect", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp-dl/include/model_zoo", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/driver/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/esp32-camera/conversions/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dotprod/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/support/mem/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/hann/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_harris/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/blackman_nuttall/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/nuttall/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/windows/flat_top/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/iir/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fir/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/add/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sub/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mul/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/addc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/mulc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/math/sqrt/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/add/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/addc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mulc/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/sub/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/fft/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/dct/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/conv/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/common/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/matrix/mul/test/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/espressif__esp-dsp/modules/kalman/ekf_imu13states/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/fb_gfx/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/dio_qspi/include", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/cores/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/variants/esp32", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/BLE/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/BluetoothSerial/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/DNSServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/EEPROM/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/ESP32/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/FFat/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdate/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/HTTPUpdateServer/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/I2S/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Insights/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/RainMaker/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/USB/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/WiFiProv/src", - "C:/Users/oem/.platformio/packages/framework-arduinoespressif32/libraries/Wire/src", - "" - ] - }, - "defines": [ - "PLATFORMIO=60119", - "ARDUINO_ESP32_DEV", - "CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=1", - "CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=1", - "CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=1", - "CONFIG_ESP_TASK_WDT_PANIC=1", - "CONFIG_ESP_COREDUMP_DECODE_INFO=1", - "HAVE_CONFIG_H", - "MBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"", - "UNITY_INCLUDE_CONFIG_H", - "WITH_POSIX", - "_GNU_SOURCE", - "IDF_VER=\"v4.4.7-dirty\"", - "ESP_PLATFORM", - "_POSIX_READER_WRITER_LOCKS", - "ARDUINO_ARCH_ESP32", - "ESP32", - "F_CPU=240000000L", - "ARDUINO=10812", - "ARDUINO_VARIANT=\"esp32\"", - "ARDUINO_BOARD=\"Espressif ESP32 Dev Module\"", - "ARDUINO_PARTITION_huge_app", - "" - ], - "cStandard": "gnu99", - "cppStandard": "gnu++11", - "compilerPath": "C:/Users/oem/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-gcc.exe", - "compilerArgs": [ - "-mlongcalls", - "" - ] - } - ], - "version": 4 -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 080e70d..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index c6b0639..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,44 +0,0 @@ -// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY -// -// PlatformIO Debugging Solution -// -// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html -// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html - -{ - "version": "0.2.0", - "configurations": [ - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug", - "executable": "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/build/esp32dev/firmware.elf", - "projectEnvName": "esp32devdbg", - "toolchainBinDir": "C:/Users/oem/.platformio/packages/toolchain-xtensa-esp32/bin", - "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": { - "type": "PlatformIO", - "task": "Pre-Debug" - } - }, - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug (skip Pre-Debug)", - "executable": "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/build/esp32dev/firmware.elf", - "projectEnvName": "esp32devdbg", - "toolchainBinDir": "C:/Users/oem/.platformio/packages/toolchain-xtensa-esp32/bin", - "internalConsoleOptions": "openOnSessionStart" - }, - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug (without uploading)", - "executable": "c:/Users/oem/Documents/PlatformIO/Projects/ESPSomfy-RTS/.pio/build/esp32dev/firmware.elf", - "projectEnvName": "esp32devdbg", - "toolchainBinDir": "C:/Users/oem/.platformio/packages/toolchain-xtensa-esp32/bin", - "internalConsoleOptions": "openOnSessionStart", - "loadMode": "manual" - } - ] -} diff --git a/src/WResp.cpp b/src/WResp.cpp index 258dfd9..4c3d379 100644 --- a/src/WResp.cpp +++ b/src/WResp.cpp @@ -1,4 +1,6 @@ #include "WResp.h" +#include +#include void JsonSockEvent::beginEvent(AsyncWebSocket *server, const char *evt, char *buff, size_t buffSize) { this->server = server; this->buff = buff; @@ -69,6 +71,7 @@ void JsonResponse::_safecat(const char *val, bool escape) { } void AsyncJsonResp::beginResponse(AsyncWebServerRequest *request, char *buff, size_t buffSize) { + this->_request = request; this->buff = buff; this->buffSize = buffSize; this->buff[0] = 0x00; @@ -78,6 +81,9 @@ void AsyncJsonResp::beginResponse(AsyncWebServerRequest *request, char *buff, si } void AsyncJsonResp::endResponse() { if(strlen(this->buff)) this->flush(); + if(this->_request && this->_stream) { + this->_request->send(this->_stream); + } } void AsyncJsonResp::flush() { if(this->_stream && strlen(this->buff) > 0) { diff --git a/src/WResp.h b/src/WResp.h index 4235ff7..6412c40 100644 --- a/src/WResp.h +++ b/src/WResp.h @@ -1,10 +1,10 @@ -#include -#include #include #include "Somfy.h" #ifndef wresp_h #define wresp_h +class WebServer; + class JsonFormatter { protected: char *buff; @@ -66,6 +66,7 @@ class JsonResponse : public JsonFormatter { class AsyncJsonResp : public JsonFormatter { protected: void _safecat(const char *val, bool escape = false) override; + AsyncWebServerRequest *_request = nullptr; AsyncResponseStream *_stream = nullptr; public: void beginResponse(AsyncWebServerRequest *request, char *buff, size_t buffSize); diff --git a/src/Web.cpp b/src/Web.cpp index 9488d1e..cc54e17 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -29,7 +28,6 @@ extern Network net; //#define WEB_MAX_RESPONSE 34768 #define WEB_MAX_RESPONSE 4096 -static char g_content[WEB_MAX_RESPONSE]; static char g_async_content[WEB_MAX_RESPONSE]; @@ -42,13 +40,10 @@ static const char _encoding_text[] = "text/plain"; static const char _encoding_html[] = "text/html"; static const char _encoding_json[] = "application/json"; -WebServer apiServer(8082); -WebServer server(80); -AsyncWebServer asyncServer(81); +AsyncWebServer asyncServer(80); AsyncWebServer asyncApiServer(8081); void Web::startup() { Serial.println("Launching web server..."); - asyncServer.serveStatic("/", LittleFS, "/").setDefaultFile("index.html"); asyncServer.on("/loginContext", HTTP_GET, [](AsyncWebServerRequest *request) { AsyncJsonResponse *response = new AsyncJsonResponse(); @@ -62,64 +57,13 @@ void Web::startup() { response->setLength(); request->send(response); }); - asyncServer.begin(); asyncApiServer.begin(); Serial.println("Async API server started on port 8081"); } void Web::loop() { - server.handleClient(); delay(1); - apiServer.handleClient(); - delay(1); -} -void Web::sendCORSHeaders(WebServer &server) { - //server.sendHeader(F("Connection"), F("Keep-Alive")); - //server.sendHeader(F("Keep-Alive"), F("timeout=5, max=1000")); - //server.sendHeader(F("Access-Control-Allow-Origin"), F("*")); - //server.sendHeader(F("Access-Control-Max-Age"), F("600")); - //server.sendHeader(F("Access-Control-Allow-Methods"), F("PUT,POST,GET,OPTIONS")); - //server.sendHeader(F("Access-Control-Allow-Headers"), F("*")); -} -void Web::sendCacheHeaders(uint32_t seconds) { - server.sendHeader(F("Cache-Control"), F("public, max-age=604800, immutable")); } void Web::end() { - //server.end(); -} -void Web::handleDeserializationError(WebServer &server, DeserializationError &err) { - switch (err.code()) { - case DeserializationError::InvalidInput: - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}")); - break; - case DeserializationError::NoMemory: - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}")); - break; - default: - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}")); - break; - } -} -bool Web::isAuthenticated(WebServer &server, bool cfg) { - Serial.println("Checking authentication"); - if(settings.Security.type == security_types::None) return true; - else if(!cfg && (settings.Security.permissions & static_cast(security_permissions::ConfigOnly)) == 0x01) return true; - else if(server.hasHeader("apikey")) { - // Api key was supplied. - Serial.println("Checking API Key..."); - char token[65]; - memset(token, 0x00, sizeof(token)); - this->createAPIToken(server.client().remoteIP(), token); - // Compare the tokens. - if(String(token) != server.header("apikey")) return false; - server.sendHeader("apikey", token); - } - else { - // Send a 401 - Serial.println("Not authenticated..."); - server.send(401, "Unauthorized API Key"); - return false; - } - return true; } bool Web::isAuthenticated(AsyncWebServerRequest *request, bool cfg) { Serial.println("Checking async authentication"); @@ -174,936 +118,8 @@ bool Web::createAPIToken(const IPAddress ipAddress, char *token) { else createAPIToken(ipAddress.toString().c_str(), token); return true; } -void Web::handleLogout(WebServer &server) { - Serial.println("Logging out of webserver"); - server.sendHeader("Location", "/"); - server.sendHeader("Cache-Control", "no-cache"); - server.sendHeader("Set-Cookie", "ESPSOMFYID=0"); - server.send(301); -} -void Web::handleLogin(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - StaticJsonDocument<256> doc; - JsonObject obj = doc.to(); - char token[65]; - memset(&token, 0x00, sizeof(token)); - this->createAPIToken(server.client().remoteIP(), token); - obj["type"] = static_cast(settings.Security.type); - if(settings.Security.type == security_types::None) { - obj["apiKey"] = token; - obj["msg"] = "Success"; - obj["success"] = true; - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - return; - } - Serial.println("Web logging in..."); - char username[33] = ""; - char password[33] = ""; - char pin[5] = ""; - memset(username, 0x00, sizeof(username)); - memset(password, 0x00, sizeof(password)); - memset(pin, 0x00, sizeof(pin)); - if(server.hasArg("plain")) { - DynamicJsonDocument docin(512); - DeserializationError err = deserializeJson(docin, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject objin = docin.as(); - if(objin.containsKey("username") && objin["username"]) strlcpy(username, objin["username"], sizeof(username)); - if(objin.containsKey("password") && objin["password"]) strlcpy(password, objin["password"], sizeof(password)); - if(objin.containsKey("pin") && objin["pin"]) strlcpy(pin, objin["pin"], sizeof(pin)); - } - } - else { - if(server.hasArg("username")) strlcpy(username, server.arg("username").c_str(), sizeof(username)); - if(server.hasArg("password")) strlcpy(password, server.arg("password").c_str(), sizeof(password)); - if(server.hasArg("pin")) strlcpy(pin, server.arg("pin").c_str(), sizeof(pin)); - } - // At this point we should have all the data we need to login. - if(settings.Security.type == security_types::PinEntry) { - Serial.print("Validating pin "); - Serial.println(pin); - if(strlen(pin) == 0 || strcmp(pin, settings.Security.pin) != 0) { - obj["success"] = false; - obj["msg"] = "Invalid Pin Entry"; - } - else { - obj["success"] = true; - obj["msg"] = "Login successful"; - obj["apiKey"] = token; - } - } - else if(settings.Security.type == security_types::Password) { - if(strlen(username) == 0 || strlen(password) == 0 || strcmp(username, settings.Security.username) != 0 || strcmp(password, settings.Security.password) != 0) { - obj["success"] = false; - obj["msg"] = "Invalid username or password"; - } - else { - obj["success"] = true; - obj["msg"] = "Login successful"; - obj["apiKey"] = token; - } - } - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - return; -} -void Web::handleStreamFile(WebServer &server, const char *filename, const char *encoding) { - if(git.lockFS) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Filesystem update in progress\"}")); - return; - } - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - esp_task_wdt_reset(); - // Try gzip variant first; streamFile() auto-adds Content-Encoding: gzip for .gz files - String gzFilename = String(filename) + ".gz"; - File file = LittleFS.open(gzFilename.c_str(), "r"); - if (!file) { - file = LittleFS.open(filename, "r"); - } - if (!file) { - Serial.print("Error opening "); - Serial.println(filename); - server.send(500, _encoding_text, "Error opening file"); - return; - } - esp_task_wdt_delete(NULL); - server.streamFile(file, encoding); - file.close(); - esp_task_wdt_add(NULL); - esp_task_wdt_reset(); -} -void Web::handleController(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - settings.printAvailHeap(); - if (method == HTTP_POST || method == HTTP_GET) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - resp.addElem("maxRooms", (uint8_t)SOMFY_MAX_ROOMS); - resp.addElem("maxShades", (uint8_t)SOMFY_MAX_SHADES); - resp.addElem("maxGroups", (uint8_t)SOMFY_MAX_GROUPS); - resp.addElem("maxGroupedShades", (uint8_t)SOMFY_MAX_GROUPED_SHADES); - resp.addElem("maxLinkedRemotes", (uint8_t)SOMFY_MAX_LINKED_REMOTES); - resp.addElem("startingAddress", (uint32_t)somfy.startingAddress); - - resp.beginObject("transceiver"); - somfy.transceiver.toJSON(resp); - resp.endObject(); - resp.beginObject("version"); - git.toJSON(resp); - resp.endObject(); - resp.beginArray("rooms"); - somfy.toJSONRooms(resp); - resp.endArray(); - resp.beginArray("shades"); - somfy.toJSONShades(resp); - resp.endArray(); - resp.beginArray("groups"); - somfy.toJSONGroups(resp); - resp.endArray(); - resp.beginArray("repeaters"); - somfy.toJSONRepeaters(resp); - resp.endArray(); - resp.endObject(); - resp.endResponse(); - } - else server.send(404, _encoding_text, _response_404); -} -void Web::handleLoginContext(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - resp.addElem("type", static_cast(settings.Security.type)); - resp.addElem("permissions", settings.Security.permissions); - resp.addElem("serverId", settings.serverId); - resp.addElem("version", settings.fwVersion.name); - resp.addElem("model", "ESPSomfyRTS"); - resp.addElem("hostname", settings.hostname); - resp.endObject(); - resp.endResponse(); -} -void Web::handleGetRepeaters(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_GET) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginArray(); - somfy.toJSONRepeaters(resp); - resp.endArray(); - resp.endResponse(); - } - else server.send(404, _encoding_text, _response_404); -} -void Web::handleGetRooms(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_GET) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginArray(); - somfy.toJSONRooms(resp); - resp.endArray(); - resp.endResponse(); - } - else server.send(404, _encoding_text, _response_404); -} -void Web::handleGetShades(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_GET) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginArray(); - somfy.toJSONShades(resp); - resp.endArray(); - resp.endResponse(); - } - else server.send(404, _encoding_text, _response_404); -} -void Web::handleGetGroups(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_GET) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginArray(); - somfy.toJSONGroups(resp); - resp.endArray(); - resp.endResponse(); - } - else server.send(404, _encoding_text, _response_404); -} -void Web::handleShadeCommand(WebServer& server) { - webServer.sendCORSHeaders(server); - if (server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - uint8_t shadeId = 255; - uint8_t target = 255; - uint8_t stepSize = 0; - int8_t repeat = -1; - somfy_commands command = somfy_commands::My; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("shadeId")) { - shadeId = atoi(server.arg("shadeId").c_str()); - if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command")); - else if (server.hasArg("target")) target = atoi(server.arg("target").c_str()); - if (server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str()); - if(server.hasArg("stepSize")) stepSize = atoi(server.arg("stepSize").c_str()); - } - else if (server.hasArg("plain")) { - Serial.println("Sending Shade Command"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - if (obj.containsKey("command")) { - String scmd = obj["command"]; - command = translateSomfyCommand(scmd); - } - else if (obj.containsKey("target")) { - target = obj["target"].as(); - } - if (obj.containsKey("repeat")) repeat = obj["repeat"].as(); - if(obj.containsKey("stepSize")) stepSize = obj["stepSize"].as(); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); - SomfyShade* shade = somfy.getShadeById(shadeId); - if (shade) { - Serial.print("Received:"); - Serial.println(server.arg("plain")); - // Send the command to the shade. - if (target <= 100) - shade->moveToTarget(shade->transformPosition(target)); - else - shade->sendCommand(command, repeat > 0 ? repeat : shade->repeats, stepSize); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSONRef(resp); - resp.endObject(); - resp.endResponse(); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); - } - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); -} -void Web::handleRepeatCommand(WebServer& server) { - webServer.sendCORSHeaders(server); - HTTPMethod method = server.method(); - if (method == HTTP_OPTIONS) { server.send(200, "OK"); return; } - uint8_t shadeId = 255; - uint8_t groupId = 255; - uint8_t stepSize = 0; - int8_t repeat = -1; - somfy_commands command = somfy_commands::My; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if(server.hasArg("shadeId")) shadeId = atoi(server.arg("shadeId").c_str()); - else if(server.hasArg("groupId")) groupId = atoi(server.arg("groupId").c_str()); - if(server.hasArg("command")) command = translateSomfyCommand(server.arg("command")); - if(server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str()); - if(server.hasArg("stepSize")) stepSize = atoi(server.arg("stepSize").c_str()); - if(shadeId == 255 && groupId == 255 && server.hasArg("plain")) { - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - if(obj.containsKey("groupId")) groupId = obj["groupId"]; - if(obj.containsKey("stepSize")) stepSize = obj["stepSize"]; - if (obj.containsKey("command")) { - String scmd = obj["command"]; - command = translateSomfyCommand(scmd); - } - if (obj.containsKey("repeat")) repeat = obj["repeat"].as(); - } - } - //DynamicJsonDocument sdoc(512); - //JsonObject sobj = sdoc.to(); - if(shadeId != 255) { - SomfyShade *shade = somfy.getShadeById(shadeId); - if(!shade) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade reference could not be found.\"}")); - return; - } - if(shade->shadeType == shade_types::garage1 && command == somfy_commands::Prog) command = somfy_commands::Toggle; - if(!shade->isLastCommand(command)) { - // We are going to send this as a new command. - shade->sendCommand(command, repeat >= 0 ? repeat : shade->repeats, stepSize); - } - else { - shade->repeatFrame(repeat >= 0 ? repeat : shade->repeats); - } - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginArray(); - shade->toJSONRef(resp); - resp.endArray(); - resp.endResponse(); - } - else if(groupId != 255) { - SomfyGroup * group = somfy.getGroupById(groupId); - if(!group) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group reference could not be found.\"}")); - return; - } - if(!group->isLastCommand(command)) { - // We are going to send this as a new command. - group->sendCommand(command, repeat >= 0 ? repeat : group->repeats, stepSize); - } - else - group->repeatFrame(repeat >= 0 ? repeat : group->repeats); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSONRef(resp); - resp.endObject(); - resp.endResponse(); - - //group->toJSON(sobj); - //serializeJson(sdoc, g_content); - //server.send(200, _encoding_json, g_content); - } - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); - } -} -void Web::handleGroupCommand(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - uint8_t groupId = 255; - uint8_t stepSize = 0; - int8_t repeat = -1; - somfy_commands command = somfy_commands::My; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("groupId")) { - groupId = atoi(server.arg("groupId").c_str()); - if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command")); - if(server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str()); - if(server.hasArg("stepSize")) stepSize = atoi(server.arg("stepSize").c_str()); - } - else if (server.hasArg("plain")) { - Serial.println("Sending Group Command"); - DynamicJsonDocument doc(256); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("groupId")) groupId = obj["groupId"]; - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}")); - return; - } - if (obj.containsKey("command")) { - String scmd = obj["command"]; - command = translateSomfyCommand(scmd); - } - if(obj.containsKey("repeat")) repeat = obj["repeat"].as(); - if(obj.containsKey("stepSize")) stepSize = obj["stepSize"].as(); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); - SomfyGroup * group = somfy.getGroupById(groupId); - if (group) { - Serial.print("Received:"); - Serial.println(server.arg("plain")); - // Send the command to the group. - group->sendCommand(command, repeat >= 0 ? repeat : group->repeats, stepSize); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSONRef(resp); - resp.endObject(); - resp.endResponse(); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}")); - } - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); -} -void Web::handleTiltCommand(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - uint8_t shadeId = 255; - uint8_t target = 255; - somfy_commands command = somfy_commands::My; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("shadeId")) { - shadeId = atoi(server.arg("shadeId").c_str()); - if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command")); - else if(server.hasArg("target")) target = atoi(server.arg("target").c_str()); - } - else if (server.hasArg("plain")) { - Serial.println("Sending Shade Tilt Command"); - DynamicJsonDocument doc(256); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - if (obj.containsKey("command")) { - String scmd = obj["command"]; - command = translateSomfyCommand(scmd); - } - else if(obj.containsKey("target")) { - target = obj["target"].as(); - } - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); - SomfyShade* shade = somfy.getShadeById(shadeId); - if (shade) { - Serial.print("Received:"); - Serial.println(server.arg("plain")); - // Send the command to the shade. - if(target <= 100) - shade->moveToTiltTarget(shade->transformPosition(target)); - else - shade->sendTiltCommand(command); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSONRef(resp); - resp.endObject(); - resp.endResponse(); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); - } - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); -} -void Web::handleRoom(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_GET) { - if (server.hasArg("roomId")) { - int roomId = atoi(server.arg("roomId").c_str()); - SomfyRoom* room = somfy.getRoomById(roomId); - if (room) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - room->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}")); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"You must supply a valid room id.\"}")); - } - } - else if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing room. - if (server.hasArg("plain")) { - Serial.println("Updating a room"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("roomId")) { - SomfyRoom* room = somfy.getRoomById(obj["roomId"]); - if (room) { - uint8_t err = room->fromJSON(obj); - if(err == 0) { - room->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - room->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else { - snprintf(g_content, sizeof(g_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err); - server.send(500, _encoding_json, g_content); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}")); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}")); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}")); - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); -} -void Web::handleShade(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_GET) { - if (server.hasArg("shadeId")) { - int shadeId = atoi(server.arg("shadeId").c_str()); - SomfyShade* shade = somfy.getShadeById(shadeId); - if (shade) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"You must supply a valid shade id.\"}")); - } - } - else if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing shade. - if (server.hasArg("plain")) { - Serial.println("Updating a shade"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) { - SomfyShade* shade = somfy.getShadeById(obj["shadeId"]); - if (shade) { - uint8_t err = shade->fromJSON(obj); - if(err == 0) { - shade->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else { - snprintf(g_content, sizeof(g_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err); - server.send(500, _encoding_json, g_content); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); -} -void Web::handleGroup(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_GET) { - if (server.hasArg("groupId")) { - int groupId = atoi(server.arg("groupId").c_str()); - SomfyGroup* group = somfy.getGroupById(groupId); - if (group) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"You must supply a valid shade id.\"}")); - } - } - else if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing group. - if (server.hasArg("plain")) { - Serial.println("Updating a group"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("groupId")) { - SomfyGroup* group = somfy.getGroupById(obj["groupId"]); - if (group) { - group->fromJSON(obj); - group->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}")); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); -} -void Web::handleDiscovery(WebServer &server) { - HTTPMethod method = apiServer.method(); - if (method == HTTP_POST || method == HTTP_GET) { - Serial.println("Discovery Requested"); - char connType[10] = "Unknown"; - if(net.connType == conn_types_t::ethernet) strcpy(connType, "Ethernet"); - else if(net.connType == conn_types_t::wifi) strcpy(connType, "Wifi"); - - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - resp.addElem("serverId", settings.serverId); - resp.addElem("version", settings.fwVersion.name); - resp.addElem("latest", git.latest.name); - resp.addElem("model", "ESPSomfyRTS"); - resp.addElem("hostname", settings.hostname); - resp.addElem("authType", static_cast(settings.Security.type)); - resp.addElem("permissions", settings.Security.permissions); - resp.addElem("chipModel", settings.chipModel); - resp.addElem("connType", connType); - resp.addElem("checkForUpdate", settings.checkForUpdate); - resp.beginObject("memory"); - resp.addElem("max", ESP.getMaxAllocHeap()); - resp.addElem("free", ESP.getFreeHeap()); - resp.addElem("min", ESP.getMinFreeHeap()); - resp.addElem("total", ESP.getHeapSize()); - resp.addElem("uptime", (uint64_t)millis()); - resp.endObject(); - resp.beginArray("rooms"); - somfy.toJSONRooms(resp); - resp.endArray(); - resp.beginArray("shades"); - somfy.toJSONShades(resp); - resp.endArray(); - resp.beginArray("groups"); - somfy.toJSONGroups(resp); - resp.endArray(); - resp.endObject(); - resp.endResponse(); - net.needsBroadcast = true; - } - else - server.send(500, _encoding_text, "Invalid http method"); -} -void Web::handleBackup(WebServer &server, bool attach) { - webServer.sendCORSHeaders(server); - if(server.hasArg("attach")) attach = toBoolean(server.arg("attach").c_str(), attach); - if(attach) { - char filename[120]; - Timestamp ts; - char * iso = ts.getISOTime(); - // Replace the invalid characters as quickly as we can. - for(uint8_t i = 0; i < strlen(iso); i++) { - switch(iso[i]) { - case '.': - // Just trim off the ms. - iso[i] = '\0'; - break; - case ':': - iso[i] = '_'; - break; - } - } - snprintf(filename, sizeof(filename), "attachment; filename=\"ESPSomfyRTS %s.backup\"", iso); - Serial.println(filename); - server.sendHeader(F("Content-Disposition"), filename); - server.sendHeader(F("Access-Control-Expose-Headers"), F("Content-Disposition")); - } - Serial.println("Saving current shade information"); - somfy.writeBackup(); - if(somfy.backupData.length() == 0) { - server.send(500, _encoding_text, "backup failed"); - return; - } - server.send(200, _encoding_text, somfy.backupData); -} -void Web::handleSetPositions(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - uint8_t shadeId = (server.hasArg("shadeId")) ? atoi(server.arg("shadeId").c_str()) : 255; - int8_t pos = (server.hasArg("position")) ? atoi(server.arg("position").c_str()) : -1; - int8_t tiltPos = (server.hasArg("tiltPosition")) ? atoi(server.arg("tiltPosition").c_str()) : -1; - if(server.hasArg("plain")) { - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if(obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - if(obj.containsKey("position")) pos = obj["position"]; - if(obj.containsKey("tiltPosition")) tiltPos = obj["tiltPosition"]; - } - } - if(shadeId != 255) { - SomfyShade *shade = somfy.getShadeById(shadeId); - if(shade) { - if(pos >= 0) shade->target = shade->currentPos = pos; - if(tiltPos >= 0 && shade->tiltType != tilt_types::none) shade->tiltTarget = shade->currentTiltPos = tiltPos; - shade->emitState(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"An invalid shadeId was provided\"}")); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"shadeId was not provided\"}")); - } -} -void Web::handleSetSensor(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - uint8_t shadeId = (server.hasArg("shadeId")) ? atoi(server.arg("shadeId").c_str()) : 255; - uint8_t groupId = (server.hasArg("groupId")) ? atoi(server.arg("groupId").c_str()) : 255; - int8_t sunny = (server.hasArg("sunny")) ? toBoolean(server.arg("sunny").c_str(), false) ? 1 : 0 : -1; - int8_t windy = (server.hasArg("windy")) ? atoi(server.arg("windy").c_str()) : -1; - int8_t repeat = (server.hasArg("repeat")) ? atoi(server.arg("repeat").c_str()) : -1; - if(server.hasArg("plain")) { - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - this->handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if(obj.containsKey("shadeId")) shadeId = obj["shadeId"].as(); - if(obj.containsKey("groupId")) groupId = obj["groupId"].as(); - if(obj.containsKey("sunny")) { - if(obj["sunny"].is()) - sunny = obj["sunny"].as() ? 1 : 0; - else - sunny = obj["sunny"].as(); - } - if(obj.containsKey("windy")) { - if(obj["windy"].is()) - windy = obj["windy"].as() ? 1 : 0; - else - windy = obj["windy"].as(); - } - if(obj.containsKey("repeat")) repeat = obj["repeat"].as(); - } - } - if(shadeId != 255) { - SomfyShade *shade = somfy.getShadeById(shadeId); - if(shade) { - shade->sendSensorCommand(windy, sunny, repeat >= 0 ? (uint8_t)repeat : shade->repeats); - shade->emitState(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"An invalid shadeId was provided\"}")); - - } - else if(groupId != 255) { - SomfyGroup *group = somfy.getGroupById(groupId); - if(group) { - group->sendSensorCommand(windy, sunny, repeat >= 0 ? (uint8_t)repeat : group->repeats); - group->emitState(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"An invalid groupId was provided\"}")); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"shadeId was not provided\"}")); - } -} -void Web::handleDownloadFirmware(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - GitRepo repo; - GitRelease *rel = nullptr; - int8_t err = repo.getReleases(); - Serial.println("downloadFirmware called..."); - if(err == 0) { - if(server.hasArg("ver")) { - if(strcmp(server.arg("ver").c_str(), "latest") == 0) rel = &repo.releases[0]; - else if(strcmp(server.arg("ver").c_str(), "main") == 0) { - rel = &repo.releases[GIT_MAX_RELEASES]; - } - else { - for(uint8_t i = 0; i < GIT_MAX_RELEASES; i++) { - if(repo.releases[i].id == 0) continue; - if(strcmp(repo.releases[i].name, server.arg("ver").c_str()) == 0) { - rel = &repo.releases[i]; - } - } - } - if(rel) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - rel->toJSON(resp); - resp.endObject(); - resp.endResponse(); - strcpy(git.targetRelease, rel->name); - git.status = GIT_AWAITING_UPDATE; - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Release not found in repo.\"}")); - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Release version not supplied.\"}")); - } - else { - server.send(err, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error communicating with Github.\"}")); - } -} -void Web::handleNotFound(WebServer &server) { - HTTPMethod method = server.method(); - Serial.printf("Request %s 404-%d ", server.uri().c_str(), method); - switch (method) { - case HTTP_POST: - Serial.print("POST "); - break; - case HTTP_GET: - Serial.print("GET "); - break; - case HTTP_PUT: - Serial.print("PUT "); - break; - case HTTP_OPTIONS: - Serial.println("OPTIONS "); - server.send(200, "OK"); - return; - default: - Serial.print("["); - Serial.print(method); - Serial.print("]"); - break; - - } - snprintf(g_content, sizeof(g_content), "404 Service Not Found: %s", server.uri().c_str()); - server.send(404, _encoding_text, g_content); -} -void Web::handleReboot(WebServer &server) { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - Serial.println("Rebooting ESP..."); - rebootDelay.reboot = true; - rebootDelay.rebootTime = millis() + 500; - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully started reboot\"}"); - } - else { - server.send(201, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } -} // ===================================================== -// Async API Handlers (port 8082) +// Async API Handlers // ===================================================== // Helper: get a query param as String, or empty if missing static String asyncParam(AsyncWebServerRequest *request, const char *name) { @@ -1855,33 +871,7 @@ void Web::handleNotFound(AsyncWebServerRequest *request) { void Web::begin() { Serial.println("Creating Web MicroServices..."); - server.enableCORS(true); - const char *keys[1] = {"apikey"}; - server.collectHeaders(keys, 1); - // API Server Handlers - apiServer.collectHeaders(keys, 1); - apiServer.enableCORS(true); - apiServer.on("/discovery", []() { webServer.handleDiscovery(apiServer); }); - apiServer.on("/rooms", []() {webServer.handleGetRooms(apiServer); }); - apiServer.on("/shades", []() { webServer.handleGetShades(apiServer); }); - apiServer.on("/groups", []() { webServer.handleGetGroups(apiServer); }); - apiServer.on("/login", []() { webServer.handleLogin(apiServer); }); - apiServer.onNotFound([]() { webServer.handleNotFound(apiServer); }); - apiServer.on("/controller", []() { webServer.handleController(apiServer); }); - apiServer.on("/shadeCommand", []() { webServer.handleShadeCommand(apiServer); }); - apiServer.on("/groupCommand", []() { webServer.handleGroupCommand(apiServer); }); - apiServer.on("/tiltCommand", []() { webServer.handleTiltCommand(apiServer); }); - apiServer.on("/repeatCommand", []() { webServer.handleRepeatCommand(apiServer); }); - apiServer.on("/room", HTTP_GET, [] () { webServer.handleRoom(apiServer); }); - apiServer.on("/shade", HTTP_GET, [] () { webServer.handleShade(apiServer); }); - apiServer.on("/group", HTTP_GET, [] () { webServer.handleGroup(apiServer); }); - apiServer.on("/setPositions", []() { webServer.handleSetPositions(apiServer); }); - apiServer.on("/setSensor", []() { webServer.handleSetSensor(apiServer); }); - apiServer.on("/downloadFirmware", []() { webServer.handleDownloadFirmware(apiServer); }); - apiServer.on("/backup", []() { webServer.handleBackup(apiServer); }); - apiServer.on("/reboot", []() { webServer.handleReboot(apiServer); }); - - // Async API Server (port 8082) + // Async API Server (port 8081) DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "*"); @@ -1927,133 +917,168 @@ void Web::begin() { }); // Web Interface - server.on("/tiltCommand", []() { webServer.handleTiltCommand(server); }); - server.on("/repeatCommand", []() { webServer.handleRepeatCommand(server); }); - server.on("/shadeCommand", []() { webServer.handleShadeCommand(server); }); - server.on("/groupCommand", []() { webServer.handleGroupCommand(server); }); - server.on("/setPositions", []() { webServer.handleSetPositions(server); }); - server.on("/setSensor", []() { webServer.handleSetSensor(server); }); - server.on("/upnp.xml", []() { SSDP.schema(server.client()); }); - server.on("/", []() { webServer.handleStreamFile(server, "/index.html", _encoding_html); }); - server.on("/login", []() { webServer.handleLogin(server); }); - server.on("/loginContext", []() { webServer.handleLoginContext(server); }); - server.on("/shades.cfg", []() { webServer.handleStreamFile(server, "/shades.cfg", _encoding_text); }); - server.on("/shades.tmp", []() { webServer.handleStreamFile(server, "/shades.tmp", _encoding_text); }); - server.on("/getReleases", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } + + // Web Interface + // Command endpoints - delegate to async handler methods + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/tiltCommand", + [](AsyncWebServerRequest *request, JsonVariant &json) { webServer.handleTiltCommand(request, json); })); + asyncServer.on("/tiltCommand", HTTP_GET, [](AsyncWebServerRequest *request) { JsonVariant v; webServer.handleTiltCommand(request, v); }); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/repeatCommand", + [](AsyncWebServerRequest *request, JsonVariant &json) { webServer.handleRepeatCommand(request, json); })); + asyncServer.on("/repeatCommand", HTTP_GET, [](AsyncWebServerRequest *request) { JsonVariant v; webServer.handleRepeatCommand(request, v); }); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/shadeCommand", + [](AsyncWebServerRequest *request, JsonVariant &json) { webServer.handleShadeCommand(request, json); })); + asyncServer.on("/shadeCommand", HTTP_GET, [](AsyncWebServerRequest *request) { JsonVariant v; webServer.handleShadeCommand(request, v); }); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/groupCommand", + [](AsyncWebServerRequest *request, JsonVariant &json) { webServer.handleGroupCommand(request, json); })); + asyncServer.on("/groupCommand", HTTP_GET, [](AsyncWebServerRequest *request) { JsonVariant v; webServer.handleGroupCommand(request, v); }); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setPositions", + [](AsyncWebServerRequest *request, JsonVariant &json) { webServer.handleSetPositions(request, json); })); + asyncServer.on("/setPositions", HTTP_GET, [](AsyncWebServerRequest *request) { JsonVariant v; webServer.handleSetPositions(request, v); }); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setSensor", + [](AsyncWebServerRequest *request, JsonVariant &json) { webServer.handleSetSensor(request, json); })); + asyncServer.on("/setSensor", HTTP_GET, [](AsyncWebServerRequest *request) { JsonVariant v; webServer.handleSetSensor(request, v); }); + + asyncServer.on("/upnp.xml", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncResponseStream *response = request->beginResponseStream("text/xml"); + SSDP.schema(*response); + request->send(response); + }); + + // / and /loginContext are already handled by serveStatic and startup() + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/login", + [](AsyncWebServerRequest *request, JsonVariant &json) { webServer.handleLogin(request, json); })); + asyncServer.on("/login", HTTP_GET, [](AsyncWebServerRequest *request) { JsonVariant v; webServer.handleLogin(request, v); }); + + asyncServer.on("/shades.cfg", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(LittleFS, "/shades.cfg", _encoding_text); + }); + asyncServer.on("/shades.tmp", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(LittleFS, "/shades.tmp", _encoding_text); + }); + + asyncServer.on("/getReleases", HTTP_GET, [](AsyncWebServerRequest *request) { GitRepo repo; repo.getReleases(); git.setCurrentRelease(repo); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - repo.toJSON(resp); + // Inline GitRepo::toJSON + resp.beginObject("fwVersion"); + serializeAppVersion(resp, settings.fwVersion); + resp.endObject(); + resp.beginObject("appVersion"); + serializeAppVersion(resp, settings.appVersion); + resp.endObject(); + resp.beginArray("releases"); + for(uint8_t i = 0; i < GIT_MAX_RELEASES + 1; i++) { + if(repo.releases[i].id == 0) continue; + resp.beginObject(); + serializeGitRelease(&repo.releases[i], resp); + resp.endObject(); + } + resp.endArray(); resp.endObject(); resp.endResponse(); }); - server.on("/downloadFirmware", []() { webServer.handleDownloadFirmware(server); }); - server.on("/cancelFirmware", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - // If we are currently downloading the filesystem we cannot cancel. + + asyncServer.on("/downloadFirmware", HTTP_GET, [](AsyncWebServerRequest *request) { webServer.handleDownloadFirmware(request); }); + + asyncServer.on("/cancelFirmware", HTTP_GET, [](AsyncWebServerRequest *request) { if(!git.lockFS) { git.status = GIT_UPDATE_CANCELLING; - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - git.toJSON(resp); + serializeGitVersion(resp); resp.endObject(); resp.endResponse(); git.cancelled = true; } else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Cannot cancel during filesystem update.\"}")); + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Cannot cancel during filesystem update.\"}")); } }); - server.on("/backup", []() { webServer.handleBackup(server, true); }); - server.on("/restore", HTTP_POST, []() { - webServer.sendCORSHeaders(server); - server.sendHeader("Connection", "close"); - if(webServer.uploadSuccess) { - server.send(200, _encoding_json, "{\"status\":\"Success\",\"desc\":\"Restoring Shade settings\"}"); - restore_options_t opts; - if(server.hasArg("data")) { - Serial.println(server.arg("data")); - StaticJsonDocument<256> doc; - DeserializationError err = deserializeJson(doc, server.arg("data")); - if (err) { - webServer.handleDeserializationError(server, err); - return; + + asyncServer.on("/backup", HTTP_GET, [](AsyncWebServerRequest *request) { webServer.handleBackup(request); }); + + asyncServer.on("/restore", HTTP_POST, + [](AsyncWebServerRequest *request) { + if(webServer.uploadSuccess) { + request->send(200, _encoding_json, "{\"status\":\"Success\",\"desc\":\"Restoring Shade settings\"}"); + restore_options_t opts; + if(asyncHasParam(request, "data")) { + String dataStr = asyncParam(request, "data"); + Serial.println(dataStr); + StaticJsonDocument<256> doc; + DeserializationError err = deserializeJson(doc, dataStr); + if(err) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; + } + else { + JsonObject obj = doc.as(); + opts.fromJSON(obj); + } } else { - JsonObject obj = doc.as(); - opts.fromJSON(obj); + Serial.println("No restore options sent. Using defaults..."); + opts.shades = true; } + ShadeConfigFile::restore(&somfy, "/shades.tmp", opts); + Serial.println("Rebooting ESP for restored settings..."); + rebootDelay.reboot = true; + rebootDelay.rebootTime = millis() + 1000; } - else { - Serial.println("No restore options sent. Using defaults..."); - opts.shades = true; - } - ShadeConfigFile::restore(&somfy, "/shades.tmp", opts); - Serial.println("Rebooting ESP for restored settings..."); - rebootDelay.reboot = true; - rebootDelay.rebootTime = millis() + 1000; - } - }, []() { + }, + [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { esp_task_wdt_reset(); - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { + if(index == 0) { webServer.uploadSuccess = false; - Serial.printf("Restore: %s\n", upload.filename.c_str()); - // Begin by opening a new temporary file. + Serial.printf("Restore: %s\n", filename.c_str()); File fup = LittleFS.open("/shades.tmp", "w"); fup.close(); } - else if (upload.status == UPLOAD_FILE_WRITE) { + if(len > 0) { File fup = LittleFS.open("/shades.tmp", "a"); - //upload.buf[upload.currentSize] = 0x00; - //Serial.print((char *)upload.buf); - fup.write(upload.buf, upload.currentSize); + fup.write(data, len); fup.close(); } - else if (upload.status == UPLOAD_FILE_END) { + if(final) { webServer.uploadSuccess = true; } - }); - server.on("/index.js", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/index.js", "text/javascript"); }); - server.on("/main.css", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/main.css", "text/css"); }); - server.on("/widgets.css", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/widgets.css", "text/css"); }); - server.on("/icons.css", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/icons.css", "text/css"); }); - server.on("/favicon.png", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/favicon.png", "image/png"); }); - server.on("/icon.png", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/icon.png", "image/png"); }); - server.on("/icon.svg", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/icon.svg", "image/svg+xml"); }); - server.on("/apple-icon.png", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/apple-icon.png", "image/png"); }); - server.onNotFound([]() { webServer.handleNotFound(server); }); - server.on("/controller", []() { webServer.handleController(server); }); - server.on("/rooms", []() { webServer.handleGetRooms(server); }); - server.on("/shades", []() { webServer.handleGetShades(server); }); - server.on("/groups", []() { webServer.handleGetGroups(server); }); - server.on("/room", []() { webServer.handleRoom(server); }); - server.on("/shade", []() { webServer.handleShade(server); }); - server.on("/group", []() { webServer.handleGroup(server); }); - server.on("/getNextRoom", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + + // Static file routes removed - handled by serveStatic in startup() + + asyncServer.on("/controller", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { webServer.handleController(request); }); + asyncServer.on("/rooms", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { webServer.handleGetRooms(request); }); + asyncServer.on("/shades", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { webServer.handleGetShades(request); }); + asyncServer.on("/groups", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { webServer.handleGetGroups(request); }); + asyncServer.on("/room", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { webServer.handleRoom(request); }); + asyncServer.on("/shade", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { webServer.handleShade(request); }); + asyncServer.on("/group", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { webServer.handleGroup(request); }); + + asyncServer.on("/getNextRoom", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); resp.addElem("roomId", somfy.getNextRoomId()); resp.endObject(); resp.endResponse(); }); - server.on("/getNextShade", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } + + asyncServer.on("/getNextShade", HTTP_GET, [](AsyncWebServerRequest *request) { uint8_t shadeId = somfy.getNextShadeId(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); resp.addElem("shadeId", shadeId); resp.addElem("remoteAddress", (uint32_t)somfy.getNextRemoteAddress(shadeId)); @@ -2062,12 +1087,12 @@ void Web::begin() { resp.addElem("proto", static_cast(somfy.transceiver.config.proto)); resp.endObject(); resp.endResponse(); - }); - server.on("/getNextGroup", []() { - webServer.sendCORSHeaders(server); + }); + + asyncServer.on("/getNextGroup", HTTP_GET, [](AsyncWebServerRequest *request) { uint8_t groupId = somfy.getNextGroupId(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); resp.addElem("groupId", groupId); resp.addElem("remoteAddress", (uint32_t)somfy.getNextRemoteAddress(groupId)); @@ -2075,443 +1100,355 @@ void Web::begin() { resp.addElem("proto", static_cast(somfy.transceiver.config.proto)); resp.endObject(); resp.endResponse(); - }); - server.on("/addRoom", []() { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - SomfyRoom * room = nullptr; - if (method == HTTP_POST || method == HTTP_PUT) { + }); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/addRoom", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}")); return; } Serial.println("Adding a room"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); + JsonObject obj = json.as(); + if(somfy.roomCount() > SOMFY_MAX_ROOMS) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of rooms exceeded.\"}")); return; } - else { - JsonObject obj = doc.as(); - Serial.println("Counting rooms"); - if (somfy.roomCount() > SOMFY_MAX_ROOMS) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of rooms exceeded.\"}")); - return; - } - else { - Serial.println("Adding room"); - room = somfy.addRoom(obj); - if (!room) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding room.\"}")); - return; - } - } + SomfyRoom *room = somfy.addRoom(obj); + if(room) { + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeRoom(room, resp); + resp.endObject(); + resp.endResponse(); } - } - if (room) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - room->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error saving Somfy Room.\"}")); - } - }); - server.on("/addShade", []() { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - SomfyShade* shade = nullptr; - if (method == HTTP_POST || method == HTTP_PUT) { + else { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding room.\"}")); + } + })); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/addShade", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); return; } Serial.println("Adding a shade"); - DynamicJsonDocument doc(1024); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); + JsonObject obj = json.as(); + if(somfy.shadeCount() > SOMFY_MAX_SHADES) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of shades exceeded.\"}")); return; } - else { - JsonObject obj = doc.as(); - Serial.println("Counting shades"); - if (somfy.shadeCount() > SOMFY_MAX_SHADES) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of shades exceeded.\"}")); - return; - } - else { - Serial.println("Adding shade"); - shade = somfy.addShade(obj); - if (!shade) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding shade.\"}")); - return; - } - } + SomfyShade *shade = somfy.addShade(obj); + if(shade) { + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeShade(shade, resp); + resp.endObject(); + resp.endResponse(); } - } - if (shade) { - //Serial.println("Serializing shade"); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error saving Somfy Shade.\"}")); - } - }); - server.on("/addGroup", []() { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - SomfyGroup * group = nullptr; - if (method == HTTP_POST || method == HTTP_PUT) { + else { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding shade.\"}")); + } + })); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/addGroup", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); return; } Serial.println("Adding a group"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); + JsonObject obj = json.as(); + if(somfy.groupCount() > SOMFY_MAX_GROUPS) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of groups exceeded.\"}")); return; } + SomfyGroup *group = somfy.addGroup(obj); + if(group) { + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeGroup(group, resp); + resp.endObject(); + resp.endResponse(); + } else { - JsonObject obj = doc.as(); - Serial.println("Counting shades"); - if (somfy.groupCount() > SOMFY_MAX_GROUPS) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of groups exceeded.\"}")); - return; - } - else { - Serial.println("Adding group"); - group = somfy.addGroup(obj); - if (!group) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding group.\"}")); - return; + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding group.\"}")); + } + })); + + asyncServer.on("/groupOptions", HTTP_GET, [](AsyncWebServerRequest *request) { + if(asyncHasParam(request, "groupId")) { + int groupId = atoi(asyncParam(request, "groupId").c_str()); + SomfyGroup* group = somfy.getGroupById(groupId); + if(group) { + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeGroup(group, resp); + resp.beginArray("availShades"); + for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { + SomfyShade *shade = &somfy.shades[i]; + if(shade->getShadeId() != 255) { + bool isLinked = false; + for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) { + if(group->linkedShades[j] == shade->getShadeId()) { + isLinked = true; + break; + } + } + if(!isLinked) { + resp.beginObject(); + serializeShadeRef(shade, resp); + resp.endObject(); + } } } + resp.endArray(); + resp.endObject(); + resp.endResponse(); } - } - if (group) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSON(resp); - resp.endObject(); - resp.endResponse(); + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); } else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error saving Somfy Group.\"}")); - } - }); - server.on("/groupOptions", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_GET || method == HTTP_POST) { - if (server.hasArg("groupId")) { - int groupId = atoi(server.arg("groupId").c_str()); - SomfyGroup* group = somfy.getGroupById(groupId); - if (group) { - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSON(resp); - resp.beginArray("availShades"); - for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { - SomfyShade *shade = &somfy.shades[i]; - if(shade->getShadeId() != 255) { - bool isLinked = false; - for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) { - if(group->linkedShades[j] == shade->getShadeId()) { - isLinked = true; - break; - } - } - if(!isLinked) { - resp.beginObject(); - shade->toJSONRef(resp); - resp.endObject(); - } - } - } - resp.endArray(); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"You must supply a valid group id.\"}")); - } - } - - }); - server.on("/saveRoom", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing room. - if (server.hasArg("plain")) { - Serial.println("Updating a room"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("roomId")) { - SomfyRoom* room = somfy.getRoomById(obj["roomId"]); - if (room) { - room->fromJSON(obj); - room->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - room->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}")); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}")); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}")); + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"You must supply a valid group id.\"}")); } }); - server.on("/saveShade", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing shade. - if (server.hasArg("plain")) { - Serial.println("Updating a shade"); - DynamicJsonDocument doc(1024); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/saveRoom", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}")); return; } + Serial.println("Updating a room"); + JsonObject obj = json.as(); + if(obj.containsKey("roomId")) { + SomfyRoom* room = somfy.getRoomById(obj["roomId"]); + if(room) { + room->fromJSON(obj); + room->save(); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeRoom(room, resp); + resp.endObject(); + resp.endResponse(); } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) { - SomfyShade* shade = somfy.getShadeById(obj["shadeId"]); - if (shade) { - int8_t err = shade->fromJSON(obj); - if(err == 0) { - shade->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else { - snprintf(g_content, sizeof(g_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err); - server.send(500, _encoding_json, g_content); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}")); + } + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}")); + })); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/saveShade", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); return; } + Serial.println("Updating a shade"); + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) { + SomfyShade* shade = somfy.getShadeById(obj["shadeId"]); + if(shade) { + int8_t err = shade->fromJSON(obj); + if(err == 0) { + shade->save(); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeShade(shade, resp); + resp.endObject(); + resp.endResponse(); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); - } - }); - server.on("/saveGroup", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing shade. - if (server.hasArg("plain")) { - Serial.println("Updating a group"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("groupId")) { - SomfyGroup* group = somfy.getGroupById(obj["groupId"]); - if (group) { - group->fromJSON(obj); - group->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - group->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); + else { + snprintf(g_async_content, sizeof(g_async_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err); + request->send(500, _encoding_json, g_async_content); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}")); } + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); - } - }); - server.on("/setMyPosition", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - uint8_t shadeId = 255; - int8_t pos = -1; - int8_t tilt = -1; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("shadeId")) { - shadeId = atoi(server.arg("shadeId").c_str()); - if(server.hasArg("pos")) pos = atoi(server.arg("pos").c_str()); - if(server.hasArg("tilt")) tilt = atoi(server.arg("tilt").c_str()); - } - else if (server.hasArg("plain")) { - DynamicJsonDocument doc(256); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - if(obj.containsKey("pos")) pos = obj["pos"].as(); - if(obj.containsKey("tilt")) tilt = obj["tilt"].as(); + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); + })); + + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/saveGroup", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); return; } + Serial.println("Updating a group"); + JsonObject obj = json.as(); + if(obj.containsKey("groupId")) { + SomfyGroup* group = somfy.getGroupById(obj["groupId"]); + if(group) { + group->fromJSON(obj); + group->save(); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeGroup(group, resp); + resp.endObject(); + resp.endResponse(); } + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group Id not found.\"}")); + } + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}")); + })); + + // setMyPosition - supports both GET with query params and POST/PUT with JSON body + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setMyPosition", + [](AsyncWebServerRequest *request, JsonVariant &json) { + uint8_t shadeId = 255; + int8_t pos = -1; + int8_t tilt = -1; + if(!json.isNull()) { + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) shadeId = obj["shadeId"]; + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); return; } + if(obj.containsKey("pos")) pos = obj["pos"].as(); + if(obj.containsKey("tilt")) tilt = obj["tilt"].as(); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); SomfyShade* shade = somfy.getShadeById(shadeId); - if (shade) { - // Send the command to the shade. + if(shade) { if(tilt < 0) tilt = shade->myPos; if(shade->tiltType == tilt_types::none) tilt = -1; if(pos >= 0 && pos <= 100) shade->setMyPosition(shade->transformPosition(pos), shade->transformPosition(tilt)); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSONRef(resp); - resp.endObject(); - resp.endResponse(); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); - } - } - else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}")); - }); - server.on("/setRollingCode", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - uint8_t shadeId = 255; - uint16_t rollingCode = 0; - if (server.hasArg("plain")) { - // Its coming in the body. - StaticJsonDocument<129> doc; - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - if(obj.containsKey("rollingCode")) rollingCode = obj["rollingCode"]; - } - } - else if (server.hasArg("shadeId")) { - shadeId = atoi(server.arg("shadeId").c_str()); - rollingCode = atoi(server.arg("rollingCode").c_str()); - } - SomfyShade* shade = nullptr; - if (shadeId != 255) shade = somfy.getShadeById(shadeId); - if (!shade) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to set rolling code\"}")); - } - else { - shade->setRollingCode(rollingCode); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - shade->toJSON(resp); + serializeShadeRef(shade, resp); resp.endObject(); resp.endResponse(); } - } - }); - server.on("/setPaired", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - uint8_t shadeId = 255; - bool paired = false; - if(server.hasArg("plain")) { - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if(err) { - webServer.handleDeserializationError(server, err); - return; - } else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - if(obj.containsKey("paired")) paired = obj["paired"]; + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); } + })); + asyncServer.on("/setMyPosition", HTTP_GET, [](AsyncWebServerRequest *request) { + uint8_t shadeId = 255; + int8_t pos = -1; + int8_t tilt = -1; + if(asyncHasParam(request, "shadeId")) { + shadeId = atoi(asyncParam(request, "shadeId").c_str()); + if(asyncHasParam(request, "pos")) pos = atoi(asyncParam(request, "pos").c_str()); + if(asyncHasParam(request, "tilt")) tilt = atoi(asyncParam(request, "tilt").c_str()); } - else if (server.hasArg("shadeId")) - shadeId = atoi(server.arg("shadeId").c_str()); - if(server.hasArg("paired")) - paired = toBoolean(server.arg("paired").c_str(), false); - SomfyShade* shade = nullptr; - if (shadeId != 255) shade = somfy.getShadeById(shadeId); - if (!shade) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to pair\"}")); + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); return; } + SomfyShade* shade = somfy.getShadeById(shadeId); + if(shade) { + if(tilt < 0) tilt = shade->myPos; + if(shade->tiltType == tilt_types::none) tilt = -1; + if(pos >= 0 && pos <= 100) + shade->setMyPosition(shade->transformPosition(pos), shade->transformPosition(tilt)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeShadeRef(shade, resp); + resp.endObject(); + resp.endResponse(); } else { - shade->paired = paired; - shade->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); + } + }); + + // setRollingCode - supports both query params and JSON body + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setRollingCode", + [](AsyncWebServerRequest *request, JsonVariant &json) { + uint8_t shadeId = 255; + uint16_t rollingCode = 0; + if(!json.isNull()) { + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) shadeId = obj["shadeId"]; + if(obj.containsKey("rollingCode")) rollingCode = obj["rollingCode"]; + } + SomfyShade* shade = nullptr; + if(shadeId != 255) shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to set rolling code\"}")); + } + else { + shade->setRollingCode(rollingCode); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeShade(shade, resp); + resp.endObject(); + resp.endResponse(); + } + })); + asyncServer.on("/setRollingCode", HTTP_GET, [](AsyncWebServerRequest *request) { + uint8_t shadeId = 255; + uint16_t rollingCode = 0; + if(asyncHasParam(request, "shadeId")) { + shadeId = atoi(asyncParam(request, "shadeId").c_str()); + rollingCode = atoi(asyncParam(request, "rollingCode").c_str()); + } + SomfyShade* shade = nullptr; + if(shadeId != 255) shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to set rolling code\"}")); + } + else { + shade->setRollingCode(rollingCode); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - shade->toJSON(resp); + serializeShade(shade, resp); resp.endObject(); resp.endResponse(); } }); - server.on("/unpairShade", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { + + // setPaired - supports both query params and JSON body + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setPaired", + [](AsyncWebServerRequest *request, JsonVariant &json) { uint8_t shadeId = 255; - if (server.hasArg("plain")) { - // Its coming in the body. - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - } + bool paired = false; + if(!json.isNull()) { + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) shadeId = obj["shadeId"]; + if(obj.containsKey("paired")) paired = obj["paired"]; } - else if (server.hasArg("shadeId")) - shadeId = atoi(server.arg("shadeId").c_str()); SomfyShade* shade = nullptr; - if (shadeId != 255) shade = somfy.getShadeById(shadeId); - if (!shade) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to unpair\"}")); + if(shadeId != 255) shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to pair\"}")); + } + else { + shade->paired = paired; + shade->save(); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeShade(shade, resp); + resp.endObject(); + resp.endResponse(); + } + })); + asyncServer.on("/setPaired", HTTP_GET, [](AsyncWebServerRequest *request) { + uint8_t shadeId = 255; + bool paired = false; + if(asyncHasParam(request, "shadeId")) + shadeId = atoi(asyncParam(request, "shadeId").c_str()); + if(asyncHasParam(request, "paired")) + paired = toBoolean(asyncParam(request, "paired").c_str(), false); + SomfyShade* shade = nullptr; + if(shadeId != 255) shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to pair\"}")); + } + else { + shade->paired = paired; + shade->save(); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeShade(shade, resp); + resp.endObject(); + resp.endResponse(); + } + }); + + // unpairShade + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/unpairShade", + [](AsyncWebServerRequest *request, JsonVariant &json) { + uint8_t shadeId = 255; + if(!json.isNull()) { + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) shadeId = obj["shadeId"]; + } + SomfyShade* shade = nullptr; + if(shadeId != 255) shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to unpair\"}")); } else { if(shade->bitLength == 56) @@ -2520,408 +1457,376 @@ void Web::begin() { shade->sendCommand(somfy_commands::Prog, 1); shade->paired = false; shade->save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - shade->toJSON(resp); + serializeShade(shade, resp); resp.endObject(); resp.endResponse(); } + })); + asyncServer.on("/unpairShade", HTTP_GET, [](AsyncWebServerRequest *request) { + uint8_t shadeId = 255; + if(asyncHasParam(request, "shadeId")) + shadeId = atoi(asyncParam(request, "shadeId").c_str()); + SomfyShade* shade = nullptr; + if(shadeId != 255) shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to unpair\"}")); } - }); - server.on("/linkRepeater", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - // We are adding a linked repeater. + else { + if(shade->bitLength == 56) + shade->sendCommand(somfy_commands::Prog, 7); + else + shade->sendCommand(somfy_commands::Prog, 1); + shade->paired = false; + shade->save(); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeShade(shade, resp); + resp.endObject(); + resp.endResponse(); + } + }); + + // linkRepeater + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/linkRepeater", + [](AsyncWebServerRequest *request, JsonVariant &json) { uint32_t address = 0; - if (server.hasArg("plain")) { + if(!json.isNull()) { Serial.println("Linking a repeater"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("address")) address = obj["address"]; - else if(obj.containsKey("remoteAddress")) address = obj["remoteAddress"]; - } + JsonObject obj = json.as(); + if(obj.containsKey("address")) address = obj["address"]; + else if(obj.containsKey("remoteAddress")) address = obj["remoteAddress"]; + } + if(address == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); } - else if(server.hasArg("address")) - address = atoi(server.arg("address").c_str()); - if(address == 0) - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); else { somfy.linkRepeater(address); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginArray(); - somfy.toJSONRepeaters(resp); + serializeRepeaters(resp); resp.endArray(); resp.endResponse(); } + })); + asyncServer.on("/linkRepeater", HTTP_GET, [](AsyncWebServerRequest *request) { + uint32_t address = 0; + if(asyncHasParam(request, "address")) + address = atoi(asyncParam(request, "address").c_str()); + if(address == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); + } + else { + somfy.linkRepeater(address); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginArray(); + serializeRepeaters(resp); + resp.endArray(); + resp.endResponse(); } }); - server.on("/unlinkRepeater", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - // We are adding a linked repeater. + + // unlinkRepeater + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/unlinkRepeater", + [](AsyncWebServerRequest *request, JsonVariant &json) { uint32_t address = 0; - if (server.hasArg("plain")) { + if(!json.isNull()) { Serial.println("Unlinking a repeater"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("address")) address = obj["address"]; - else if(obj.containsKey("remoteAddress")) address = obj["remoteAddress"]; - } + JsonObject obj = json.as(); + if(obj.containsKey("address")) address = obj["address"]; + else if(obj.containsKey("remoteAddress")) address = obj["remoteAddress"]; + } + if(address == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); } - else if(server.hasArg("address")) - address = atoi(server.arg("address").c_str()); - if(address == 0) - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); else { somfy.unlinkRepeater(address); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginArray(); - somfy.toJSONRepeaters(resp); + serializeRepeaters(resp); resp.endArray(); resp.endResponse(); } + })); + asyncServer.on("/unlinkRepeater", HTTP_GET, [](AsyncWebServerRequest *request) { + uint32_t address = 0; + if(asyncHasParam(request, "address")) + address = atoi(asyncParam(request, "address").c_str()); + if(address == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}")); + } + else { + somfy.unlinkRepeater(address); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginArray(); + serializeRepeaters(resp); + resp.endArray(); + resp.endResponse(); } }); - - server.on("/unlinkRemote", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing shade by adding a linked remote. - if (server.hasArg("plain")) { - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) { - SomfyShade* shade = somfy.getShadeById(obj["shadeId"]); - if (shade) { - if (obj.containsKey("remoteAddress")) { - shade->unlinkRemote(obj["remoteAddress"]); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Remote address not provided.\"}")); - } - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); + + // unlinkRemote + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/unlinkRemote", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No remote object supplied.\"}")); return; } + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) { + SomfyShade* shade = somfy.getShadeById(obj["shadeId"]); + if(shade) { + if(obj.containsKey("remoteAddress")) { + shade->unlinkRemote(obj["remoteAddress"]); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No remote object supplied.\"}")); - } - }); - server.on("/linkRemote", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - // We are updating an existing shade by adding a linked remote. - if (server.hasArg("plain")) { - Serial.println("Linking a remote"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) { - SomfyShade* shade = somfy.getShadeById(obj["shadeId"]); - if (shade) { - if (obj.containsKey("remoteAddress")) { - if (obj.containsKey("rollingCode")) shade->linkRemote(obj["remoteAddress"], obj["rollingCode"]); - else shade->linkRemote(obj["remoteAddress"]); - } - else { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Remote address not provided.\"}")); - } - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - shade->toJSON(resp); - resp.endObject(); - resp.endResponse(); - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); + else { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Remote address not provided.\"}")); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - } - } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No remote object supplied.\"}")); - } - }); - server.on("/linkToGroup", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("plain")) { - Serial.println("Linking a shade to a group"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - uint8_t shadeId = obj.containsKey("shadeId") ? obj["shadeId"] : 0; - uint8_t groupId = obj.containsKey("groupId") ? obj["groupId"] : 0; - if(groupId == 0) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not provided.\"}")); - return; - } - if(shadeId == 0) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not provided.\"}")); - return; - } - SomfyGroup * group = somfy.getGroupById(groupId); - if(!group) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not found.\"}")); - return; - } - SomfyShade * shade = somfy.getShadeById(shadeId); - if(!shade) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not found.\"}")); - return; - } - group->linkShade(shadeId); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - group->toJSON(resp); + serializeShade(shade, resp); resp.endObject(); resp.endResponse(); } + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No linking object supplied.\"}")); - } - }); - server.on("/unlinkFromGroup", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("plain")) { - Serial.println("Unlinking a shade from a group"); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - switch (err.code()) { - case DeserializationError::InvalidInput: - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}")); - break; - case DeserializationError::NoMemory: - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}")); - break; - default: - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}")); - break; + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); + })); + + // linkRemote + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/linkRemote", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No remote object supplied.\"}")); return; } + Serial.println("Linking a remote"); + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) { + SomfyShade* shade = somfy.getShadeById(obj["shadeId"]); + if(shade) { + if(obj.containsKey("remoteAddress")) { + if(obj.containsKey("rollingCode")) shade->linkRemote(obj["remoteAddress"], obj["rollingCode"]); + else shade->linkRemote(obj["remoteAddress"]); } - } - else { - JsonObject obj = doc.as(); - uint8_t shadeId = obj.containsKey("shadeId") ? obj["shadeId"] : 0; - uint8_t groupId = obj.containsKey("groupId") ? obj["groupId"] : 0; - if(groupId == 0) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not provided.\"}")); - return; + else { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Remote address not provided.\"}")); } - if(shadeId == 0) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not provided.\"}")); - return; - } - SomfyGroup * group = somfy.getGroupById(groupId); - if(!group) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not found.\"}")); - return; - } - SomfyShade * shade = somfy.getShadeById(shadeId); - if(!shade) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not found.\"}")); - return; - } - group->unlinkShade(shadeId); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - group->toJSON(resp); + serializeShade(shade, resp); resp.endObject(); resp.endResponse(); } + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade Id not found.\"}")); } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No unlinking object supplied.\"}")); - } - }); - server.on("/deleteRoom", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - uint8_t roomId = 0; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("roomId")) { - roomId = atoi(server.arg("roomId").c_str()); + else request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); + })); + + // linkToGroup + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/linkToGroup", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No linking object supplied.\"}")); return; } + Serial.println("Linking a shade to a group"); + JsonObject obj = json.as(); + uint8_t shadeId = obj.containsKey("shadeId") ? obj["shadeId"] : 0; + uint8_t groupId = obj.containsKey("groupId") ? obj["groupId"] : 0; + if(groupId == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not provided.\"}")); + return; } - else if (server.hasArg("plain")) { + if(shadeId == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not provided.\"}")); + return; + } + SomfyGroup *group = somfy.getGroupById(groupId); + if(!group) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not found.\"}")); + return; + } + SomfyShade *shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not found.\"}")); + return; + } + group->linkShade(shadeId); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeGroup(group, resp); + resp.endObject(); + resp.endResponse(); + })); + + // unlinkFromGroup + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/unlinkFromGroup", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No unlinking object supplied.\"}")); return; } + Serial.println("Unlinking a shade from a group"); + JsonObject obj = json.as(); + uint8_t shadeId = obj.containsKey("shadeId") ? obj["shadeId"] : 0; + uint8_t groupId = obj.containsKey("groupId") ? obj["groupId"] : 0; + if(groupId == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not provided.\"}")); + return; + } + if(shadeId == 0) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not provided.\"}")); + return; + } + SomfyGroup *group = somfy.getGroupById(groupId); + if(!group) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group id not found.\"}")); + return; + } + SomfyShade *shade = somfy.getShadeById(shadeId); + if(!shade) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade id not found.\"}")); + return; + } + group->unlinkShade(shadeId); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeGroup(group, resp); + resp.endObject(); + resp.endResponse(); + })); + + // deleteRoom - supports GET with query params and POST/PUT with JSON body + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/deleteRoom", + [](AsyncWebServerRequest *request, JsonVariant &json) { + uint8_t roomId = 0; + if(!json.isNull()) { Serial.println("Deleting a Room"); - DynamicJsonDocument doc(256); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("roomId")) roomId = obj["roomId"]; - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}")); - } + JsonObject obj = json.as(); + if(obj.containsKey("roomId")) roomId = obj["roomId"]; + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}")); return; } } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}")); + SomfyRoom* room = somfy.getRoomById(roomId); + if(!room) request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room with the specified id not found.\"}")); + else { + somfy.deleteRoom(roomId); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Room deleted.\"}")); + } + })); + asyncServer.on("/deleteRoom", HTTP_GET, [](AsyncWebServerRequest *request) { + uint8_t roomId = 0; + if(asyncHasParam(request, "roomId")) { + roomId = atoi(asyncParam(request, "roomId").c_str()); } + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}")); return; } SomfyRoom* room = somfy.getRoomById(roomId); - if (!room) server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room with the specified id not found.\"}")); + if(!room) request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room with the specified id not found.\"}")); else { somfy.deleteRoom(roomId); - server.send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Room deleted.\"}")); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Room deleted.\"}")); } - }); - server.on("/deleteShade", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - uint8_t shadeId = 255; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("shadeId")) { - shadeId = atoi(server.arg("shadeId").c_str()); - } - else if (server.hasArg("plain")) { + }); + + // deleteShade - supports GET with query params and POST/PUT with JSON body + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/deleteShade", + [](AsyncWebServerRequest *request, JsonVariant &json) { + uint8_t shadeId = 255; + if(!json.isNull()) { Serial.println("Deleting a shade"); - DynamicJsonDocument doc(256); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - } + JsonObject obj = json.as(); + if(obj.containsKey("shadeId")) shadeId = obj["shadeId"]; + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); return; } } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); + SomfyShade* shade = somfy.getShadeById(shadeId); + if(!shade) request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); + else if(shade->isInGroup()) { + request->send(400, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"This shade is a member of a group and cannot be deleted.\"}")); + } + else { + somfy.deleteShade(shadeId); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Shade deleted.\"}")); + } + })); + asyncServer.on("/deleteShade", HTTP_GET, [](AsyncWebServerRequest *request) { + uint8_t shadeId = 255; + if(asyncHasParam(request, "shadeId")) { + shadeId = atoi(asyncParam(request, "shadeId").c_str()); } + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); return; } SomfyShade* shade = somfy.getShadeById(shadeId); - if (!shade) server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); + if(!shade) request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}")); else if(shade->isInGroup()) { - server.send(400, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"This shade is a member of a group and cannot be deleted.\"}")); + request->send(400, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"This shade is a member of a group and cannot be deleted.\"}")); } else { somfy.deleteShade(shadeId); - server.send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Shade deleted.\"}")); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Shade deleted.\"}")); } - }); - server.on("/deleteGroup", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - uint8_t groupId = 255; - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { - if (server.hasArg("groupId")) { - groupId = atoi(server.arg("groupId").c_str()); - } - else if (server.hasArg("plain")) { + }); + + // deleteGroup - supports GET with query params and POST/PUT with JSON body + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/deleteGroup", + [](AsyncWebServerRequest *request, JsonVariant &json) { + uint8_t groupId = 255; + if(!json.isNull()) { Serial.println("Deleting a group"); - DynamicJsonDocument doc(256); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - if (obj.containsKey("groupId")) groupId = obj["groupId"]; - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}")); - } + JsonObject obj = json.as(); + if(obj.containsKey("groupId")) groupId = obj["groupId"]; + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}")); return; } } - else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); + SomfyGroup *group = somfy.getGroupById(groupId); + if(!group) request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}")); + else { + somfy.deleteGroup(groupId); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Group deleted.\"}")); + } + })); + asyncServer.on("/deleteGroup", HTTP_GET, [](AsyncWebServerRequest *request) { + uint8_t groupId = 255; + if(asyncHasParam(request, "groupId")) { + groupId = atoi(asyncParam(request, "groupId").c_str()); } - SomfyGroup * group = somfy.getGroupById(groupId); - if (!group) server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}")); + else { request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}")); return; } + SomfyGroup *group = somfy.getGroupById(groupId); + if(!group) request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}")); else { somfy.deleteGroup(groupId); - server.send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Group deleted.\"}")); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Group deleted.\"}")); } - }); - server.on("/updateFirmware", HTTP_POST, []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - if (Update.hasError()) - server.send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating firmware: \"}"); - else - server.send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated firmware\"}"); - rebootDelay.reboot = true; - rebootDelay.rebootTime = millis() + 500; - }, []() { - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { + }); + + // updateFirmware - file upload + asyncServer.on("/updateFirmware", HTTP_POST, + [](AsyncWebServerRequest *request) { + if(Update.hasError()) + request->send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating firmware: \"}"); + else + request->send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated firmware\"}"); + rebootDelay.reboot = true; + rebootDelay.rebootTime = millis() + 500; + }, + [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { + if(index == 0) { webServer.uploadSuccess = false; - Serial.printf("Update: %s - %d\n", upload.filename.c_str(), upload.totalSize); - //if(!Update.begin(upload.totalSize, U_SPIFFS)) { - if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size + Serial.printf("Update: %s\n", filename.c_str()); + if(!Update.begin(UPDATE_SIZE_UNKNOWN)) { Update.printError(Serial); } else { - somfy.transceiver.end(); // Shut down the radio so we do not get any interrupts during this process. + somfy.transceiver.end(); mqtt.end(); } } - else if(upload.status == UPLOAD_FILE_ABORTED) { - Serial.printf("Upload of %s aborted\n", upload.filename.c_str()); - Update.abort(); - } - else if (upload.status == UPLOAD_FILE_WRITE) { - /* flashing firmware to ESP*/ - if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + if(len > 0) { + if(Update.write(data, len) != len) { Update.printError(Serial); - Serial.printf("Upload of %s aborted invalid size %d\n", upload.filename.c_str(), upload.currentSize); + Serial.printf("Upload of %s aborted invalid size %d\n", filename.c_str(), len); Update.abort(); } } - else if (upload.status == UPLOAD_FILE_END) { - if (Update.end(true)) { //true to set the size to the current progress - Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + if(final) { + if(Update.end(true)) { + Serial.printf("Update Success: %u\nRebooting...\n", index + len); webServer.uploadSuccess = true; } else { @@ -2930,75 +1835,65 @@ void Web::begin() { } esp_task_wdt_reset(); }); - server.on("/updateShadeConfig", HTTP_POST, []() { - if(git.lockFS) { - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Filesystem update in progress\"}")); - return; - } - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - server.sendHeader("Connection", "close"); - server.send(200, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Updating Shade Config: \"}"); - }, []() { - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { + + // updateShadeConfig - file upload + asyncServer.on("/updateShadeConfig", HTTP_POST, + [](AsyncWebServerRequest *request) { + if(git.lockFS) { + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Filesystem update in progress\"}")); + return; + } + request->send(200, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Updating Shade Config: \"}"); + }, + [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { + if(index == 0) { Serial.printf("Update: shades.cfg\n"); File fup = LittleFS.open("/shades.tmp", "w"); fup.close(); } - else if (upload.status == UPLOAD_FILE_WRITE) { - /* flashing littlefs to ESP*/ - if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { - File fup = LittleFS.open("/shades.tmp", "a"); - fup.write(upload.buf, upload.currentSize); - fup.close(); - } + if(len > 0) { + File fup = LittleFS.open("/shades.tmp", "a"); + fup.write(data, len); + fup.close(); } - else if (upload.status == UPLOAD_FILE_END) { + if(final) { somfy.loadShadesFile("/shades.tmp"); } }); - server.on("/updateApplication", HTTP_POST, []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - server.sendHeader("Connection", "close"); - if (Update.hasError()) - server.send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating application: \"}"); - else - server.send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated application\"}"); - rebootDelay.reboot = true; - rebootDelay.rebootTime = millis() + 500; - }, []() { - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { + + // updateApplication - file upload + asyncServer.on("/updateApplication", HTTP_POST, + [](AsyncWebServerRequest *request) { + if(Update.hasError()) + request->send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating application: \"}"); + else + request->send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated application\"}"); + rebootDelay.reboot = true; + rebootDelay.rebootTime = millis() + 500; + }, + [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { + if(index == 0) { webServer.uploadSuccess = false; - Serial.printf("Update: %s %d\n", upload.filename.c_str(), upload.totalSize); - //if(!Update.begin(upload.totalSize, U_SPIFFS)) { - if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS)) { //start with max available size and tell it we are updating the file system. + Serial.printf("Update: %s\n", filename.c_str()); + if(!Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS)) { Update.printError(Serial); } else { - somfy.transceiver.end(); // Shut down the radio so we do not get any interrupts during this process. + somfy.transceiver.end(); mqtt.end(); } } - else if(upload.status == UPLOAD_FILE_ABORTED) { - Serial.printf("Upload of %s aborted\n", upload.filename.c_str()); - Update.abort(); - somfy.commit(); - } - else if (upload.status == UPLOAD_FILE_WRITE) { - /* flashing littlefs to ESP*/ - if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + if(len > 0) { + if(Update.write(data, len) != len) { Update.printError(Serial); - Serial.printf("Upload of %s aborted invalid size %d\n", upload.filename.c_str(), upload.currentSize); + Serial.printf("Upload of %s aborted invalid size %d\n", filename.c_str(), len); Update.abort(); } } - else if (upload.status == UPLOAD_FILE_END) { - if (Update.end(true)) { //true to set the size to the current progress + if(final) { + if(Update.end(true)) { webServer.uploadSuccess = true; - Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + Serial.printf("Update Success: %u\nRebooting...\n", index + len); somfy.commit(); } else { @@ -3008,22 +1903,18 @@ void Web::begin() { } esp_task_wdt_reset(); }); - server.on("/scanaps", []() { - webServer.sendCORSHeaders(server); + + asyncServer.on("/scanaps", HTTP_GET, [](AsyncWebServerRequest *request) { esp_task_wdt_reset(); - - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } esp_task_wdt_delete(NULL); if(net.softAPOpened) WiFi.disconnect(false); int n = WiFi.scanNetworks(false, true); esp_task_wdt_add(NULL); - Serial.print("Scanned "); Serial.print(n); Serial.println(" networks"); - // Ok we need to chunk this response as well. - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); resp.beginObject("connected"); resp.addElem("name", settings.WIFI.ssid); @@ -3033,7 +1924,7 @@ void Web::begin() { resp.endObject(); resp.beginArray("accessPoints"); for(int i = 0; i < n; ++i) { - if(WiFi.SSID(i).length() == 0 || WiFi.RSSI(i) < -95) continue; // Ignore hidden and weak networks that we cannot connect to anyway. + if(WiFi.SSID(i).length() == 0 || WiFi.RSSI(i) < -95) continue; resp.beginObject(); resp.addElem("name", WiFi.SSID(i).c_str()); resp.addElem("channel", (int32_t)WiFi.channel(i)); @@ -3044,533 +1935,374 @@ void Web::begin() { resp.endArray(); resp.endObject(); resp.endResponse(); - }); - server.on("/reboot", []() { webServer.handleReboot(server);}); - server.on("/saveSecurity", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - Serial.print("Error parsing JSON "); - Serial.println(err.c_str()); - String msg = err.c_str(); - server.send(400, _encoding_html, "Error parsing JSON body
" + msg); - } - else { - JsonObject obj = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - settings.Security.fromJSON(obj); - settings.Security.save(); - char token[65]; - webServer.createAPIToken(server.client().remoteIP(), token); - obj["apiKey"] = token; - DynamicJsonDocument sdoc(1024); - JsonObject sobj = sdoc.to(); - settings.Security.toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); + }); + + asyncServer.on("/reboot", WebRequestMethodComposite(HTTP_POST) | HTTP_PUT, [](AsyncWebServerRequest *request) { webServer.handleReboot(request); }); + + // saveSecurity + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/saveSecurity", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(400, _encoding_html, "Error parsing JSON body"); + return; } - else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } - }); - server.on("/getSecurity", []() { - webServer.sendCORSHeaders(server); + JsonObject obj = json.as(); + settings.Security.fromJSON(obj); + settings.Security.save(); + char token[65]; + webServer.createAPIToken(request->client()->remoteIP(), token); + DynamicJsonDocument sdoc(1024); + JsonObject sobj = sdoc.to(); + settings.Security.toJSON(sobj); + sobj["apiKey"] = token; + serializeJson(sdoc, g_async_content, sizeof(g_async_content)); + request->send(200, _encoding_json, g_async_content); + })); + + // getSecurity + asyncServer.on("/getSecurity", HTTP_GET, [](AsyncWebServerRequest *request) { DynamicJsonDocument doc(512); JsonObject obj = doc.to(); settings.Security.toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - }); - server.on("/saveRadio", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - Serial.print("Error parsing JSON "); - Serial.println(err.c_str()); - String msg = err.c_str(); - server.send(400, _encoding_html, "Error parsing JSON body
" + msg); - } - else { - JsonObject obj = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - somfy.transceiver.fromJSON(obj); - somfy.transceiver.save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - somfy.transceiver.toJSON(resp); - resp.endObject(); - resp.endResponse(); + serializeJson(doc, g_async_content, sizeof(g_async_content)); + request->send(200, _encoding_json, g_async_content); + }); + + // saveRadio + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/saveRadio", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(400, _encoding_html, "Error parsing JSON body"); + return; } - else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } - }); - server.on("/getRadio", []() { - webServer.sendCORSHeaders(server); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + JsonObject obj = json.as(); + somfy.transceiver.fromJSON(obj); + somfy.transceiver.save(); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); + resp.beginObject(); + serializeTransceiverConfig(resp); + resp.endObject(); + resp.endResponse(); + })); + + // getRadio + asyncServer.on("/getRadio", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - somfy.transceiver.toJSON(resp); + serializeTransceiverConfig(resp); resp.endObject(); resp.endResponse(); - }); - server.on("/sendRemoteCommand", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - HTTPMethod method = server.method(); - if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { + }); + + // sendRemoteCommand - supports GET with query params and POST/PUT with JSON body + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/sendRemoteCommand", + [](AsyncWebServerRequest *request, JsonVariant &json) { somfy_frame_t frame; uint8_t repeats = 0; - if (server.hasArg("address")) { - frame.remoteAddress = atoi(server.arg("address").c_str()); - if (server.hasArg("encKey")) frame.encKey = atoi(server.arg("encKey").c_str()); - if (server.hasArg("command")) frame.cmd = translateSomfyCommand(server.arg("command")); - if (server.hasArg("rcode")) frame.rollingCode = atoi(server.arg("rcode").c_str()); - if (server.hasArg("repeats")) repeats = atoi(server.arg("repeats").c_str()); + if(!json.isNull()) { + JsonObject obj = json.as(); + String scmd; + if(obj.containsKey("address")) frame.remoteAddress = obj["address"]; + if(obj.containsKey("command")) scmd = obj["command"].as(); + if(obj.containsKey("repeats")) repeats = obj["repeats"]; + if(obj.containsKey("rcode")) frame.rollingCode = obj["rcode"]; + if(obj.containsKey("encKey")) frame.encKey = obj["encKey"]; + frame.cmd = translateSomfyCommand(scmd.c_str()); } - else if (server.hasArg("plain")) { - StaticJsonDocument<128> doc; - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - String scmd; - if (obj.containsKey("address")) frame.remoteAddress = obj["address"]; - if (obj.containsKey("command")) scmd = obj["command"].as(); - if (obj.containsKey("repeats")) repeats = obj["repeats"]; - if (obj.containsKey("rcode")) frame.rollingCode = obj["rcode"]; - if (obj.containsKey("encKey")) frame.encKey = obj["encKey"]; - frame.cmd = translateSomfyCommand(scmd.c_str()); - } - } - if (frame.remoteAddress > 0 && frame.rollingCode > 0) { + if(frame.remoteAddress > 0 && frame.rollingCode > 0) { somfy.sendFrame(frame, repeats); - server.send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Command Sent\"}")); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Command Sent\"}")); } else - server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No address or rolling code provided\"}")); + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No address or rolling code provided\"}")); + })); + asyncServer.on("/sendRemoteCommand", HTTP_GET, [](AsyncWebServerRequest *request) { + somfy_frame_t frame; + uint8_t repeats = 0; + if(asyncHasParam(request, "address")) { + frame.remoteAddress = atoi(asyncParam(request, "address").c_str()); + if(asyncHasParam(request, "encKey")) frame.encKey = atoi(asyncParam(request, "encKey").c_str()); + if(asyncHasParam(request, "command")) frame.cmd = translateSomfyCommand(asyncParam(request, "command")); + if(asyncHasParam(request, "rcode")) frame.rollingCode = atoi(asyncParam(request, "rcode").c_str()); + if(asyncHasParam(request, "repeats")) repeats = atoi(asyncParam(request, "repeats").c_str()); } - }); - server.on("/setgeneral", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(512); - - Serial.print("Plain: "); - Serial.print(server.method()); - Serial.println(server.arg("plain")); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; + if(frame.remoteAddress > 0 && frame.rollingCode > 0) { + somfy.sendFrame(frame, repeats); + request->send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Command Sent\"}")); } - else { - JsonObject obj = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - // Parse out all the inputs. - if (obj.containsKey("hostname") || obj.containsKey("ssdpBroadcast") || obj.containsKey("checkForUpdate")) { - bool checkForUpdate = settings.checkForUpdate; - settings.fromJSON(obj); - settings.save(); - if(settings.checkForUpdate != checkForUpdate) git.emitUpdateCheck(); - if(obj.containsKey("hostname")) net.updateHostname(); + else + request->send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No address or rolling code provided\"}")); + }); + + // setgeneral + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setgeneral", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; + } + JsonObject obj = json.as(); + if(obj.containsKey("hostname") || obj.containsKey("ssdpBroadcast") || obj.containsKey("checkForUpdate")) { + bool checkForUpdate = settings.checkForUpdate; + settings.fromJSON(obj); + settings.save(); + if(settings.checkForUpdate != checkForUpdate) git.emitUpdateCheck(); + if(obj.containsKey("hostname")) net.updateHostname(); + } + if(obj.containsKey("ntpServer") || obj.containsKey("ntpServer")) { + settings.NTP.fromJSON(obj); + settings.NTP.save(); + } + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set General Settings\"}"); + })); + + // setNetwork + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setNetwork", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(400, _encoding_html, "Error parsing JSON body"); + return; + } + JsonObject obj = json.as(); + bool reboot = false; + if(obj.containsKey("connType") && obj["connType"].as() != static_cast(settings.connType)) { + settings.connType = static_cast(obj["connType"].as()); + settings.save(); + reboot = true; + } + if(obj.containsKey("wifi")) { + JsonObject objWifi = obj["wifi"]; + if(settings.connType == conn_types_t::wifi) { + if(objWifi.containsKey("ssid") && objWifi["ssid"].as().compareTo(settings.WIFI.ssid) != 0) { + if(WiFi.softAPgetStationNum() == 0) reboot = true; + } + if(objWifi.containsKey("passphrase") && objWifi["passphrase"].as().compareTo(settings.WIFI.passphrase) != 0) { + if(WiFi.softAPgetStationNum() == 0) reboot = true; + } } - if (obj.containsKey("ntpServer") || obj.containsKey("ntpServer")) { - settings.NTP.fromJSON(obj); - settings.NTP.save(); - } - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set General Settings\"}"); + settings.WIFI.fromJSON(objWifi); + settings.WIFI.save(); + } + if(obj.containsKey("ethernet")) { + JsonObject objEth = obj["ethernet"]; + if(settings.connType == conn_types_t::ethernet || settings.connType == conn_types_t::ethernetpref) + reboot = true; + settings.Ethernet.fromJSON(objEth); + settings.Ethernet.save(); + } + if(reboot) { + Serial.println("Rebooting ESP for new Network settings..."); + rebootDelay.reboot = true; + rebootDelay.rebootTime = millis() + 1000; + } + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set Network Settings\"}"); + })); + + // setIP + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/setIP", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; + } + Serial.println("Setting IP..."); + JsonObject obj = json.as(); + settings.IP.fromJSON(obj); + settings.IP.save(); + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set Network Settings\"}"); + })); + + // connectwifi + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/connectwifi", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; + } + JsonObject obj = json.as(); + Serial.println("Settings WIFI connection..."); + String ssid = ""; + String passphrase = ""; + if(obj.containsKey("ssid")) ssid = obj["ssid"].as(); + if(obj.containsKey("passphrase")) passphrase = obj["passphrase"].as(); + bool reboot = false; + if(ssid.compareTo(settings.WIFI.ssid) != 0) reboot = true; + if(passphrase.compareTo(settings.WIFI.passphrase) != 0) reboot = true; + if(!settings.WIFI.ssidExists(ssid.c_str()) && ssid.length() > 0) { + request->send(400, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"WiFi Network Does not exist\"}"); } else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } - }); - server.on("/setNetwork", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(1024); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - Serial.print("Error parsing JSON "); - Serial.println(err.c_str()); - String msg = err.c_str(); - server.send(400, _encoding_html, "Error parsing JSON body
" + msg); - } - else { - JsonObject obj = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - // Parse out all the inputs. - bool reboot = false; - if(obj.containsKey("connType") && obj["connType"].as() != static_cast(settings.connType)) { - settings.connType = static_cast(obj["connType"].as()); - settings.save(); - reboot = true; - } - if(obj.containsKey("wifi")) { - JsonObject objWifi = obj["wifi"]; - if(settings.connType == conn_types_t::wifi) { - if(objWifi.containsKey("ssid") && objWifi["ssid"].as().compareTo(settings.WIFI.ssid) != 0) { - if(WiFi.softAPgetStationNum() == 0) reboot = true; - } - if(objWifi.containsKey("passphrase") && objWifi["passphrase"].as().compareTo(settings.WIFI.passphrase) != 0) { - if(WiFi.softAPgetStationNum() == 0) reboot = true; - } - } - settings.WIFI.fromJSON(objWifi); - settings.WIFI.save(); - } - if(obj.containsKey("ethernet")) - { - JsonObject objEth = obj["ethernet"]; - // This is an ethernet connection so if anything changes we need to reboot. - if(settings.connType == conn_types_t::ethernet || settings.connType == conn_types_t::ethernetpref) - reboot = true; - settings.Ethernet.fromJSON(objEth); - settings.Ethernet.save(); - } - if (reboot) { - Serial.println("Rebooting ESP for new Network settings..."); + SETCHARPROP(settings.WIFI.ssid, ssid.c_str(), sizeof(settings.WIFI.ssid)); + SETCHARPROP(settings.WIFI.passphrase, passphrase.c_str(), sizeof(settings.WIFI.passphrase)); + settings.WIFI.save(); + settings.WIFI.print(); + request->send(201, _encoding_json, "{\"status\":\"OK\",\"desc\":\"Successfully set server connection\"}"); + if(reboot) { + Serial.println("Rebooting ESP for new WiFi settings..."); rebootDelay.reboot = true; rebootDelay.rebootTime = millis() + 1000; } - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set Network Settings\"}"); } - else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } - }); - server.on("/setIP", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - Serial.println("Setting IP..."); - DynamicJsonDocument doc(1024); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - settings.IP.fromJSON(obj); - settings.IP.save(); - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set Network Settings\"}"); - } - else { - server.send(201, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } - }); - server.on("/connectwifi", []() { - webServer.sendCORSHeaders(server); - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - Serial.println("Settings WIFI connection..."); - DynamicJsonDocument doc(512); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - HTTPMethod method = server.method(); - //Serial.print(F("HTTP Method: ")); - //Serial.println(server.method()); - if (method == HTTP_POST || method == HTTP_PUT) { - String ssid = ""; - String passphrase = ""; - if (obj.containsKey("ssid")) ssid = obj["ssid"].as(); - if (obj.containsKey("passphrase")) passphrase = obj["passphrase"].as(); - bool reboot; - if (ssid.compareTo(settings.WIFI.ssid) != 0) reboot = true; - if (passphrase.compareTo(settings.WIFI.passphrase) != 0) reboot = true; - if (!settings.WIFI.ssidExists(ssid.c_str()) && ssid.length() > 0) { - server.send(400, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"WiFi Network Does not exist\"}"); - } - else { - SETCHARPROP(settings.WIFI.ssid, ssid.c_str(), sizeof(settings.WIFI.ssid)); - SETCHARPROP(settings.WIFI.passphrase, passphrase.c_str(), sizeof(settings.WIFI.passphrase)); - settings.WIFI.save(); - settings.WIFI.print(); - server.send(201, _encoding_json, "{\"status\":\"OK\",\"desc\":\"Successfully set server connection\"}"); - if (reboot) { - Serial.println("Rebooting ESP for new WiFi settings..."); - rebootDelay.reboot = true; - rebootDelay.rebootTime = millis() + 1000; - } - } - } - else { - server.send(201, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } - }); - server.on("/modulesettings", []() { - webServer.sendCORSHeaders(server); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + })); + + // modulesettings + asyncServer.on("/modulesettings", HTTP_GET, [](AsyncWebServerRequest *request) { + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); resp.addElem("fwVersion", settings.fwVersion.name); - settings.toJSON(resp); - settings.NTP.toJSON(resp); + resp.addElem("ssdpBroadcast", settings.ssdpBroadcast); + resp.addElem("hostname", settings.hostname); + resp.addElem("connType", static_cast(settings.connType)); + resp.addElem("chipModel", settings.chipModel); + resp.addElem("checkForUpdate", settings.checkForUpdate); + resp.addElem("ntpServer", settings.NTP.ntpServer); + resp.addElem("posixZone", settings.NTP.posixZone); resp.endObject(); resp.endResponse(); - /* - DynamicJsonDocument doc(512); - JsonObject obj = doc.to(); - doc["fwVersion"] = settings.fwVersion.name; - settings.toJSON(obj); - //settings.Ethernet.toJSON(obj); - //settings.WIFI.toJSON(obj); - settings.NTP.toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - */ - }); - server.on("/networksettings", []() { - webServer.sendCORSHeaders(server); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - settings.toJSON(resp); - resp.addElem("fwVersion", settings.fwVersion.name); - resp.beginObject("ethernet"); - settings.Ethernet.toJSON(resp); - resp.endObject(); - resp.beginObject("wifi"); - settings.WIFI.toJSON(resp); - resp.endObject(); - resp.beginObject("ip"); - settings.IP.toJSON(resp); - resp.endObject(); - resp.endObject(); - resp.endResponse(); - - /* + }); + + // networksettings + asyncServer.on("/networksettings", HTTP_GET, [](AsyncWebServerRequest *request) { DynamicJsonDocument doc(2048); JsonObject obj = doc.to(); - doc["fwVersion"] = settings.fwVersion.name; settings.toJSON(obj); + obj["fwVersion"] = settings.fwVersion.name; JsonObject eth = obj.createNestedObject("ethernet"); settings.Ethernet.toJSON(eth); JsonObject wifi = obj.createNestedObject("wifi"); settings.WIFI.toJSON(wifi); JsonObject ip = obj.createNestedObject("ip"); settings.IP.toJSON(ip); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - */ - }); - server.on("/connectmqtt", []() { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(1024); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonObject obj = doc.as(); - HTTPMethod method = server.method(); + serializeJson(doc, g_async_content, sizeof(g_async_content)); + request->send(200, _encoding_json, g_async_content); + }); + + // connectmqtt + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/connectmqtt", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; + } + JsonObject obj = json.as(); Serial.print("Saving MQTT "); - Serial.print(F("HTTP Method: ")); - Serial.println(server.method()); - if (method == HTTP_POST || method == HTTP_PUT) { - mqtt.disconnect(); - settings.MQTT.fromJSON(obj); - settings.MQTT.save(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - settings.MQTT.toJSON(resp); - resp.endObject(); - resp.endResponse(); - /* - DynamicJsonDocument sdoc(1024); - JsonObject sobj = sdoc.to(); - settings.MQTT.toJSON(sobj); - serializeJson(sdoc, g_content); - server.send(200, _encoding_json, g_content); - */ - } - else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } - }); - server.on("/mqttsettings", []() { - webServer.sendCORSHeaders(server); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); - resp.beginObject(); - settings.MQTT.toJSON(resp); - resp.endObject(); - resp.endResponse(); - - /* + mqtt.disconnect(); + settings.MQTT.fromJSON(obj); + settings.MQTT.save(); + DynamicJsonDocument sdoc(1024); + JsonObject sobj = sdoc.to(); + settings.MQTT.toJSON(sobj); + serializeJson(sdoc, g_async_content, sizeof(g_async_content)); + request->send(200, _encoding_json, g_async_content); + })); + + // mqttsettings + asyncServer.on("/mqttsettings", HTTP_GET, [](AsyncWebServerRequest *request) { DynamicJsonDocument doc(1024); JsonObject obj = doc.to(); settings.MQTT.toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - */ - }); - server.on("/roomSortOrder", []() { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(512); - Serial.print("Plain: "); - Serial.print(server.method()); - Serial.println(server.arg("plain")); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonArray arr = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - // Parse out all the inputs. - uint8_t order = 0; - for(JsonVariant v : arr) { - uint8_t roomId = v.as(); - if (roomId != 0) { - SomfyRoom *room = somfy.getRoomById(roomId); - if(room) room->sortOrder = order++; - } - } - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set room order\"}"); - } - else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); - } - } + serializeJson(doc, g_async_content, sizeof(g_async_content)); + request->send(200, _encoding_json, g_async_content); }); - server.on("/shadeSortOrder", []() { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(512); - Serial.print("Plain: "); - Serial.print(server.method()); - Serial.println(server.arg("plain")); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonArray arr = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - // Parse out all the inputs. - uint8_t order = 0; - for(JsonVariant v : arr) { - uint8_t shadeId = v.as(); - if (shadeId != 255) { - SomfyShade *shade = somfy.getShadeById(shadeId); - if(shade) shade->sortOrder = order++; - } + + // roomSortOrder + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/roomSortOrder", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; + } + JsonArray arr = json.as(); + uint8_t order = 0; + for(JsonVariant v : arr) { + uint8_t roomId = v.as(); + if(roomId != 0) { + SomfyRoom *room = somfy.getRoomById(roomId); + if(room) room->sortOrder = order++; } - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set shade order\"}"); } - else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set room order\"}"); + })); + + // shadeSortOrder + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/shadeSortOrder", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; } - } - }); - server.on("/groupSortOrder", []() { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - DynamicJsonDocument doc(512); - Serial.print("Plain: "); - Serial.print(server.method()); - Serial.println(server.arg("plain")); - DeserializationError err = deserializeJson(doc, server.arg("plain")); - if (err) { - webServer.handleDeserializationError(server, err); - return; - } - else { - JsonArray arr = doc.as(); - HTTPMethod method = server.method(); - if (method == HTTP_POST || method == HTTP_PUT) { - // Parse out all the inputs. - uint8_t order = 0; - for(JsonVariant v : arr) { - uint8_t groupId = v.as(); - if (groupId != 255) { - SomfyGroup *group = somfy.getGroupById(groupId); - if(group) group->sortOrder = order++; - } + JsonArray arr = json.as(); + uint8_t order = 0; + for(JsonVariant v : arr) { + uint8_t shadeId = v.as(); + if(shadeId != 255) { + SomfyShade *shade = somfy.getShadeById(shadeId); + if(shade) shade->sortOrder = order++; } - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set group order\"}"); } - else { - server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}"); + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set shade order\"}"); + })); + + // groupSortOrder + asyncServer.addHandler(new AsyncCallbackJsonWebHandler("/groupSortOrder", + [](AsyncWebServerRequest *request, JsonVariant &json) { + if(json.isNull()) { + request->send(500, "application/json", "{\"status\":\"ERROR\",\"desc\":\"JSON parse error\"}"); + return; } - } - }); - server.on("/beginFrequencyScan", []() { - webServer.sendCORSHeaders(server); + JsonArray arr = json.as(); + uint8_t order = 0; + for(JsonVariant v : arr) { + uint8_t groupId = v.as(); + if(groupId != 255) { + SomfyGroup *group = somfy.getGroupById(groupId); + if(group) group->sortOrder = order++; + } + } + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set group order\"}"); + })); + + asyncServer.on("/beginFrequencyScan", HTTP_GET, [](AsyncWebServerRequest *request) { somfy.transceiver.beginFrequencyScan(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - somfy.transceiver.toJSON(resp); + serializeTransceiverConfig(resp); resp.endObject(); resp.endResponse(); - /* - DynamicJsonDocument doc(1024); - JsonObject obj = doc.to(); - somfy.transceiver.toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - */ }); - server.on("/endFrequencyScan", []() { - webServer.sendCORSHeaders(server); + + asyncServer.on("/endFrequencyScan", HTTP_GET, [](AsyncWebServerRequest *request) { somfy.transceiver.endFrequencyScan(); - JsonResponse resp; - resp.beginResponse(&server, g_content, sizeof(g_content)); + AsyncJsonResp resp; + resp.beginResponse(request, g_async_content, sizeof(g_async_content)); resp.beginObject(); - somfy.transceiver.toJSON(resp); + serializeTransceiverConfig(resp); resp.endObject(); resp.endResponse(); - /* - DynamicJsonDocument doc(1024); - JsonObject obj = doc.to(); - somfy.transceiver.toJSON(obj); - serializeJson(doc, g_content); - server.send(200, _encoding_json, g_content); - */ }); - server.on("/recoverFilesystem", [] () { - if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; } - webServer.sendCORSHeaders(server); + + asyncServer.on("/recoverFilesystem", WebRequestMethodComposite(HTTP_GET) | HTTP_POST, [](AsyncWebServerRequest *request) { if(git.status == GIT_UPDATING) - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Filesystem is updating. Please wait!!!\"}"); + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Filesystem is updating. Please wait!!!\"}"); else if(git.status != GIT_STATUS_READY) - server.send(200, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Cannot recover file system at this time.\"}"); + request->send(200, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Cannot recover file system at this time.\"}"); else { git.recoverFilesystem(); - server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Recovering filesystem from github please wait!!!\"}"); + request->send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Recovering filesystem from github please wait!!!\"}"); } }); - server.begin(); - apiServer.begin(); + + asyncServer.onNotFound([](AsyncWebServerRequest *r) { + if(r->method() == HTTP_OPTIONS) { r->send(200); return; } + webServer.handleNotFound(r); + }); + + // serveStatic MUST be registered AFTER all route handlers so it doesn't shadow them + asyncServer.serveStatic("/", LittleFS, "/").setDefaultFile("index.html"); + + asyncServer.begin(); } diff --git a/src/Web.h b/src/Web.h index 6c39102..042db62 100644 --- a/src/Web.h +++ b/src/Web.h @@ -1,4 +1,3 @@ -#include #include #include #include "Somfy.h" @@ -7,45 +6,18 @@ class Web { public: bool uploadSuccess = false; - void sendCORSHeaders(WebServer &server); - void sendCacheHeaders(uint32_t seconds=604800); void startup(); - void handleLogin(WebServer &server); - void handleLogout(WebServer &server); - void handleStreamFile(WebServer &server, const char *filename, const char *encoding); - void handleController(WebServer &server); - void handleLoginContext(WebServer &server); - void handleGetRepeaters(WebServer &server); - void handleGetRooms(WebServer &server); - void handleGetShades(WebServer &server); - void handleGetGroups(WebServer &server); - void handleShadeCommand(WebServer &server); - void handleRepeatCommand(WebServer &server); - void handleGroupCommand(WebServer &server); - void handleTiltCommand(WebServer &server); - void handleDiscovery(WebServer &server); - void handleNotFound(WebServer &server); - void handleRoom(WebServer &server); - void handleShade(WebServer &server); - void handleGroup(WebServer &server); - void handleSetPositions(WebServer &server); - void handleSetSensor(WebServer &server); - void handleDownloadFirmware(WebServer &server); - void handleBackup(WebServer &server, bool attach = false); - void handleReboot(WebServer &server); - void handleDeserializationError(WebServer &server, DeserializationError &err); void begin(); void loop(); void end(); - // Web Handlers + // Auth helpers bool createAPIToken(const IPAddress ipAddress, char *token); bool createAPIToken(const char *payload, char *token); bool createAPIPinToken(const IPAddress ipAddress, const char *pin, char *token); bool createAPIPasswordToken(const IPAddress ipAddress, const char *username, const char *password, char *token); - bool isAuthenticated(WebServer &server, bool cfg = false); bool isAuthenticated(AsyncWebServerRequest *request, bool cfg = false); - // Async API handler overloads (port 8081) + // Async API handlers void handleDiscovery(AsyncWebServerRequest *request); void handleGetRooms(AsyncWebServerRequest *request); void handleGetShades(AsyncWebServerRequest *request);