From 76646d8ccd01809db2ea7f8aeccbd77703939892 Mon Sep 17 00:00:00 2001 From: elims <46279009+theelims@users.noreply.github.com> Date: Thu, 4 May 2023 22:28:26 +0200 Subject: [PATCH] reorganized some stuff --- README.md | 4 +- factory_settings.ini | 2 +- interface/src/lib/SettingsCard.svelte | 18 - .../lib/{ => components}/ConfirmDialog.svelte | 0 .../lib/{ => components}/InputPassword.svelte | 0 .../src/lib/components/SettingsCard.svelte | 35 ++ interface/src/lib/components/spinner.svelte | 8 + interface/src/lib/spinner.svelte | 8 - interface/src/lib/stores/features.ts | 20 + interface/src/routes/+page.svelte | 2 +- interface/src/routes/connections/MQTT.svelte | 12 +- interface/src/routes/connections/NTP.svelte | 4 +- interface/src/routes/demo/+page.svelte | 11 +- interface/src/routes/demo/+page.ts | 8 +- interface/src/routes/demo/Demo.svelte | 7 + interface/src/routes/login.svelte | 2 +- .../src/routes/system/OTASettings.svelte | 18 +- .../src/routes/system/SystemStatus.svelte | 4 +- interface/src/routes/user/+page.svelte | 1 - interface/src/routes/wifi/Accesspoint.svelte | 4 +- interface/src/routes/wifi/Wifi.svelte | 58 +-- lib/framework/APSettingsService.cpp | 145 +++--- lib/framework/APSettingsService.h | 168 +++--- lib/framework/APStatus.cpp | 31 +- lib/framework/APStatus.h | 15 +- lib/framework/ArduinoJsonJWT.cpp | 228 +++++---- lib/framework/ArduinoJsonJWT.h | 29 +- lib/framework/AuthenticationService.cpp | 62 +-- lib/framework/AuthenticationService.h | 27 +- lib/framework/ESP8266React.cpp | 129 ++--- lib/framework/ESP8266React.h | 121 +++-- lib/framework/FSPersistence.h | 203 ++++---- lib/framework/FactoryResetService.cpp | 53 +- lib/framework/FactoryResetService.h | 17 +- lib/framework/Features.h | 1 - lib/framework/FeaturesService.cpp | 40 +- lib/framework/FeaturesService.h | 11 +- lib/framework/HttpEndpoint.h | 289 ++++++----- lib/framework/IPUtils.h | 21 +- lib/framework/JsonUtils.h | 41 +- lib/framework/MqttPubSub.h | 323 ++++++------ lib/framework/MqttSettingsService.cpp | 260 +++++----- lib/framework/MqttSettingsService.h | 168 +++--- lib/framework/MqttStatus.cpp | 35 +- lib/framework/MqttStatus.h | 15 +- lib/framework/NTPSettingsService.cpp | 136 ++--- lib/framework/NTPSettingsService.h | 80 +-- lib/framework/NTPStatus.cpp | 65 +-- lib/framework/NTPStatus.h | 13 +- lib/framework/OTASettingsService.cpp | 93 ++-- lib/framework/OTASettingsService.h | 64 +-- lib/framework/RestartService.cpp | 18 +- lib/framework/RestartService.h | 24 +- lib/framework/SecurityManager.h | 145 +++--- lib/framework/SecuritySettingsService.cpp | 224 ++++---- lib/framework/SecuritySettingsService.h | 160 +++--- lib/framework/SettingValue.cpp | 87 ++-- lib/framework/SettingValue.h | 7 +- lib/framework/StatefulService.h | 222 ++++---- lib/framework/SystemStatus.cpp | 62 +-- lib/framework/SystemStatus.h | 13 +- lib/framework/UploadFirmwareService.cpp | 145 +++--- lib/framework/UploadFirmwareService.h | 31 +- lib/framework/WebSocketTxRx.h | 479 +++++++++--------- lib/framework/WiFiScanner.cpp | 102 ++-- lib/framework/WiFiScanner.h | 17 +- lib/framework/WiFiSettingsService.cpp | 154 +++--- lib/framework/WiFiSettingsService.h | 150 +++--- lib/framework/WiFiStatus.cpp | 115 +++-- lib/framework/WiFiStatus.h | 37 +- platformio.ini | 3 +- src/LightMqttSettingsService.cpp | 23 +- src/LightMqttSettingsService.h | 56 +- src/LightStateService.cpp | 114 +++-- src/LightStateService.h | 103 ++-- src/main.cpp | 30 +- 76 files changed, 3052 insertions(+), 2578 deletions(-) delete mode 100644 interface/src/lib/SettingsCard.svelte rename interface/src/lib/{ => components}/ConfirmDialog.svelte (100%) rename interface/src/lib/{ => components}/InputPassword.svelte (100%) create mode 100644 interface/src/lib/components/SettingsCard.svelte create mode 100644 interface/src/lib/components/spinner.svelte delete mode 100644 interface/src/lib/spinner.svelte create mode 100644 interface/src/lib/stores/features.ts create mode 100644 interface/src/routes/demo/Demo.svelte diff --git a/README.md b/README.md index e1512cef..a03ec1f7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A word of warning, this repository is heavily been worked on. It is not yet prod This project aims to port the fabulous [rjwats/esp8266-react](https://github.com/rjwats/esp8266-react) project to Sveltekit and to add some more useful features. -A simple, secure and extensible framework for IoT projects built on ESP32 platforms with responsive [Sveltekit](https://kit.svelte.dev/) front-end built with [TailwindCSS](https://tailwindcss.com/) and [DaisyUI](https://daisyui.com/). +A simple, secure and extensible framework for IoT projects for ESP32 platforms with responsive [Sveltekit](https://kit.svelte.dev/) front-end built with [TailwindCSS](https://tailwindcss.com/) and [DaisyUI](https://daisyui.com/). First Step - recreate the Front End with SvelteKit: @@ -13,7 +13,7 @@ First Step - recreate the Front End with SvelteKit: - [x] System-API - [x] OTA-API - [x] Firmware Upload API -- [ ] MQTT-API +- [x] MQTT-API - [x] NTP-API - [ ] Feature Selector Front End - [ ] Demo Project diff --git a/factory_settings.ini b/factory_settings.ini index 6153675e..4db8010e 100644 --- a/factory_settings.ini +++ b/factory_settings.ini @@ -13,7 +13,7 @@ build_flags = ; Access point settings -D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED - -D FACTORY_AP_SSID=\"ESP32-Sveltekit-#{unique_id}\" ; 1-64 characters, supports placeholders + -D FACTORY_AP_SSID=\"ESP32-SvelteKit-#{unique_id}\" ; 1-64 characters, supports placeholders -D FACTORY_AP_PASSWORD=\"esp-sveltekit\" ; 8-64 characters -D FACTORY_AP_CHANNEL=1 -D FACTORY_AP_SSID_HIDDEN=false diff --git a/interface/src/lib/SettingsCard.svelte b/interface/src/lib/SettingsCard.svelte deleted file mode 100644 index d6425f1d..00000000 --- a/interface/src/lib/SettingsCard.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -
- -
- - - - -
-
- -
-
diff --git a/interface/src/lib/ConfirmDialog.svelte b/interface/src/lib/components/ConfirmDialog.svelte similarity index 100% rename from interface/src/lib/ConfirmDialog.svelte rename to interface/src/lib/components/ConfirmDialog.svelte diff --git a/interface/src/lib/InputPassword.svelte b/interface/src/lib/components/InputPassword.svelte similarity index 100% rename from interface/src/lib/InputPassword.svelte rename to interface/src/lib/components/InputPassword.svelte diff --git a/interface/src/lib/components/SettingsCard.svelte b/interface/src/lib/components/SettingsCard.svelte new file mode 100644 index 00000000..ce457c8e --- /dev/null +++ b/interface/src/lib/components/SettingsCard.svelte @@ -0,0 +1,35 @@ + + +{#if collapsable} +
+ +
+ + + + +
+
+ +
+
+{:else} +
+
+ + + + +
+
+ +
+
+{/if} diff --git a/interface/src/lib/components/spinner.svelte b/interface/src/lib/components/spinner.svelte new file mode 100644 index 00000000..3e1e0dea --- /dev/null +++ b/interface/src/lib/components/spinner.svelte @@ -0,0 +1,8 @@ + + +
+ +

Loading...

+
diff --git a/interface/src/lib/spinner.svelte b/interface/src/lib/spinner.svelte deleted file mode 100644 index 05298c88..00000000 --- a/interface/src/lib/spinner.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -
- -

Loading...

-
\ No newline at end of file diff --git a/interface/src/lib/stores/features.ts b/interface/src/lib/stores/features.ts new file mode 100644 index 00000000..a25935fe --- /dev/null +++ b/interface/src/lib/stores/features.ts @@ -0,0 +1,20 @@ +import { readable } from 'svelte/store'; + +type features = { + project: boolean; + security: boolean; + mqtt: boolean; + ntp: boolean; + ota: boolean; + upload_firmware: boolean; +}; + +export const time = readable(new Date(), function start(set) { + const interval = setInterval(() => { + set(new Date()); + }, 1000); + + return function stop() { + clearInterval(interval); + }; +}); diff --git a/interface/src/routes/+page.svelte b/interface/src/routes/+page.svelte index 38a6752a..8d40f3b4 100644 --- a/interface/src/routes/+page.svelte +++ b/interface/src/routes/+page.svelte @@ -11,7 +11,7 @@

Welcome to ESP32-SvelteKit

- A simple, secure and extensible framework for IoT projects built on ESP32 platforms with + A simple, secure and extensible framework for IoT projects for ESP32 platforms with responsive import { onMount, onDestroy } from 'svelte'; - import InputPassword from '$lib/InputPassword.svelte'; - import SettingsCard from '$lib/SettingsCard.svelte'; + import InputPassword from '$lib/components/InputPassword.svelte'; + import SettingsCard from '$lib/components/SettingsCard.svelte'; import MQTT from '~icons/tabler/topology-star-3'; import Client from '~icons/tabler/robot'; @@ -123,7 +123,7 @@ } - + MQTT

@@ -179,7 +179,7 @@
-
+
Change MQTT Settings
@@ -275,9 +275,9 @@ - Clean Session? + Clean Session?
diff --git a/interface/src/routes/connections/NTP.svelte b/interface/src/routes/connections/NTP.svelte index 4cad841f..6684f8a9 100644 --- a/interface/src/routes/connections/NTP.svelte +++ b/interface/src/routes/connections/NTP.svelte @@ -1,6 +1,6 @@ - + Network Time
diff --git a/interface/src/routes/demo/+page.svelte b/interface/src/routes/demo/+page.svelte index 4c128e0b..c19d44c1 100644 --- a/interface/src/routes/demo/+page.svelte +++ b/interface/src/routes/demo/+page.svelte @@ -1,6 +1,13 @@ + +
+ +
diff --git a/interface/src/routes/demo/+page.ts b/interface/src/routes/demo/+page.ts index 8ecd7cb0..3a0e58a9 100644 --- a/interface/src/routes/demo/+page.ts +++ b/interface/src/routes/demo/+page.ts @@ -1,7 +1,11 @@ import type { PageLoad } from './$types'; -export const load = (async () => { +export const load = (async ({ fetch }) => { + const lightStatRes = await fetch('/rest/lightState'); + const lightStatItem = await lightStatRes.json(); + return { - title: 'Demo App' + lightState: lightStatItem, + title: "Project Demo" }; }) satisfies PageLoad; \ No newline at end of file diff --git a/interface/src/routes/demo/Demo.svelte b/interface/src/routes/demo/Demo.svelte new file mode 100644 index 00000000..ea401e28 --- /dev/null +++ b/interface/src/routes/demo/Demo.svelte @@ -0,0 +1,7 @@ + diff --git a/interface/src/routes/login.svelte b/interface/src/routes/login.svelte index 9696ed72..54af5dd2 100644 --- a/interface/src/routes/login.svelte +++ b/interface/src/routes/login.svelte @@ -1,6 +1,6 @@
diff --git a/interface/src/routes/system/OTASettings.svelte b/interface/src/routes/system/OTASettings.svelte index 8656038f..0fb05ebb 100644 --- a/interface/src/routes/system/OTASettings.svelte +++ b/interface/src/routes/system/OTASettings.svelte @@ -1,8 +1,8 @@ - + OTA Firmware Update - -
+
@@ -119,7 +119,7 @@ Upload Firmware
- + Uploading a new firmware (.bin) file will replace the existing firmware.
@@ -127,7 +127,7 @@ import { onDestroy } from 'svelte'; import { openModal, closeModal } from 'svelte-modals'; - import ConfirmDialog from '$lib/ConfirmDialog.svelte'; - import SettingsCard from '$lib/SettingsCard.svelte'; + import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'; + import SettingsCard from '$lib/components/SettingsCard.svelte'; import CPU from '~icons/tabler/cpu'; import Power from '~icons/tabler/power'; diff --git a/interface/src/routes/user/+page.svelte b/interface/src/routes/user/+page.svelte index d4b18ff2..0d9aa7f5 100644 --- a/interface/src/routes/user/+page.svelte +++ b/interface/src/routes/user/+page.svelte @@ -1,6 +1,5 @@ diff --git a/interface/src/routes/wifi/Accesspoint.svelte b/interface/src/routes/wifi/Accesspoint.svelte index 8b0746ac..82090753 100644 --- a/interface/src/routes/wifi/Accesspoint.svelte +++ b/interface/src/routes/wifi/Accesspoint.svelte @@ -1,7 +1,7 @@ - + Wi-Fi -
+
@@ -190,12 +190,12 @@
@@ -213,8 +213,8 @@
-
- +
+
SSID
@@ -229,8 +229,8 @@
-
- +
+
IP Address
@@ -245,8 +245,8 @@
-
- +
+
MAC Address
@@ -262,7 +262,7 @@ }} > @@ -275,8 +275,8 @@
-
- +
+
RSSI
@@ -290,8 +290,8 @@
-
- +
+
Channel
@@ -305,8 +305,8 @@
-
- +
+
Gateway IP
@@ -320,8 +320,8 @@
-
- +
+
Subnet Mask
@@ -335,8 +335,8 @@
-
- +
+
DNS 1
@@ -356,14 +356,14 @@
Change Wi-Fi Settings
-
+
-
-
-
+
+
diff --git a/lib/framework/APSettingsService.cpp b/lib/framework/APSettingsService.cpp index 0bfcbb42..45a6d3d3 100644 --- a/lib/framework/APSettingsService.cpp +++ b/lib/framework/APSettingsService.cpp @@ -1,83 +1,102 @@ #include -APSettingsService::APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : - _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager), - _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE), - _dnsServer(nullptr), - _lastManaged(0), - _reconfigureAp(false) { - addUpdateHandler([&](const String& originId) { reconfigureAP(); }, false); +APSettingsService::APSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager) : _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE), + _dnsServer(nullptr), + _lastManaged(0), + _reconfigureAp(false) +{ + addUpdateHandler([&](const String &originId) + { reconfigureAP(); }, + false); } -void APSettingsService::begin() { - _fsPersistence.readFromFS(); - reconfigureAP(); +void APSettingsService::begin() +{ + _fsPersistence.readFromFS(); + reconfigureAP(); } -void APSettingsService::reconfigureAP() { - _lastManaged = millis() - MANAGE_NETWORK_DELAY; - _reconfigureAp = true; +void APSettingsService::reconfigureAP() +{ + _lastManaged = millis() - MANAGE_NETWORK_DELAY; + _reconfigureAp = true; } -void APSettingsService::loop() { - unsigned long currentMillis = millis(); - unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged); - if (manageElapsed >= MANAGE_NETWORK_DELAY) { - _lastManaged = currentMillis; - manageAP(); - } - handleDNS(); +void APSettingsService::loop() +{ + unsigned long currentMillis = millis(); + unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged); + if (manageElapsed >= MANAGE_NETWORK_DELAY) + { + _lastManaged = currentMillis; + manageAP(); + } + handleDNS(); } -void APSettingsService::manageAP() { - WiFiMode_t currentWiFiMode = WiFi.getMode(); - if (_state.provisionMode == AP_MODE_ALWAYS || - (_state.provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED)) { - if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) { - startAP(); +void APSettingsService::manageAP() +{ + WiFiMode_t currentWiFiMode = WiFi.getMode(); + if (_state.provisionMode == AP_MODE_ALWAYS || + (_state.provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED)) + { + if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) + { + startAP(); + } + } + else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && + (_reconfigureAp || !WiFi.softAPgetStationNum())) + { + stopAP(); } - } else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && - (_reconfigureAp || !WiFi.softAPgetStationNum())) { - stopAP(); - } - _reconfigureAp = false; + _reconfigureAp = false; } -void APSettingsService::startAP() { - Serial.println(F("Starting software access point")); - WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask); - WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients); - if (!_dnsServer) { - IPAddress apIp = WiFi.softAPIP(); - Serial.print(F("Starting captive portal on ")); - Serial.println(apIp); - _dnsServer = new DNSServer; - _dnsServer->start(DNS_PORT, "*", apIp); - } +void APSettingsService::startAP() +{ + Serial.println(F("Starting software access point")); + WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask); + WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients); + if (!_dnsServer) + { + IPAddress apIp = WiFi.softAPIP(); + Serial.print(F("Starting captive portal on ")); + Serial.println(apIp); + _dnsServer = new DNSServer; + _dnsServer->start(DNS_PORT, "*", apIp); + } } -void APSettingsService::stopAP() { - if (_dnsServer) { - Serial.println(F("Stopping captive portal")); - _dnsServer->stop(); - delete _dnsServer; - _dnsServer = nullptr; - } - Serial.println(F("Stopping software access point")); - WiFi.softAPdisconnect(true); +void APSettingsService::stopAP() +{ + if (_dnsServer) + { + Serial.println(F("Stopping captive portal")); + _dnsServer->stop(); + delete _dnsServer; + _dnsServer = nullptr; + } + Serial.println(F("Stopping software access point")); + WiFi.softAPdisconnect(true); } -void APSettingsService::handleDNS() { - if (_dnsServer) { - _dnsServer->processNextRequest(); - } +void APSettingsService::handleDNS() +{ + if (_dnsServer) + { + _dnsServer->processNextRequest(); + } } -APNetworkStatus APSettingsService::getAPNetworkStatus() { - WiFiMode_t currentWiFiMode = WiFi.getMode(); - bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA; - if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) { - return APNetworkStatus::LINGERING; - } - return apActive ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE; +APNetworkStatus APSettingsService::getAPNetworkStatus() +{ + WiFiMode_t currentWiFiMode = WiFi.getMode(); + bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA; + if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) + { + return APNetworkStatus::LINGERING; + } + return apActive ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE; } diff --git a/lib/framework/APSettingsService.h b/lib/framework/APSettingsService.h index 998f290f..d74ec46a 100644 --- a/lib/framework/APSettingsService.h +++ b/lib/framework/APSettingsService.h @@ -55,92 +55,104 @@ #define MANAGE_NETWORK_DELAY 10000 #define DNS_PORT 53 -enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING }; - -class APSettings { - public: - uint8_t provisionMode; - String ssid; - String password; - uint8_t channel; - bool ssidHidden; - uint8_t maxClients; - - IPAddress localIP; - IPAddress gatewayIP; - IPAddress subnetMask; - - bool operator==(const APSettings& settings) const { - return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && - channel == settings.channel && ssidHidden == settings.ssidHidden && maxClients == settings.maxClients && - localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask; - } - - static void read(APSettings& settings, JsonObject& root) { - root["provision_mode"] = settings.provisionMode; - root["ssid"] = settings.ssid; - root["password"] = settings.password; - root["channel"] = settings.channel; - root["ssid_hidden"] = settings.ssidHidden; - root["max_clients"] = settings.maxClients; - root["local_ip"] = settings.localIP.toString(); - root["gateway_ip"] = settings.gatewayIP.toString(); - root["subnet_mask"] = settings.subnetMask.toString(); - } - - static StateUpdateResult update(JsonObject& root, APSettings& settings) { - APSettings newSettings = {}; - newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE; - switch (settings.provisionMode) { - case AP_MODE_ALWAYS: - case AP_MODE_DISCONNECTED: - case AP_MODE_NEVER: - break; - default: - newSettings.provisionMode = AP_MODE_ALWAYS; +enum APNetworkStatus +{ + ACTIVE = 0, + INACTIVE, + LINGERING +}; + +class APSettings +{ +public: + uint8_t provisionMode; + String ssid; + String password; + uint8_t channel; + bool ssidHidden; + uint8_t maxClients; + + IPAddress localIP; + IPAddress gatewayIP; + IPAddress subnetMask; + + bool operator==(const APSettings &settings) const + { + return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && + channel == settings.channel && ssidHidden == settings.ssidHidden && maxClients == settings.maxClients && + localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask; } - newSettings.ssid = root["ssid"] | SettingValue::format(FACTORY_AP_SSID); - newSettings.password = root["password"] | FACTORY_AP_PASSWORD; - newSettings.channel = root["channel"] | FACTORY_AP_CHANNEL; - newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN; - newSettings.maxClients = root["max_clients"] | FACTORY_AP_MAX_CLIENTS; - - JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP); - JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP); - JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK); - - if (newSettings == settings) { - return StateUpdateResult::UNCHANGED; + + static void read(APSettings &settings, JsonObject &root) + { + root["provision_mode"] = settings.provisionMode; + root["ssid"] = settings.ssid; + root["password"] = settings.password; + root["channel"] = settings.channel; + root["ssid_hidden"] = settings.ssidHidden; + root["max_clients"] = settings.maxClients; + root["local_ip"] = settings.localIP.toString(); + root["gateway_ip"] = settings.gatewayIP.toString(); + root["subnet_mask"] = settings.subnetMask.toString(); + } + + static StateUpdateResult update(JsonObject &root, APSettings &settings) + { + APSettings newSettings = {}; + newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE; + switch (settings.provisionMode) + { + case AP_MODE_ALWAYS: + case AP_MODE_DISCONNECTED: + case AP_MODE_NEVER: + break; + default: + newSettings.provisionMode = AP_MODE_ALWAYS; + } + newSettings.ssid = root["ssid"] | SettingValue::format(FACTORY_AP_SSID); + newSettings.password = root["password"] | FACTORY_AP_PASSWORD; + newSettings.channel = root["channel"] | FACTORY_AP_CHANNEL; + newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN; + newSettings.maxClients = root["max_clients"] | FACTORY_AP_MAX_CLIENTS; + + JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP); + JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP); + JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK); + + if (newSettings == settings) + { + return StateUpdateResult::UNCHANGED; + } + settings = newSettings; + return StateUpdateResult::CHANGED; } - settings = newSettings; - return StateUpdateResult::CHANGED; - } }; -class APSettingsService : public StatefulService { - public: - APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); +class APSettingsService : public StatefulService +{ +public: + APSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager); - void begin(); - void loop(); - APNetworkStatus getAPNetworkStatus(); + void begin(); + void loop(); + APNetworkStatus getAPNetworkStatus(); - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; +private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; - // for the captive portal - DNSServer* _dnsServer; + // for the captive portal + DNSServer *_dnsServer; - // for the mangement delay loop - volatile unsigned long _lastManaged; - volatile boolean _reconfigureAp; + // for the mangement delay loop + volatile unsigned long _lastManaged; + volatile boolean _reconfigureAp; - void reconfigureAP(); - void manageAP(); - void startAP(); - void stopAP(); - void handleDNS(); + void reconfigureAP(); + void manageAP(); + void startAP(); + void stopAP(); + void handleDNS(); }; -#endif // end APSettingsConfig_h +#endif // end APSettingsConfig_h diff --git a/lib/framework/APStatus.cpp b/lib/framework/APStatus.cpp index 5bfe3008..1085207a 100644 --- a/lib/framework/APStatus.cpp +++ b/lib/framework/APStatus.cpp @@ -1,22 +1,23 @@ #include -APStatus::APStatus(AsyncWebServer* server, SecurityManager* securityManager, APSettingsService* apSettingsService) : - _apSettingsService(apSettingsService) { - server->on(AP_STATUS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, std::placeholders::_1), - AuthenticationPredicates::IS_AUTHENTICATED)); +APStatus::APStatus(AsyncWebServer *server, SecurityManager *securityManager, APSettingsService *apSettingsService) : _apSettingsService(apSettingsService) +{ + server->on(AP_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); } -void APStatus::apStatus(AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE); - JsonObject root = response->getRoot(); +void APStatus::apStatus(AsyncWebServerRequest *request) +{ + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE); + JsonObject root = response->getRoot(); - root["status"] = _apSettingsService->getAPNetworkStatus(); - root["ip_address"] = WiFi.softAPIP().toString(); - root["mac_address"] = WiFi.softAPmacAddress(); - root["station_num"] = WiFi.softAPgetStationNum(); + root["status"] = _apSettingsService->getAPNetworkStatus(); + root["ip_address"] = WiFi.softAPIP().toString(); + root["mac_address"] = WiFi.softAPmacAddress(); + root["station_num"] = WiFi.softAPgetStationNum(); - response->setLength(); - request->send(response); + response->setLength(); + request->send(response); } diff --git a/lib/framework/APStatus.h b/lib/framework/APStatus.h index 12620b08..89356a26 100644 --- a/lib/framework/APStatus.h +++ b/lib/framework/APStatus.h @@ -19,13 +19,14 @@ #define MAX_AP_STATUS_SIZE 1024 #define AP_STATUS_SERVICE_PATH "/rest/apStatus" -class APStatus { - public: - APStatus(AsyncWebServer* server, SecurityManager* securityManager, APSettingsService* apSettingsService); +class APStatus +{ +public: + APStatus(AsyncWebServer *server, SecurityManager *securityManager, APSettingsService *apSettingsService); - private: - APSettingsService* _apSettingsService; - void apStatus(AsyncWebServerRequest* request); +private: + APSettingsService *_apSettingsService; + void apStatus(AsyncWebServerRequest *request); }; -#endif // end APStatus_h +#endif // end APStatus_h diff --git a/lib/framework/ArduinoJsonJWT.cpp b/lib/framework/ArduinoJsonJWT.cpp index 8b449e1b..65f23879 100644 --- a/lib/framework/ArduinoJsonJWT.cpp +++ b/lib/framework/ArduinoJsonJWT.cpp @@ -1,14 +1,17 @@ #include "ArduinoJsonJWT.h" -ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) { +ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) +{ } -void ArduinoJsonJWT::setSecret(String secret) { - _secret = secret; +void ArduinoJsonJWT::setSecret(String secret) +{ + _secret = secret; } -String ArduinoJsonJWT::getSecret() { - return _secret; +String ArduinoJsonJWT::getSecret() +{ + return _secret; } /* @@ -18,127 +21,138 @@ String ArduinoJsonJWT::getSecret() { * * No need to pull in additional crypto libraries - lets use what we already have. */ -String ArduinoJsonJWT::sign(String& payload) { - unsigned char hmacResult[32]; - { +String ArduinoJsonJWT::sign(String &payload) +{ + unsigned char hmacResult[32]; + { #ifdef ESP32 - mbedtls_md_context_t ctx; - mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; - mbedtls_md_init(&ctx); - mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); - mbedtls_md_hmac_starts(&ctx, (unsigned char*)_secret.c_str(), _secret.length()); - mbedtls_md_hmac_update(&ctx, (unsigned char*)payload.c_str(), payload.length()); - mbedtls_md_hmac_finish(&ctx, hmacResult); - mbedtls_md_free(&ctx); + mbedtls_md_context_t ctx; + mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); + mbedtls_md_hmac_starts(&ctx, (unsigned char *)_secret.c_str(), _secret.length()); + mbedtls_md_hmac_update(&ctx, (unsigned char *)payload.c_str(), payload.length()); + mbedtls_md_hmac_finish(&ctx, hmacResult); + mbedtls_md_free(&ctx); #elif defined(ESP8266) - br_hmac_key_context keyCtx; - br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length()); - br_hmac_context hmacCtx; - br_hmac_init(&hmacCtx, &keyCtx, 0); - br_hmac_update(&hmacCtx, payload.c_str(), payload.length()); - br_hmac_out(&hmacCtx, hmacResult); + br_hmac_key_context keyCtx; + br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length()); + br_hmac_context hmacCtx; + br_hmac_init(&hmacCtx, &keyCtx, 0); + br_hmac_update(&hmacCtx, payload.c_str(), payload.length()); + br_hmac_out(&hmacCtx, hmacResult); #endif - } - return encode((char*)hmacResult, 32); + } + return encode((char *)hmacResult, 32); } -String ArduinoJsonJWT::buildJWT(JsonObject& payload) { - // serialize, then encode payload - String jwt; - serializeJson(payload, jwt); - jwt = encode(jwt.c_str(), jwt.length()); +String ArduinoJsonJWT::buildJWT(JsonObject &payload) +{ + // serialize, then encode payload + String jwt; + serializeJson(payload, jwt); + jwt = encode(jwt.c_str(), jwt.length()); - // add the header to payload - jwt = JWT_HEADER + '.' + jwt; + // add the header to payload + jwt = JWT_HEADER + '.' + jwt; - // add signature - jwt += '.' + sign(jwt); + // add signature + jwt += '.' + sign(jwt); - return jwt; + return jwt; } -void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument& jsonDocument) { - // clear json document before we begin, jsonDocument wil be null on failure - jsonDocument.clear(); - - // must have the correct header and delimiter - if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) { - return; - } - - // check there is a signature delimieter - int signatureDelimiterIndex = jwt.lastIndexOf('.'); - if (signatureDelimiterIndex == JWT_HEADER_SIZE) { - return; - } - - // check the signature is valid - String signature = jwt.substring(signatureDelimiterIndex + 1); - jwt = jwt.substring(0, signatureDelimiterIndex); - if (sign(jwt) != signature) { - return; - } - - // decode payload - jwt = jwt.substring(JWT_HEADER_SIZE + 1); - jwt = decode(jwt); - - // parse payload, clearing json document after failure - DeserializationError error = deserializeJson(jsonDocument, jwt); - if (error != DeserializationError::Ok || !jsonDocument.is()) { +void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument &jsonDocument) +{ + // clear json document before we begin, jsonDocument wil be null on failure jsonDocument.clear(); - } + + // must have the correct header and delimiter + if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) + { + return; + } + + // check there is a signature delimieter + int signatureDelimiterIndex = jwt.lastIndexOf('.'); + if (signatureDelimiterIndex == JWT_HEADER_SIZE) + { + return; + } + + // check the signature is valid + String signature = jwt.substring(signatureDelimiterIndex + 1); + jwt = jwt.substring(0, signatureDelimiterIndex); + if (sign(jwt) != signature) + { + return; + } + + // decode payload + jwt = jwt.substring(JWT_HEADER_SIZE + 1); + jwt = decode(jwt); + + // parse payload, clearing json document after failure + DeserializationError error = deserializeJson(jsonDocument, jwt); + if (error != DeserializationError::Ok || !jsonDocument.is()) + { + jsonDocument.clear(); + } } -String ArduinoJsonJWT::encode(const char* cstr, int inputLen) { - // prepare encoder - base64_encodestate _state; +String ArduinoJsonJWT::encode(const char *cstr, int inputLen) +{ + // prepare encoder + base64_encodestate _state; #ifdef ESP32 - base64_init_encodestate(&_state); - size_t encodedLength = base64_encode_expected_len(inputLen) + 1; + base64_init_encodestate(&_state); + size_t encodedLength = base64_encode_expected_len(inputLen) + 1; #elif defined(ESP8266) - base64_init_encodestate_nonewlines(&_state); - size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1; + base64_init_encodestate_nonewlines(&_state); + size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1; #endif - // prepare buffer of correct length, returning an empty string on failure - char* buffer = (char*)malloc(encodedLength * sizeof(char)); - if (buffer == nullptr) { - return ""; - } - - // encode to buffer - int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state); - len += base64_encode_blockend(&buffer[len], &_state); - buffer[len] = 0; - - // convert to arduino string, freeing buffer - String value = String(buffer); - free(buffer); - buffer = nullptr; - - // remove padding and convert to URL safe form - while (value.length() > 0 && value.charAt(value.length() - 1) == '=') { - value.remove(value.length() - 1); - } - value.replace('+', '-'); - value.replace('/', '_'); - - // return as string - return value; + // prepare buffer of correct length, returning an empty string on failure + char *buffer = (char *)malloc(encodedLength * sizeof(char)); + if (buffer == nullptr) + { + return ""; + } + + // encode to buffer + int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state); + len += base64_encode_blockend(&buffer[len], &_state); + buffer[len] = 0; + + // convert to arduino string, freeing buffer + String value = String(buffer); + free(buffer); + buffer = nullptr; + + // remove padding and convert to URL safe form + while (value.length() > 0 && value.charAt(value.length() - 1) == '=') + { + value.remove(value.length() - 1); + } + value.replace('+', '-'); + value.replace('/', '_'); + + // return as string + return value; } -String ArduinoJsonJWT::decode(String value) { - // convert to standard base64 - value.replace('-', '+'); - value.replace('_', '/'); +String ArduinoJsonJWT::decode(String value) +{ + // convert to standard base64 + value.replace('-', '+'); + value.replace('_', '/'); - // prepare buffer of correct length - char buffer[base64_decode_expected_len(value.length()) + 1]; + // prepare buffer of correct length + char buffer[base64_decode_expected_len(value.length()) + 1]; - // decode - int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]); - buffer[len] = 0; + // decode + int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]); + buffer[len] = 0; - // return as string - return String(buffer); + // return as string + return String(buffer); } diff --git a/lib/framework/ArduinoJsonJWT.h b/lib/framework/ArduinoJsonJWT.h index beeedc06..7cb4687d 100644 --- a/lib/framework/ArduinoJsonJWT.h +++ b/lib/framework/ArduinoJsonJWT.h @@ -12,26 +12,27 @@ #include #endif -class ArduinoJsonJWT { - private: - String _secret; +class ArduinoJsonJWT +{ +private: + String _secret; - const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; - const int JWT_HEADER_SIZE = JWT_HEADER.length(); + const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; + const int JWT_HEADER_SIZE = JWT_HEADER.length(); - String sign(String& value); + String sign(String &value); - static String encode(const char* cstr, int len); - static String decode(String value); + static String encode(const char *cstr, int len); + static String decode(String value); - public: - ArduinoJsonJWT(String secret); +public: + ArduinoJsonJWT(String secret); - void setSecret(String secret); - String getSecret(); + void setSecret(String secret); + String getSecret(); - String buildJWT(JsonObject& payload); - void parseJWT(String jwt, JsonDocument& jsonDocument); + String buildJWT(JsonObject &payload); + void parseJWT(String jwt, JsonDocument &jsonDocument); }; #endif diff --git a/lib/framework/AuthenticationService.cpp b/lib/framework/AuthenticationService.cpp index 84c347cc..a85426c1 100644 --- a/lib/framework/AuthenticationService.cpp +++ b/lib/framework/AuthenticationService.cpp @@ -2,47 +2,51 @@ #if FT_ENABLED(FT_SECURITY) -AuthenticationService::AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager) : - _securityManager(securityManager), - _signInHandler(SIGN_IN_PATH, - std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2)) { - server->on(VERIFY_AUTHORIZATION_PATH, - HTTP_GET, - std::bind(&AuthenticationService::verifyAuthorization, this, std::placeholders::_1)); - _signInHandler.setMethod(HTTP_POST); - _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE); - server->addHandler(&_signInHandler); +AuthenticationService::AuthenticationService(AsyncWebServer *server, SecurityManager *securityManager) : _securityManager(securityManager), + _signInHandler(SIGN_IN_PATH, + std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2)) +{ + server->on(VERIFY_AUTHORIZATION_PATH, + HTTP_GET, + std::bind(&AuthenticationService::verifyAuthorization, this, std::placeholders::_1)); + _signInHandler.setMethod(HTTP_POST); + _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE); + server->addHandler(&_signInHandler); } /** * Verifys that the request supplied a valid JWT. */ -void AuthenticationService::verifyAuthorization(AsyncWebServerRequest* request) { - Authentication authentication = _securityManager->authenticateRequest(request); - request->send(authentication.authenticated ? 200 : 401); +void AuthenticationService::verifyAuthorization(AsyncWebServerRequest *request) +{ + Authentication authentication = _securityManager->authenticateRequest(request); + request->send(authentication.authenticated ? 200 : 401); } /** * Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in * subsequent requests. */ -void AuthenticationService::signIn(AsyncWebServerRequest* request, JsonVariant& json) { - if (json.is()) { - String username = json["username"]; - String password = json["password"]; - Authentication authentication = _securityManager->authenticate(username, password); - if (authentication.authenticated) { - User* user = authentication.user; - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE); - JsonObject jsonObject = response->getRoot(); - jsonObject["access_token"] = _securityManager->generateJWT(user); - response->setLength(); - request->send(response); - return; +void AuthenticationService::signIn(AsyncWebServerRequest *request, JsonVariant &json) +{ + if (json.is()) + { + String username = json["username"]; + String password = json["password"]; + Authentication authentication = _securityManager->authenticate(username, password); + if (authentication.authenticated) + { + User *user = authentication.user; + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE); + JsonObject jsonObject = response->getRoot(); + jsonObject["access_token"] = _securityManager->generateJWT(user); + response->setLength(); + request->send(response); + return; + } } - } - AsyncWebServerResponse* response = request->beginResponse(401); - request->send(response); + AsyncWebServerResponse *response = request->beginResponse(401); + request->send(response); } #endif // end FT_ENABLED(FT_SECURITY) diff --git a/lib/framework/AuthenticationService.h b/lib/framework/AuthenticationService.h index 85202237..30b62cec 100644 --- a/lib/framework/AuthenticationService.h +++ b/lib/framework/AuthenticationService.h @@ -13,18 +13,19 @@ #if FT_ENABLED(FT_SECURITY) -class AuthenticationService { - public: - AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager); - - private: - SecurityManager* _securityManager; - AsyncCallbackJsonWebHandler _signInHandler; - - // endpoint functions - void signIn(AsyncWebServerRequest* request, JsonVariant& json); - void verifyAuthorization(AsyncWebServerRequest* request); +class AuthenticationService +{ +public: + AuthenticationService(AsyncWebServer *server, SecurityManager *securityManager); + +private: + SecurityManager *_securityManager; + AsyncCallbackJsonWebHandler _signInHandler; + + // endpoint functions + void signIn(AsyncWebServerRequest *request, JsonVariant &json); + void verifyAuthorization(AsyncWebServerRequest *request); }; -#endif // end FT_ENABLED(FT_SECURITY) -#endif // end SecurityManager_h +#endif // end FT_ENABLED(FT_SECURITY) +#endif // end SecurityManager_h diff --git a/lib/framework/ESP8266React.cpp b/lib/framework/ESP8266React.cpp index 6e8969f6..e9af4fe1 100644 --- a/lib/framework/ESP8266React.cpp +++ b/lib/framework/ESP8266React.cpp @@ -1,114 +1,119 @@ #include -ESP8266React::ESP8266React(AsyncWebServer* server) : - _featureService(server), - _securitySettingsService(server, &ESPFS), - _wifiSettingsService(server, &ESPFS, &_securitySettingsService), - _wifiScanner(server, &_securitySettingsService), - _wifiStatus(server, &_securitySettingsService), - _apSettingsService(server, &ESPFS, &_securitySettingsService), - _apStatus(server, &_securitySettingsService, &_apSettingsService), +ESP8266React::ESP8266React(AsyncWebServer *server) : _featureService(server), + _securitySettingsService(server, &ESPFS), + _wifiSettingsService(server, &ESPFS, &_securitySettingsService), + _wifiScanner(server, &_securitySettingsService), + _wifiStatus(server, &_securitySettingsService), + _apSettingsService(server, &ESPFS, &_securitySettingsService), + _apStatus(server, &_securitySettingsService, &_apSettingsService), #if FT_ENABLED(FT_NTP) - _ntpSettingsService(server, &ESPFS, &_securitySettingsService), - _ntpStatus(server, &_securitySettingsService), + _ntpSettingsService(server, &ESPFS, &_securitySettingsService), + _ntpStatus(server, &_securitySettingsService), #endif #if FT_ENABLED(FT_OTA) - _otaSettingsService(server, &ESPFS, &_securitySettingsService), + _otaSettingsService(server, &ESPFS, &_securitySettingsService), #endif #if FT_ENABLED(FT_UPLOAD_FIRMWARE) - _uploadFirmwareService(server, &_securitySettingsService), + _uploadFirmwareService(server, &_securitySettingsService), #endif #if FT_ENABLED(FT_MQTT) - _mqttSettingsService(server, &ESPFS, &_securitySettingsService), - _mqttStatus(server, &_mqttSettingsService, &_securitySettingsService), + _mqttSettingsService(server, &ESPFS, &_securitySettingsService), + _mqttStatus(server, &_mqttSettingsService, &_securitySettingsService), #endif #if FT_ENABLED(FT_SECURITY) - _authenticationService(server, &_securitySettingsService), + _authenticationService(server, &_securitySettingsService), #endif - _restartService(server, &_securitySettingsService), - _factoryResetService(server, &ESPFS, &_securitySettingsService), - _systemStatus(server, &_securitySettingsService) { + _restartService(server, &_securitySettingsService), + _factoryResetService(server, &ESPFS, &_securitySettingsService), + _systemStatus(server, &_securitySettingsService) +{ #ifdef PROGMEM_WWW - // Serve static resources from PROGMEM - WWWData::registerRoutes( - [server, this](const String& uri, const String& contentType, const uint8_t* content, size_t len) { - ArRequestHandlerFunction requestHandler = [contentType, content, len](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, contentType, content, len); - response->addHeader("Content-Encoding", "gzip"); - request->send(response); - }; - server->on(uri.c_str(), HTTP_GET, requestHandler); - // Serving non matching get requests with "/index.html" - // OPTIONS get a straight up 200 response - if (uri.equals("/index.html")) { - server->onNotFound([requestHandler](AsyncWebServerRequest* request) { + // Serve static resources from PROGMEM + WWWData::registerRoutes( + [server, this](const String &uri, const String &contentType, const uint8_t *content, size_t len) + { + ArRequestHandlerFunction requestHandler = [contentType, content, len](AsyncWebServerRequest *request) + { + AsyncWebServerResponse *response = request->beginResponse_P(200, contentType, content, len); + response->addHeader("Content-Encoding", "gzip"); + request->send(response); + }; + server->on(uri.c_str(), HTTP_GET, requestHandler); + // Serving non matching get requests with "/index.html" + // OPTIONS get a straight up 200 response + if (uri.equals("/index.html")) + { + server->onNotFound([requestHandler](AsyncWebServerRequest *request) + { if (request->method() == HTTP_GET) { requestHandler(request); } else if (request->method() == HTTP_OPTIONS) { request->send(200); } else { request->send(404); + } }); } - }); - } - }); + }); #else - // Serve static resources from /www/ - server->serveStatic("/js/", ESPFS, "/www/js/"); - server->serveStatic("/css/", ESPFS, "/www/css/"); - server->serveStatic("/fonts/", ESPFS, "/www/fonts/"); - server->serveStatic("/app/", ESPFS, "/www/app/"); - server->serveStatic("/favicon.ico", ESPFS, "/www/favicon.ico"); - // Serving all other get requests with "/www/index.htm" - // OPTIONS get a straight up 200 response - server->onNotFound([](AsyncWebServerRequest* request) { + // Serve static resources from /www/ + server->serveStatic("/js/", ESPFS, "/www/js/"); + server->serveStatic("/css/", ESPFS, "/www/css/"); + server->serveStatic("/fonts/", ESPFS, "/www/fonts/"); + server->serveStatic("/app/", ESPFS, "/www/app/"); + server->serveStatic("/favicon.ico", ESPFS, "/www/favicon.ico"); + // Serving all other get requests with "/www/index.htm" + // OPTIONS get a straight up 200 response + server->onNotFound([](AsyncWebServerRequest *request) + { if (request->method() == HTTP_GET) { request->send(ESPFS, "/www/index.html"); } else if (request->method() == HTTP_OPTIONS) { request->send(200); } else { request->send(404); - } - }); + } }); #endif // Enable CORS if required #if defined(ENABLE_CORS) - DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN); - DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); - DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); #endif } -void ESP8266React::begin() { +void ESP8266React::begin() +{ #ifdef ESP32 - ESPFS.begin(true); + ESPFS.begin(true); #elif defined(ESP8266) - ESPFS.begin(); + ESPFS.begin(); #endif - _wifiSettingsService.begin(); - _apSettingsService.begin(); + _wifiSettingsService.begin(); + _apSettingsService.begin(); #if FT_ENABLED(FT_NTP) - _ntpSettingsService.begin(); + _ntpSettingsService.begin(); #endif #if FT_ENABLED(FT_OTA) - _otaSettingsService.begin(); + _otaSettingsService.begin(); #endif #if FT_ENABLED(FT_MQTT) - _mqttSettingsService.begin(); + _mqttSettingsService.begin(); #endif #if FT_ENABLED(FT_SECURITY) - _securitySettingsService.begin(); + _securitySettingsService.begin(); #endif } -void ESP8266React::loop() { - _wifiSettingsService.loop(); - _apSettingsService.loop(); +void ESP8266React::loop() +{ + _wifiSettingsService.loop(); + _apSettingsService.loop(); #if FT_ENABLED(FT_OTA) - _otaSettingsService.loop(); + _otaSettingsService.loop(); #endif #if FT_ENABLED(FT_MQTT) - _mqttSettingsService.loop(); + _mqttSettingsService.loop(); #endif } diff --git a/lib/framework/ESP8266React.h b/lib/framework/ESP8266React.h index 42e3f7a8..a5de6229 100644 --- a/lib/framework/ESP8266React.h +++ b/lib/framework/ESP8266React.h @@ -38,89 +38,100 @@ #define CORS_ORIGIN "*" #endif -class ESP8266React { - public: - ESP8266React(AsyncWebServer* server); +class ESP8266React +{ +public: + ESP8266React(AsyncWebServer *server); - void begin(); - void loop(); + void begin(); + void loop(); - FS* getFS() { - return &ESPFS; - } + FS *getFS() + { + return &ESPFS; + } - SecurityManager* getSecurityManager() { - return &_securitySettingsService; - } + SecurityManager *getSecurityManager() + { + return &_securitySettingsService; + } #if FT_ENABLED(FT_SECURITY) - StatefulService* getSecuritySettingsService() { - return &_securitySettingsService; - } + StatefulService *getSecuritySettingsService() + { + return &_securitySettingsService; + } #endif - StatefulService* getWiFiSettingsService() { - return &_wifiSettingsService; - } + StatefulService *getWiFiSettingsService() + { + return &_wifiSettingsService; + } - StatefulService* getAPSettingsService() { - return &_apSettingsService; - } + StatefulService *getAPSettingsService() + { + return &_apSettingsService; + } #if FT_ENABLED(FT_NTP) - StatefulService* getNTPSettingsService() { - return &_ntpSettingsService; - } + StatefulService *getNTPSettingsService() + { + return &_ntpSettingsService; + } #endif #if FT_ENABLED(FT_OTA) - StatefulService* getOTASettingsService() { - return &_otaSettingsService; - } + StatefulService *getOTASettingsService() + { + return &_otaSettingsService; + } #endif #if FT_ENABLED(FT_MQTT) - StatefulService* getMqttSettingsService() { - return &_mqttSettingsService; - } - - AsyncMqttClient* getMqttClient() { - return _mqttSettingsService.getMqttClient(); - } + StatefulService *getMqttSettingsService() + { + return &_mqttSettingsService; + } + + AsyncMqttClient *getMqttClient() + { + return _mqttSettingsService.getMqttClient(); + } #endif - void factoryReset() { - _factoryResetService.factoryReset(); - } - - private: - FeaturesService _featureService; - SecuritySettingsService _securitySettingsService; - WiFiSettingsService _wifiSettingsService; - WiFiScanner _wifiScanner; - WiFiStatus _wifiStatus; - APSettingsService _apSettingsService; - APStatus _apStatus; + void factoryReset() + { + _factoryResetService.factoryReset(); + } + +private: + FeaturesService _featureService; + SecuritySettingsService _securitySettingsService; + WiFiSettingsService _wifiSettingsService; + WiFiScanner _wifiScanner; + WiFiStatus _wifiStatus; + APSettingsService _apSettingsService; + APStatus _apStatus; #if FT_ENABLED(FT_NTP) - NTPSettingsService _ntpSettingsService; - NTPStatus _ntpStatus; + NTPSettingsService _ntpSettingsService; + NTPStatus _ntpStatus; #endif #if FT_ENABLED(FT_OTA) - OTASettingsService _otaSettingsService; + OTASettingsService _otaSettingsService; #endif #if FT_ENABLED(FT_UPLOAD_FIRMWARE) - UploadFirmwareService _uploadFirmwareService; + UploadFirmwareService _uploadFirmwareService; #endif #if FT_ENABLED(FT_MQTT) - MqttSettingsService _mqttSettingsService; - MqttStatus _mqttStatus; + MqttSettingsService _mqttSettingsService; + MqttStatus _mqttStatus; #endif #if FT_ENABLED(FT_SECURITY) - AuthenticationService _authenticationService; + AuthenticationService _authenticationService; #endif - RestartService _restartService; - FactoryResetService _factoryResetService; - SystemStatus _systemStatus; + RestartService _restartService; + FactoryResetService _factoryResetService; + SystemStatus _systemStatus; }; #endif diff --git a/lib/framework/FSPersistence.h b/lib/framework/FSPersistence.h index 29bbf3ee..7edad317 100644 --- a/lib/framework/FSPersistence.h +++ b/lib/framework/FSPersistence.h @@ -5,112 +5,127 @@ #include template -class FSPersistence { - public: - FSPersistence(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - FS* fs, - const char* filePath, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - _stateReader(stateReader), - _stateUpdater(stateUpdater), - _statefulService(statefulService), - _fs(fs), - _filePath(filePath), - _bufferSize(bufferSize), - _updateHandlerId(0) { - enableUpdateHandler(); - } - - void readFromFS() { - File settingsFile = _fs->open(_filePath, "r"); - - if (settingsFile) { - DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); - DeserializationError error = deserializeJson(jsonDocument, settingsFile); - if (error == DeserializationError::Ok && jsonDocument.is()) { - JsonObject jsonObject = jsonDocument.as(); - _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); - settingsFile.close(); - return; - } - settingsFile.close(); +class FSPersistence +{ +public: + FSPersistence(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + FS *fs, + const char *filePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + _fs(fs), + _filePath(filePath), + _bufferSize(bufferSize), + _updateHandlerId(0) + { + enableUpdateHandler(); + } + + void readFromFS() + { + File settingsFile = _fs->open(_filePath, "r"); + + if (settingsFile) + { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + DeserializationError error = deserializeJson(jsonDocument, settingsFile); + if (error == DeserializationError::Ok && jsonDocument.is()) + { + JsonObject jsonObject = jsonDocument.as(); + _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + settingsFile.close(); + return; + } + settingsFile.close(); + } + + // If we reach here we have not been successful in loading the config and hard-coded defaults are now applied. + // The settings are then written back to the file system so the defaults persist between resets. This last step is + // required as in some cases defaults contain randomly generated values which would otherwise be modified on reset. + applyDefaults(); + writeToFS(); } - // If we reach here we have not been successful in loading the config and hard-coded defaults are now applied. - // The settings are then written back to the file system so the defaults persist between resets. This last step is - // required as in some cases defaults contain randomly generated values which would otherwise be modified on reset. - applyDefaults(); - writeToFS(); - } + bool writeToFS() + { + // create and populate a new json object + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + JsonObject jsonObject = jsonDocument.to(); + _statefulService->read(jsonObject, _stateReader); - bool writeToFS() { - // create and populate a new json object - DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); - JsonObject jsonObject = jsonDocument.to(); - _statefulService->read(jsonObject, _stateReader); + // make directories if required + mkdirs(); - // make directories if required - mkdirs(); + // serialize it to filesystem + File settingsFile = _fs->open(_filePath, "w"); - // serialize it to filesystem - File settingsFile = _fs->open(_filePath, "w"); + // failed to open file, return false + if (!settingsFile) + { + return false; + } - // failed to open file, return false - if (!settingsFile) { - return false; + // serialize the data to the file + serializeJson(jsonDocument, settingsFile); + settingsFile.close(); + return true; } - // serialize the data to the file - serializeJson(jsonDocument, settingsFile); - settingsFile.close(); - return true; - } + void disableUpdateHandler() + { + if (_updateHandlerId) + { + _statefulService->removeUpdateHandler(_updateHandlerId); + _updateHandlerId = 0; + } + } - void disableUpdateHandler() { - if (_updateHandlerId) { - _statefulService->removeUpdateHandler(_updateHandlerId); - _updateHandlerId = 0; + void enableUpdateHandler() + { + if (!_updateHandlerId) + { + _updateHandlerId = _statefulService->addUpdateHandler([&](const String &originId) + { writeToFS(); }); + } } - } - void enableUpdateHandler() { - if (!_updateHandlerId) { - _updateHandlerId = _statefulService->addUpdateHandler([&](const String& originId) { writeToFS(); }); +private: + JsonStateReader _stateReader; + JsonStateUpdater _stateUpdater; + StatefulService *_statefulService; + FS *_fs; + const char *_filePath; + size_t _bufferSize; + update_handler_id_t _updateHandlerId; + + // We assume we have a _filePath with format "/directory1/directory2/filename" + // We create a directory for each missing parent + void mkdirs() + { + String path(_filePath); + int index = 0; + while ((index = path.indexOf('/', index + 1)) != -1) + { + String segment = path.substring(0, index); + if (!_fs->exists(segment)) + { + _fs->mkdir(segment); + } + } } - } - - private: - JsonStateReader _stateReader; - JsonStateUpdater _stateUpdater; - StatefulService* _statefulService; - FS* _fs; - const char* _filePath; - size_t _bufferSize; - update_handler_id_t _updateHandlerId; - - // We assume we have a _filePath with format "/directory1/directory2/filename" - // We create a directory for each missing parent - void mkdirs() { - String path(_filePath); - int index = 0; - while ((index = path.indexOf('/', index + 1)) != -1) { - String segment = path.substring(0, index); - if (!_fs->exists(segment)) { - _fs->mkdir(segment); - } + +protected: + // We assume the updater supplies sensible defaults if an empty object + // is supplied, this virtual function allows that to be changed. + virtual void applyDefaults() + { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + JsonObject jsonObject = jsonDocument.as(); + _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); } - } - - protected: - // We assume the updater supplies sensible defaults if an empty object - // is supplied, this virtual function allows that to be changed. - virtual void applyDefaults() { - DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); - JsonObject jsonObject = jsonDocument.as(); - _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); - } }; -#endif // end FSPersistence +#endif // end FSPersistence diff --git a/lib/framework/FactoryResetService.cpp b/lib/framework/FactoryResetService.cpp index 3f68abd2..a19d9759 100644 --- a/lib/framework/FactoryResetService.cpp +++ b/lib/framework/FactoryResetService.cpp @@ -2,38 +2,43 @@ using namespace std::placeholders; -FactoryResetService::FactoryResetService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : fs(fs) { - server->on(FACTORY_RESET_SERVICE_PATH, - HTTP_POST, - securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), - AuthenticationPredicates::IS_ADMIN)); +FactoryResetService::FactoryResetService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager) : fs(fs) +{ + server->on(FACTORY_RESET_SERVICE_PATH, + HTTP_POST, + securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), + AuthenticationPredicates::IS_ADMIN)); } -void FactoryResetService::handleRequest(AsyncWebServerRequest* request) { - request->onDisconnect(std::bind(&FactoryResetService::factoryReset, this)); - request->send(200); +void FactoryResetService::handleRequest(AsyncWebServerRequest *request) +{ + request->onDisconnect(std::bind(&FactoryResetService::factoryReset, this)); + request->send(200); } /** * Delete function assumes that all files are stored flat, within the config directory. */ -void FactoryResetService::factoryReset() { +void FactoryResetService::factoryReset() +{ #ifdef ESP32 - File root = fs->open(FS_CONFIG_DIRECTORY); - File file; - while (file = root.openNextFile()) { - String path = file.path(); - file.close(); - fs->remove(path); - } + File root = fs->open(FS_CONFIG_DIRECTORY); + File file; + while (file = root.openNextFile()) + { + String path = file.path(); + file.close(); + fs->remove(path); + } #elif defined(ESP8266) - Dir configDirectory = fs->openDir(FS_CONFIG_DIRECTORY); - while (configDirectory.next()) { - String path = FS_CONFIG_DIRECTORY; - path.concat("/"); - path.concat(configDirectory.fileName()); - fs->remove(path); - } + Dir configDirectory = fs->openDir(FS_CONFIG_DIRECTORY); + while (configDirectory.next()) + { + String path = FS_CONFIG_DIRECTORY; + path.concat("/"); + path.concat(configDirectory.fileName()); + fs->remove(path); + } #endif - RestartService::restartNow(); + RestartService::restartNow(); } diff --git a/lib/framework/FactoryResetService.h b/lib/framework/FactoryResetService.h index 2336e6f3..34afa394 100644 --- a/lib/framework/FactoryResetService.h +++ b/lib/framework/FactoryResetService.h @@ -17,16 +17,17 @@ #define FS_CONFIG_DIRECTORY "/config" #define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset" -class FactoryResetService { - FS* fs; +class FactoryResetService +{ + FS *fs; - public: - FactoryResetService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); +public: + FactoryResetService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager); - void factoryReset(); + void factoryReset(); - private: - void handleRequest(AsyncWebServerRequest* request); +private: + void handleRequest(AsyncWebServerRequest *request); }; -#endif // end FactoryResetService_h +#endif // end FactoryResetService_h diff --git a/lib/framework/Features.h b/lib/framework/Features.h index 2de82c51..f69529d2 100644 --- a/lib/framework/Features.h +++ b/lib/framework/Features.h @@ -33,5 +33,4 @@ #define FT_UPLOAD_FIRMWARE 0 #endif - #endif diff --git a/lib/framework/FeaturesService.cpp b/lib/framework/FeaturesService.cpp index 095f1f2d..f2567e18 100644 --- a/lib/framework/FeaturesService.cpp +++ b/lib/framework/FeaturesService.cpp @@ -1,42 +1,44 @@ #include -FeaturesService::FeaturesService(AsyncWebServer* server) { - server->on(FEATURES_SERVICE_PATH, HTTP_GET, std::bind(&FeaturesService::features, this, std::placeholders::_1)); +FeaturesService::FeaturesService(AsyncWebServer *server) +{ + server->on(FEATURES_SERVICE_PATH, HTTP_GET, std::bind(&FeaturesService::features, this, std::placeholders::_1)); } -void FeaturesService::features(AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE); - JsonObject root = response->getRoot(); +void FeaturesService::features(AsyncWebServerRequest *request) +{ + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE); + JsonObject root = response->getRoot(); #if FT_ENABLED(FT_PROJECT) - root["project"] = true; + root["project"] = true; #else - root["project"] = false; + root["project"] = false; #endif #if FT_ENABLED(FT_SECURITY) - root["security"] = true; + root["security"] = true; #else - root["security"] = false; + root["security"] = false; #endif #if FT_ENABLED(FT_MQTT) - root["mqtt"] = true; + root["mqtt"] = true; #else - root["mqtt"] = false; + root["mqtt"] = false; #endif #if FT_ENABLED(FT_NTP) - root["ntp"] = true; + root["ntp"] = true; #else - root["ntp"] = false; + root["ntp"] = false; #endif #if FT_ENABLED(FT_OTA) - root["ota"] = true; + root["ota"] = true; #else - root["ota"] = false; + root["ota"] = false; #endif #if FT_ENABLED(FT_UPLOAD_FIRMWARE) - root["upload_firmware"] = true; + root["upload_firmware"] = true; #else - root["upload_firmware"] = false; + root["upload_firmware"] = false; #endif - response->setLength(); - request->send(response); + response->setLength(); + request->send(response); } diff --git a/lib/framework/FeaturesService.h b/lib/framework/FeaturesService.h index 867101ec..8ce2e628 100644 --- a/lib/framework/FeaturesService.h +++ b/lib/framework/FeaturesService.h @@ -18,12 +18,13 @@ #define MAX_FEATURES_SIZE 256 #define FEATURES_SERVICE_PATH "/rest/features" -class FeaturesService { - public: - FeaturesService(AsyncWebServer* server); +class FeaturesService +{ +public: + FeaturesService(AsyncWebServer *server); - private: - void features(AsyncWebServerRequest* request); +private: + void features(AsyncWebServerRequest *request); }; #endif diff --git a/lib/framework/HttpEndpoint.h b/lib/framework/HttpEndpoint.h index f45e716a..7a5c0e48 100644 --- a/lib/framework/HttpEndpoint.h +++ b/lib/framework/HttpEndpoint.h @@ -12,154 +12,163 @@ #define HTTP_ENDPOINT_ORIGIN_ID "http" template -class HttpGetEndpoint { - public: - HttpGetEndpoint(JsonStateReader stateReader, - StatefulService* statefulService, - AsyncWebServer* server, - const String& servicePath, - SecurityManager* securityManager, - AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { - server->on(servicePath.c_str(), - HTTP_GET, - securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1), - authenticationPredicate)); - } - - HttpGetEndpoint(JsonStateReader stateReader, - StatefulService* statefulService, - AsyncWebServer* server, - const String& servicePath, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { - server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1)); - } - - protected: - JsonStateReader _stateReader; - StatefulService* _statefulService; - size_t _bufferSize; - - void fetchSettings(AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize); - JsonObject jsonObject = response->getRoot().to(); - _statefulService->read(jsonObject, _stateReader); - - response->setLength(); - request->send(response); - } +class HttpGetEndpoint +{ +public: + HttpGetEndpoint(JsonStateReader stateReader, + StatefulService *statefulService, + AsyncWebServer *server, + const String &servicePath, + SecurityManager *securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) + { + server->on(servicePath.c_str(), + HTTP_GET, + securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1), + authenticationPredicate)); + } + + HttpGetEndpoint(JsonStateReader stateReader, + StatefulService *statefulService, + AsyncWebServer *server, + const String &servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) + { + server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1)); + } + +protected: + JsonStateReader _stateReader; + StatefulService *_statefulService; + size_t _bufferSize; + + void fetchSettings(AsyncWebServerRequest *request) + { + AsyncJsonResponse *response = new AsyncJsonResponse(false, _bufferSize); + JsonObject jsonObject = response->getRoot().to(); + _statefulService->read(jsonObject, _stateReader); + + response->setLength(); + request->send(response); + } }; template -class HttpPostEndpoint { - public: - HttpPostEndpoint(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const String& servicePath, - SecurityManager* securityManager, - AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - _stateReader(stateReader), - _stateUpdater(stateUpdater), - _statefulService(statefulService), - _updateHandler( - servicePath, - securityManager->wrapCallback( - std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), - authenticationPredicate), - bufferSize), - _bufferSize(bufferSize) { - _updateHandler.setMethod(HTTP_POST); - server->addHandler(&_updateHandler); - } - - HttpPostEndpoint(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const String& servicePath, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - _stateReader(stateReader), - _stateUpdater(stateUpdater), - _statefulService(statefulService), - _updateHandler(servicePath, - std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), - bufferSize), - _bufferSize(bufferSize) { - _updateHandler.setMethod(HTTP_POST); - server->addHandler(&_updateHandler); - } - - protected: - JsonStateReader _stateReader; - JsonStateUpdater _stateUpdater; - StatefulService* _statefulService; - AsyncCallbackJsonWebHandler _updateHandler; - size_t _bufferSize; - - void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) { - if (!json.is()) { - request->send(400); - return; +class HttpPostEndpoint +{ +public: + HttpPostEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const String &servicePath, + SecurityManager *securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + _updateHandler( + servicePath, + securityManager->wrapCallback( + std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), + authenticationPredicate), + bufferSize), + _bufferSize(bufferSize) + { + _updateHandler.setMethod(HTTP_POST); + server->addHandler(&_updateHandler); } - JsonObject jsonObject = json.as(); - StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); - if (outcome == StateUpdateResult::ERROR) { - request->send(400); - return; + + HttpPostEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const String &servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + _updateHandler(servicePath, + std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), + bufferSize), + _bufferSize(bufferSize) + { + _updateHandler.setMethod(HTTP_POST); + server->addHandler(&_updateHandler); } - if (outcome == StateUpdateResult::CHANGED) { - request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); }); + +protected: + JsonStateReader _stateReader; + JsonStateUpdater _stateUpdater; + StatefulService *_statefulService; + AsyncCallbackJsonWebHandler _updateHandler; + size_t _bufferSize; + + void updateSettings(AsyncWebServerRequest *request, JsonVariant &json) + { + if (!json.is()) + { + request->send(400); + return; + } + JsonObject jsonObject = json.as(); + StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + if (outcome == StateUpdateResult::ERROR) + { + request->send(400); + return; + } + if (outcome == StateUpdateResult::CHANGED) + { + request->onDisconnect([this]() + { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); }); + } + AsyncJsonResponse *response = new AsyncJsonResponse(false, _bufferSize); + jsonObject = response->getRoot().to(); + _statefulService->read(jsonObject, _stateReader); + response->setLength(); + request->send(response); } - AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize); - jsonObject = response->getRoot().to(); - _statefulService->read(jsonObject, _stateReader); - response->setLength(); - request->send(response); - } }; template -class HttpEndpoint : public HttpGetEndpoint, public HttpPostEndpoint { - public: - HttpEndpoint(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const String& servicePath, - SecurityManager* securityManager, - AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - HttpGetEndpoint(stateReader, - statefulService, - server, - servicePath, - securityManager, - authenticationPredicate, - bufferSize), - HttpPostEndpoint(stateReader, - stateUpdater, - statefulService, - server, - servicePath, - securityManager, - authenticationPredicate, - bufferSize) { - } - - HttpEndpoint(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const String& servicePath, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - HttpGetEndpoint(stateReader, statefulService, server, servicePath, bufferSize), - HttpPostEndpoint(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) { - } +class HttpEndpoint : public HttpGetEndpoint, public HttpPostEndpoint +{ +public: + HttpEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const String &servicePath, + SecurityManager *securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : HttpGetEndpoint(stateReader, + statefulService, + server, + servicePath, + securityManager, + authenticationPredicate, + bufferSize), + HttpPostEndpoint(stateReader, + stateUpdater, + statefulService, + server, + servicePath, + securityManager, + authenticationPredicate, + bufferSize) + { + } + + HttpEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const String &servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : HttpGetEndpoint(stateReader, statefulService, server, servicePath, bufferSize), + HttpPostEndpoint(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) + { + } }; -#endif // end HttpEndpoint +#endif // end HttpEndpoint diff --git a/lib/framework/IPUtils.h b/lib/framework/IPUtils.h index 74886a07..c74f0d83 100644 --- a/lib/framework/IPUtils.h +++ b/lib/framework/IPUtils.h @@ -5,14 +5,17 @@ const IPAddress IP_NOT_SET = IPAddress(INADDR_NONE); -class IPUtils { - public: - static bool isSet(const IPAddress& ip) { - return ip != IP_NOT_SET; - } - static bool isNotSet(const IPAddress& ip) { - return ip == IP_NOT_SET; - } +class IPUtils +{ +public: + static bool isSet(const IPAddress &ip) + { + return ip != IP_NOT_SET; + } + static bool isNotSet(const IPAddress &ip) + { + return ip == IP_NOT_SET; + } }; -#endif // end IPUtils_h +#endif // end IPUtils_h diff --git a/lib/framework/JsonUtils.h b/lib/framework/JsonUtils.h index 57d94b82..e57506cd 100644 --- a/lib/framework/JsonUtils.h +++ b/lib/framework/JsonUtils.h @@ -5,25 +5,32 @@ #include #include -class JsonUtils { - public: - static void readIP(JsonObject& root, const String& key, IPAddress& ip, const String& def) { - IPAddress defaultIp = {}; - if (!defaultIp.fromString(def)) { - defaultIp = INADDR_NONE; +class JsonUtils +{ +public: + static void readIP(JsonObject &root, const String &key, IPAddress &ip, const String &def) + { + IPAddress defaultIp = {}; + if (!defaultIp.fromString(def)) + { + defaultIp = INADDR_NONE; + } + readIP(root, key, ip, defaultIp); } - readIP(root, key, ip, defaultIp); - } - static void readIP(JsonObject& root, const String& key, IPAddress& ip, const IPAddress& defaultIp = INADDR_NONE) { - if (!root[key].is() || !ip.fromString(root[key].as())) { - ip = defaultIp; + static void readIP(JsonObject &root, const String &key, IPAddress &ip, const IPAddress &defaultIp = INADDR_NONE) + { + if (!root[key].is() || !ip.fromString(root[key].as())) + { + ip = defaultIp; + } } - } - static void writeIP(JsonObject& root, const String& key, const IPAddress& ip) { - if (IPUtils::isSet(ip)) { - root[key] = ip.toString(); + static void writeIP(JsonObject &root, const String &key, const IPAddress &ip) + { + if (IPUtils::isSet(ip)) + { + root[key] = ip.toString(); + } } - } }; -#endif // end JsonUtils +#endif // end JsonUtils diff --git a/lib/framework/MqttPubSub.h b/lib/framework/MqttPubSub.h index aa846066..d5b3e732 100644 --- a/lib/framework/MqttPubSub.h +++ b/lib/framework/MqttPubSub.h @@ -7,172 +7,195 @@ #define MQTT_ORIGIN_ID "mqtt" template -class MqttConnector { - protected: - StatefulService* _statefulService; - AsyncMqttClient* _mqttClient; - size_t _bufferSize; - - MqttConnector(StatefulService* statefulService, AsyncMqttClient* mqttClient, size_t bufferSize) : - _statefulService(statefulService), _mqttClient(mqttClient), _bufferSize(bufferSize) { - _mqttClient->onConnect(std::bind(&MqttConnector::onConnect, this)); - } - - virtual void onConnect() = 0; - - public: - inline AsyncMqttClient* getMqttClient() const { - return _mqttClient; - } +class MqttConnector +{ +protected: + StatefulService *_statefulService; + AsyncMqttClient *_mqttClient; + size_t _bufferSize; + + MqttConnector(StatefulService *statefulService, AsyncMqttClient *mqttClient, size_t bufferSize) : _statefulService(statefulService), _mqttClient(mqttClient), _bufferSize(bufferSize) + { + _mqttClient->onConnect(std::bind(&MqttConnector::onConnect, this)); + } + + virtual void onConnect() = 0; + +public: + inline AsyncMqttClient *getMqttClient() const + { + return _mqttClient; + } }; template -class MqttPub : virtual public MqttConnector { - public: - MqttPub(JsonStateReader stateReader, - StatefulService* statefulService, - AsyncMqttClient* mqttClient, - const String& pubTopic = "", - bool retain = false, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - MqttConnector(statefulService, mqttClient, bufferSize), - _stateReader(stateReader), - _pubTopic(pubTopic), - _retain(retain) { - MqttConnector::_statefulService->addUpdateHandler([&](const String& originId) { publish(); }, false); - } - - void setRetain(const bool retain) { - _retain = retain; - publish(); - } - - void setPubTopic(const String& pubTopic) { - _pubTopic = pubTopic; - publish(); - } - - protected: - virtual void onConnect() { - publish(); - } - - private: - JsonStateReader _stateReader; - String _pubTopic; - bool _retain; - - void publish() { - if (_pubTopic.length() > 0 && MqttConnector::_mqttClient->connected()) { - // serialize to json doc - DynamicJsonDocument json(MqttConnector::_bufferSize); - JsonObject jsonObject = json.to(); - MqttConnector::_statefulService->read(jsonObject, _stateReader); - - // serialize to string - String payload; - serializeJson(json, payload); - - // publish the payload - MqttConnector::_mqttClient->publish(_pubTopic.c_str(), 0, _retain, payload.c_str()); +class MqttPub : virtual public MqttConnector +{ +public: + MqttPub(JsonStateReader stateReader, + StatefulService *statefulService, + AsyncMqttClient *mqttClient, + const String &pubTopic = "", + bool retain = false, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : MqttConnector(statefulService, mqttClient, bufferSize), + _stateReader(stateReader), + _pubTopic(pubTopic), + _retain(retain) + { + MqttConnector::_statefulService->addUpdateHandler([&](const String &originId) + { publish(); }, + false); + } + + void setRetain(const bool retain) + { + _retain = retain; + publish(); + } + + void setPubTopic(const String &pubTopic) + { + _pubTopic = pubTopic; + publish(); + } + +protected: + virtual void onConnect() + { + publish(); + } + +private: + JsonStateReader _stateReader; + String _pubTopic; + bool _retain; + + void publish() + { + if (_pubTopic.length() > 0 && MqttConnector::_mqttClient->connected()) + { + // serialize to json doc + DynamicJsonDocument json(MqttConnector::_bufferSize); + JsonObject jsonObject = json.to(); + MqttConnector::_statefulService->read(jsonObject, _stateReader); + + // serialize to string + String payload; + serializeJson(json, payload); + + // publish the payload + MqttConnector::_mqttClient->publish(_pubTopic.c_str(), 0, _retain, payload.c_str()); + } } - } }; template -class MqttSub : virtual public MqttConnector { - public: - MqttSub(JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncMqttClient* mqttClient, - const String& subTopic = "", - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - MqttConnector(statefulService, mqttClient, bufferSize), _stateUpdater(stateUpdater), _subTopic(subTopic) { - MqttConnector::_mqttClient->onMessage(std::bind(&MqttSub::onMqttMessage, - this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4, - std::placeholders::_5, - std::placeholders::_6)); - } - - void setSubTopic(const String& subTopic) { - if (!_subTopic.equals(subTopic)) { - // unsubscribe from the existing topic if one was set - if (_subTopic.length() > 0) { - MqttConnector::_mqttClient->unsubscribe(_subTopic.c_str()); - } - // set the new topic and re-configure the subscription - _subTopic = subTopic; - subscribe(); +class MqttSub : virtual public MqttConnector +{ +public: + MqttSub(JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncMqttClient *mqttClient, + const String &subTopic = "", + size_t bufferSize = DEFAULT_BUFFER_SIZE) : MqttConnector(statefulService, mqttClient, bufferSize), _stateUpdater(stateUpdater), _subTopic(subTopic) + { + MqttConnector::_mqttClient->onMessage(std::bind(&MqttSub::onMqttMessage, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); } - } - - protected: - virtual void onConnect() { - subscribe(); - } - private: - JsonStateUpdater _stateUpdater; - String _subTopic; + void setSubTopic(const String &subTopic) + { + if (!_subTopic.equals(subTopic)) + { + // unsubscribe from the existing topic if one was set + if (_subTopic.length() > 0) + { + MqttConnector::_mqttClient->unsubscribe(_subTopic.c_str()); + } + // set the new topic and re-configure the subscription + _subTopic = subTopic; + subscribe(); + } + } - void subscribe() { - if (_subTopic.length() > 0) { - MqttConnector::_mqttClient->subscribe(_subTopic.c_str(), 2); +protected: + virtual void onConnect() + { + subscribe(); } - } - - void onMqttMessage(char* topic, - char* payload, - AsyncMqttClientMessageProperties properties, - size_t len, - size_t index, - size_t total) { - // we only care about the topic we are watching in this class - if (strcmp(_subTopic.c_str(), topic)) { - return; + +private: + JsonStateUpdater _stateUpdater; + String _subTopic; + + void subscribe() + { + if (_subTopic.length() > 0) + { + MqttConnector::_mqttClient->subscribe(_subTopic.c_str(), 2); + } } - // deserialize from string - DynamicJsonDocument json(MqttConnector::_bufferSize); - DeserializationError error = deserializeJson(json, payload, len); - if (!error && json.is()) { - JsonObject jsonObject = json.as(); - MqttConnector::_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID); + void onMqttMessage(char *topic, + char *payload, + AsyncMqttClientMessageProperties properties, + size_t len, + size_t index, + size_t total) + { + // we only care about the topic we are watching in this class + if (strcmp(_subTopic.c_str(), topic)) + { + return; + } + + // deserialize from string + DynamicJsonDocument json(MqttConnector::_bufferSize); + DeserializationError error = deserializeJson(json, payload, len); + if (!error && json.is()) + { + JsonObject jsonObject = json.as(); + MqttConnector::_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID); + } } - } }; template -class MqttPubSub : public MqttPub, public MqttSub { - public: - MqttPubSub(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncMqttClient* mqttClient, - const String& pubTopic = "", - const String& subTopic = "", - bool retain = false, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - MqttConnector(statefulService, mqttClient, bufferSize), - MqttPub(stateReader, statefulService, mqttClient, pubTopic, retain, bufferSize), - MqttSub(stateUpdater, statefulService, mqttClient, subTopic, bufferSize) { - } - - public: - void configureTopics(const String& pubTopic, const String& subTopic) { - MqttSub::setSubTopic(subTopic); - MqttPub::setPubTopic(pubTopic); - } - - protected: - void onConnect() { - MqttSub::onConnect(); - MqttPub::onConnect(); - } +class MqttPubSub : public MqttPub, public MqttSub +{ +public: + MqttPubSub(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncMqttClient *mqttClient, + const String &pubTopic = "", + const String &subTopic = "", + bool retain = false, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : MqttConnector(statefulService, mqttClient, bufferSize), + MqttPub(stateReader, statefulService, mqttClient, pubTopic, retain, bufferSize), + MqttSub(stateUpdater, statefulService, mqttClient, subTopic, bufferSize) + { + } + +public: + void configureTopics(const String &pubTopic, const String &subTopic) + { + MqttSub::setSubTopic(subTopic); + MqttPub::setPubTopic(pubTopic); + } + +protected: + void onConnect() + { + MqttSub::onConnect(); + MqttPub::onConnect(); + } }; -#endif // end MqttPubSub +#endif // end MqttPubSub diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index 148e8346..020587a4 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -5,157 +5,189 @@ * * Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr. */ -static char* retainCstr(const char* cstr, char** ptr) { - // free up previously retained value if exists - free(*ptr); - *ptr = nullptr; - - // dynamically allocate and copy cstr (if non null) - if (cstr != nullptr) { - *ptr = (char*)malloc(strlen(cstr) + 1); - strcpy(*ptr, cstr); - } - - // return reference to pointer for convenience - return *ptr; -} - -MqttSettingsService::MqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : - _httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager), - _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE), - _retainedHost(nullptr), - _retainedClientId(nullptr), - _retainedUsername(nullptr), - _retainedPassword(nullptr), - _reconfigureMqtt(false), - _disconnectedAt(0), - _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED), - _mqttClient() { +static char *retainCstr(const char *cstr, char **ptr) +{ + // free up previously retained value if exists + free(*ptr); + *ptr = nullptr; + + // dynamically allocate and copy cstr (if non null) + if (cstr != nullptr) + { + *ptr = (char *)malloc(strlen(cstr) + 1); + strcpy(*ptr, cstr); + } + + // return reference to pointer for convenience + return *ptr; +} + +MqttSettingsService::MqttSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager) : _httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE), + _retainedHost(nullptr), + _retainedClientId(nullptr), + _retainedUsername(nullptr), + _retainedPassword(nullptr), + _reconfigureMqtt(false), + _disconnectedAt(0), + _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED), + _mqttClient() +{ #ifdef ESP32 - WiFi.onEvent( - std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), - WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); - WiFi.onEvent(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), - WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); + WiFi.onEvent( + std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); + WiFi.onEvent(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); #elif defined(ESP8266) - _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( - std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); - _onStationModeGotIPHandler = - WiFi.onStationModeGotIP(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1)); + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( + std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); + _onStationModeGotIPHandler = + WiFi.onStationModeGotIP(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1)); #endif - _mqttClient.onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, std::placeholders::_1)); - _mqttClient.onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, std::placeholders::_1)); - addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); + _mqttClient.onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, std::placeholders::_1)); + _mqttClient.onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, std::placeholders::_1)); + addUpdateHandler([&](const String &originId) + { onConfigUpdated(); }, + false); } -MqttSettingsService::~MqttSettingsService() { +MqttSettingsService::~MqttSettingsService() +{ } -void MqttSettingsService::begin() { - _fsPersistence.readFromFS(); +void MqttSettingsService::begin() +{ + _fsPersistence.readFromFS(); } -void MqttSettingsService::loop() { - if (_reconfigureMqtt || (_disconnectedAt && (unsigned long)(millis() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) { - // reconfigure MQTT client - configureMqtt(); +void MqttSettingsService::loop() +{ + if (_reconfigureMqtt || (_disconnectedAt && (unsigned long)(millis() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) + { + // reconfigure MQTT client + configureMqtt(); - // clear the reconnection flags - _reconfigureMqtt = false; - _disconnectedAt = 0; - } + // clear the reconnection flags + _reconfigureMqtt = false; + _disconnectedAt = 0; + } } -bool MqttSettingsService::isEnabled() { - return _state.enabled; +bool MqttSettingsService::isEnabled() +{ + return _state.enabled; } -bool MqttSettingsService::isConnected() { - return _mqttClient.connected(); +bool MqttSettingsService::isConnected() +{ + return _mqttClient.connected(); } -const char* MqttSettingsService::getClientId() { - return _mqttClient.getClientId(); +const char *MqttSettingsService::getClientId() +{ + return _mqttClient.getClientId(); } -AsyncMqttClientDisconnectReason MqttSettingsService::getDisconnectReason() { - return _disconnectReason; +AsyncMqttClientDisconnectReason MqttSettingsService::getDisconnectReason() +{ + return _disconnectReason; } -AsyncMqttClient* MqttSettingsService::getMqttClient() { - return &_mqttClient; +AsyncMqttClient *MqttSettingsService::getMqttClient() +{ + return &_mqttClient; } -void MqttSettingsService::onMqttConnect(bool sessionPresent) { - Serial.print(F("Connected to MQTT, ")); - if (sessionPresent) { - Serial.println(F("with persistent session")); - } else { - Serial.println(F("without persistent session")); - } +void MqttSettingsService::onMqttConnect(bool sessionPresent) +{ + Serial.print(F("Connected to MQTT, ")); + if (sessionPresent) + { + Serial.println(F("with persistent session")); + } + else + { + Serial.println(F("without persistent session")); + } } -void MqttSettingsService::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) { - Serial.print(F("Disconnected from MQTT reason: ")); - Serial.println((uint8_t)reason); - _disconnectReason = reason; - _disconnectedAt = millis(); +void MqttSettingsService::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) +{ + Serial.print(F("Disconnected from MQTT reason: ")); + Serial.println((uint8_t)reason); + _disconnectReason = reason; + _disconnectedAt = millis(); } -void MqttSettingsService::onConfigUpdated() { - _reconfigureMqtt = true; - _disconnectedAt = 0; +void MqttSettingsService::onConfigUpdated() +{ + _reconfigureMqtt = true; + _disconnectedAt = 0; } #ifdef ESP32 -void MqttSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { - if (_state.enabled) { - Serial.println(F("WiFi connection dropped, starting MQTT client.")); - onConfigUpdated(); - } +void MqttSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) +{ + if (_state.enabled) + { + Serial.println(F("WiFi connection dropped, starting MQTT client.")); + onConfigUpdated(); + } } -void MqttSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { - if (_state.enabled) { - Serial.println(F("WiFi connection dropped, stopping MQTT client.")); - onConfigUpdated(); - } +void MqttSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) +{ + if (_state.enabled) + { + Serial.println(F("WiFi connection dropped, stopping MQTT client.")); + onConfigUpdated(); + } } #elif defined(ESP8266) -void MqttSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { - if (_state.enabled) { - Serial.println(F("WiFi connection dropped, starting MQTT client.")); - onConfigUpdated(); - } +void MqttSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP &event) +{ + if (_state.enabled) + { + Serial.println(F("WiFi connection dropped, starting MQTT client.")); + onConfigUpdated(); + } } -void MqttSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { - if (_state.enabled) { - Serial.println(F("WiFi connection dropped, stopping MQTT client.")); - onConfigUpdated(); - } +void MqttSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected &event) +{ + if (_state.enabled) + { + Serial.println(F("WiFi connection dropped, stopping MQTT client.")); + onConfigUpdated(); + } } #endif -void MqttSettingsService::configureMqtt() { - // disconnect if currently connected - _mqttClient.disconnect(); - - // only connect if WiFi is connected and MQTT is enabled - if (_state.enabled && WiFi.isConnected()) { - Serial.println(F("Connecting to MQTT...")); - _mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port); - if (_state.username.length() > 0) { - _mqttClient.setCredentials( - retainCstr(_state.username.c_str(), &_retainedUsername), - retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword)); - } else { - _mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword)); +void MqttSettingsService::configureMqtt() +{ + // disconnect if currently connected + _mqttClient.disconnect(); + + // only connect if WiFi is connected and MQTT is enabled + if (_state.enabled && WiFi.isConnected()) + { + Serial.println(F("Connecting to MQTT...")); + _mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port); + if (_state.username.length() > 0) + { + _mqttClient.setCredentials( + retainCstr(_state.username.c_str(), &_retainedUsername), + retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword)); + } + else + { + _mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword)); + } + _mqttClient.setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId)); + _mqttClient.setKeepAlive(_state.keepAlive); + _mqttClient.setCleanSession(_state.cleanSession); + _mqttClient.setMaxTopicLength(_state.maxTopicLength); + _mqttClient.connect(); } - _mqttClient.setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId)); - _mqttClient.setKeepAlive(_state.keepAlive); - _mqttClient.setCleanSession(_state.cleanSession); - _mqttClient.setMaxTopicLength(_state.maxTopicLength); - _mqttClient.connect(); - } } diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h index a0117438..e5c4a7aa 100644 --- a/lib/framework/MqttSettingsService.h +++ b/lib/framework/MqttSettingsService.h @@ -48,101 +48,105 @@ #define MQTT_RECONNECTION_DELAY 5000 -class MqttSettings { - public: - // host and port - if enabled - bool enabled; - String host; - uint16_t port; - - // username and password - String username; - String password; - - // client id settings - String clientId; - - // connection settings - uint16_t keepAlive; - bool cleanSession; - uint16_t maxTopicLength; - - static void read(MqttSettings& settings, JsonObject& root) { - root["enabled"] = settings.enabled; - root["host"] = settings.host; - root["port"] = settings.port; - root["username"] = settings.username; - root["password"] = settings.password; - root["client_id"] = settings.clientId; - root["keep_alive"] = settings.keepAlive; - root["clean_session"] = settings.cleanSession; - root["max_topic_length"] = settings.maxTopicLength; - } - - static StateUpdateResult update(JsonObject& root, MqttSettings& settings) { - settings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; - settings.host = root["host"] | FACTORY_MQTT_HOST; - settings.port = root["port"] | FACTORY_MQTT_PORT; - settings.username = root["username"] | SettingValue::format(FACTORY_MQTT_USERNAME); - settings.password = root["password"] | FACTORY_MQTT_PASSWORD; - settings.clientId = root["client_id"] | SettingValue::format(FACTORY_MQTT_CLIENT_ID); - settings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE; - settings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION; - settings.maxTopicLength = root["max_topic_length"] | FACTORY_MQTT_MAX_TOPIC_LENGTH; - return StateUpdateResult::CHANGED; - } +class MqttSettings +{ +public: + // host and port - if enabled + bool enabled; + String host; + uint16_t port; + + // username and password + String username; + String password; + + // client id settings + String clientId; + + // connection settings + uint16_t keepAlive; + bool cleanSession; + uint16_t maxTopicLength; + + static void read(MqttSettings &settings, JsonObject &root) + { + root["enabled"] = settings.enabled; + root["host"] = settings.host; + root["port"] = settings.port; + root["username"] = settings.username; + root["password"] = settings.password; + root["client_id"] = settings.clientId; + root["keep_alive"] = settings.keepAlive; + root["clean_session"] = settings.cleanSession; + root["max_topic_length"] = settings.maxTopicLength; + } + + static StateUpdateResult update(JsonObject &root, MqttSettings &settings) + { + settings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; + settings.host = root["host"] | FACTORY_MQTT_HOST; + settings.port = root["port"] | FACTORY_MQTT_PORT; + settings.username = root["username"] | SettingValue::format(FACTORY_MQTT_USERNAME); + settings.password = root["password"] | FACTORY_MQTT_PASSWORD; + settings.clientId = root["client_id"] | SettingValue::format(FACTORY_MQTT_CLIENT_ID); + settings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE; + settings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION; + settings.maxTopicLength = root["max_topic_length"] | FACTORY_MQTT_MAX_TOPIC_LENGTH; + return StateUpdateResult::CHANGED; + } }; -class MqttSettingsService : public StatefulService { - public: - MqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); - ~MqttSettingsService(); +class MqttSettingsService : public StatefulService +{ +public: + MqttSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager); + ~MqttSettingsService(); - void begin(); - void loop(); - bool isEnabled(); - bool isConnected(); - const char* getClientId(); - AsyncMqttClientDisconnectReason getDisconnectReason(); - AsyncMqttClient* getMqttClient(); + void begin(); + void loop(); + bool isEnabled(); + bool isConnected(); + const char *getClientId(); + AsyncMqttClientDisconnectReason getDisconnectReason(); + AsyncMqttClient *getMqttClient(); - protected: - void onConfigUpdated(); +protected: + void onConfigUpdated(); - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; +private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; - // Pointers to hold retained copies of the mqtt client connection strings. - // This is required as AsyncMqttClient holds refrences to the supplied connection strings. - char* _retainedHost; - char* _retainedClientId; - char* _retainedUsername; - char* _retainedPassword; + // Pointers to hold retained copies of the mqtt client connection strings. + // This is required as AsyncMqttClient holds refrences to the supplied connection strings. + char *_retainedHost; + char *_retainedClientId; + char *_retainedUsername; + char *_retainedPassword; - // variable to help manage connection - bool _reconfigureMqtt; - unsigned long _disconnectedAt; + // variable to help manage connection + bool _reconfigureMqtt; + unsigned long _disconnectedAt; - // connection status - AsyncMqttClientDisconnectReason _disconnectReason; + // connection status + AsyncMqttClientDisconnectReason _disconnectReason; - // the MQTT client instance - AsyncMqttClient _mqttClient; + // the MQTT client instance + AsyncMqttClient _mqttClient; #ifdef ESP32 - void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); - void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); #elif defined(ESP8266) - WiFiEventHandler _onStationModeDisconnectedHandler; - WiFiEventHandler _onStationModeGotIPHandler; - void onStationModeGotIP(const WiFiEventStationModeGotIP& event); - void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); + WiFiEventHandler _onStationModeDisconnectedHandler; + WiFiEventHandler _onStationModeGotIPHandler; + void onStationModeGotIP(const WiFiEventStationModeGotIP &event); + void onStationModeDisconnected(const WiFiEventStationModeDisconnected &event); #endif - void onMqttConnect(bool sessionPresent); - void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); - void configureMqtt(); + void onMqttConnect(bool sessionPresent); + void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); + void configureMqtt(); }; -#endif // end MqttSettingsService_h +#endif // end MqttSettingsService_h diff --git a/lib/framework/MqttStatus.cpp b/lib/framework/MqttStatus.cpp index 8f5bac34..f9f38ee0 100644 --- a/lib/framework/MqttStatus.cpp +++ b/lib/framework/MqttStatus.cpp @@ -1,24 +1,25 @@ #include -MqttStatus::MqttStatus(AsyncWebServer* server, - MqttSettingsService* mqttSettingsService, - SecurityManager* securityManager) : - _mqttSettingsService(mqttSettingsService) { - server->on(MQTT_STATUS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest(std::bind(&MqttStatus::mqttStatus, this, std::placeholders::_1), - AuthenticationPredicates::IS_AUTHENTICATED)); +MqttStatus::MqttStatus(AsyncWebServer *server, + MqttSettingsService *mqttSettingsService, + SecurityManager *securityManager) : _mqttSettingsService(mqttSettingsService) +{ + server->on(MQTT_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&MqttStatus::mqttStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); } -void MqttStatus::mqttStatus(AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_MQTT_STATUS_SIZE); - JsonObject root = response->getRoot(); +void MqttStatus::mqttStatus(AsyncWebServerRequest *request) +{ + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_MQTT_STATUS_SIZE); + JsonObject root = response->getRoot(); - root["enabled"] = _mqttSettingsService->isEnabled(); - root["connected"] = _mqttSettingsService->isConnected(); - root["client_id"] = _mqttSettingsService->getClientId(); - root["disconnect_reason"] = (uint8_t)_mqttSettingsService->getDisconnectReason(); + root["enabled"] = _mqttSettingsService->isEnabled(); + root["connected"] = _mqttSettingsService->isConnected(); + root["client_id"] = _mqttSettingsService->getClientId(); + root["disconnect_reason"] = (uint8_t)_mqttSettingsService->getDisconnectReason(); - response->setLength(); - request->send(response); + response->setLength(); + request->send(response); } diff --git a/lib/framework/MqttStatus.h b/lib/framework/MqttStatus.h index a726d3b5..3a4f151d 100644 --- a/lib/framework/MqttStatus.h +++ b/lib/framework/MqttStatus.h @@ -18,14 +18,15 @@ #define MAX_MQTT_STATUS_SIZE 1024 #define MQTT_STATUS_SERVICE_PATH "/rest/mqttStatus" -class MqttStatus { - public: - MqttStatus(AsyncWebServer* server, MqttSettingsService* mqttSettingsService, SecurityManager* securityManager); +class MqttStatus +{ +public: + MqttStatus(AsyncWebServer *server, MqttSettingsService *mqttSettingsService, SecurityManager *securityManager); - private: - MqttSettingsService* _mqttSettingsService; +private: + MqttSettingsService *_mqttSettingsService; - void mqttStatus(AsyncWebServerRequest* request); + void mqttStatus(AsyncWebServerRequest *request); }; -#endif // end MqttStatus_h +#endif // end MqttStatus_h diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp index 75894d8c..9f857771 100644 --- a/lib/framework/NTPSettingsService.cpp +++ b/lib/framework/NTPSettingsService.cpp @@ -1,90 +1,104 @@ #include -NTPSettingsService::NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : - _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager), - _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE), - _timeHandler(TIME_PATH, - securityManager->wrapCallback( - std::bind(&NTPSettingsService::configureTime, this, std::placeholders::_1, std::placeholders::_2), - AuthenticationPredicates::IS_ADMIN)) { - _timeHandler.setMethod(HTTP_POST); - _timeHandler.setMaxContentLength(MAX_TIME_SIZE); - server->addHandler(&_timeHandler); +NTPSettingsService::NTPSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager) : _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE), + _timeHandler(TIME_PATH, + securityManager->wrapCallback( + std::bind(&NTPSettingsService::configureTime, this, std::placeholders::_1, std::placeholders::_2), + AuthenticationPredicates::IS_ADMIN)) +{ + _timeHandler.setMethod(HTTP_POST); + _timeHandler.setMaxContentLength(MAX_TIME_SIZE); + server->addHandler(&_timeHandler); #ifdef ESP32 - WiFi.onEvent( - std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), - WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); - WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), - WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); + WiFi.onEvent( + std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); + WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); #elif defined(ESP8266) - _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( - std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); - _onStationModeGotIPHandler = - WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1)); + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( + std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); + _onStationModeGotIPHandler = + WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1)); #endif - addUpdateHandler([&](const String& originId) { configureNTP(); }, false); + addUpdateHandler([&](const String &originId) + { configureNTP(); }, + false); } -void NTPSettingsService::begin() { - _fsPersistence.readFromFS(); - configureNTP(); +void NTPSettingsService::begin() +{ + _fsPersistence.readFromFS(); + configureNTP(); } #ifdef ESP32 -void NTPSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.println(F("Got IP address, starting NTP Synchronization")); - configureNTP(); +void NTPSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) +{ + Serial.println(F("Got IP address, starting NTP Synchronization")); + configureNTP(); } -void NTPSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.println(F("WiFi connection dropped, stopping NTP.")); - configureNTP(); +void NTPSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) +{ + Serial.println(F("WiFi connection dropped, stopping NTP.")); + configureNTP(); } #elif defined(ESP8266) -void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { - Serial.println(F("Got IP address, starting NTP Synchronization")); - configureNTP(); +void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP &event) +{ + Serial.println(F("Got IP address, starting NTP Synchronization")); + configureNTP(); } -void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { - Serial.println(F("WiFi connection dropped, stopping NTP.")); - configureNTP(); +void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected &event) +{ + Serial.println(F("WiFi connection dropped, stopping NTP.")); + configureNTP(); } #endif -void NTPSettingsService::configureNTP() { - if (WiFi.isConnected() && _state.enabled) { - Serial.println(F("Starting NTP...")); +void NTPSettingsService::configureNTP() +{ + if (WiFi.isConnected() && _state.enabled) + { + Serial.println(F("Starting NTP...")); #ifdef ESP32 - configTzTime(_state.tzFormat.c_str(), _state.server.c_str()); + configTzTime(_state.tzFormat.c_str(), _state.server.c_str()); #elif defined(ESP8266) - configTime(_state.tzFormat.c_str(), _state.server.c_str()); + configTime(_state.tzFormat.c_str(), _state.server.c_str()); #endif - } else { + } + else + { #ifdef ESP32 - setenv("TZ", _state.tzFormat.c_str(), 1); - tzset(); + setenv("TZ", _state.tzFormat.c_str(), 1); + tzset(); #elif defined(ESP8266) - setTZ(_state.tzFormat.c_str()); + setTZ(_state.tzFormat.c_str()); #endif - sntp_stop(); - } + sntp_stop(); + } } -void NTPSettingsService::configureTime(AsyncWebServerRequest* request, JsonVariant& json) { - if (!sntp_enabled() && json.is()) { - struct tm tm = {0}; - String timeLocal = json["local_time"]; - char* s = strptime(timeLocal.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); - if (s != nullptr) { - time_t time = mktime(&tm); - struct timeval now = {.tv_sec = time}; - settimeofday(&now, nullptr); - AsyncWebServerResponse* response = request->beginResponse(200); - request->send(response); - return; +void NTPSettingsService::configureTime(AsyncWebServerRequest *request, JsonVariant &json) +{ + if (!sntp_enabled() && json.is()) + { + struct tm tm = {0}; + String timeLocal = json["local_time"]; + char *s = strptime(timeLocal.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); + if (s != nullptr) + { + time_t time = mktime(&tm); + struct timeval now = {.tv_sec = time}; + settimeofday(&now, nullptr); + AsyncWebServerResponse *response = request->beginResponse(200); + request->send(response); + return; + } } - } - AsyncWebServerResponse* response = request->beginResponse(400); - request->send(response); + AsyncWebServerResponse *response = request->beginResponse(400); + request->send(response); } diff --git a/lib/framework/NTPSettingsService.h b/lib/framework/NTPSettingsService.h index bf25ca49..9d9891be 100644 --- a/lib/framework/NTPSettingsService.h +++ b/lib/framework/NTPSettingsService.h @@ -33,52 +33,56 @@ #define MAX_TIME_SIZE 256 #define TIME_PATH "/rest/time" -class NTPSettings { - public: - bool enabled; - String tzLabel; - String tzFormat; - String server; - - static void read(NTPSettings& settings, JsonObject& root) { - root["enabled"] = settings.enabled; - root["server"] = settings.server; - root["tz_label"] = settings.tzLabel; - root["tz_format"] = settings.tzFormat; - } - - static StateUpdateResult update(JsonObject& root, NTPSettings& settings) { - settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED; - settings.server = root["server"] | FACTORY_NTP_SERVER; - settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL; - settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT; - return StateUpdateResult::CHANGED; - } +class NTPSettings +{ +public: + bool enabled; + String tzLabel; + String tzFormat; + String server; + + static void read(NTPSettings &settings, JsonObject &root) + { + root["enabled"] = settings.enabled; + root["server"] = settings.server; + root["tz_label"] = settings.tzLabel; + root["tz_format"] = settings.tzFormat; + } + + static StateUpdateResult update(JsonObject &root, NTPSettings &settings) + { + settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED; + settings.server = root["server"] | FACTORY_NTP_SERVER; + settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL; + settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT; + return StateUpdateResult::CHANGED; + } }; -class NTPSettingsService : public StatefulService { - public: - NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); +class NTPSettingsService : public StatefulService +{ +public: + NTPSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager); - void begin(); + void begin(); - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; - AsyncCallbackJsonWebHandler _timeHandler; +private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + AsyncCallbackJsonWebHandler _timeHandler; #ifdef ESP32 - void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); - void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); #elif defined(ESP8266) - WiFiEventHandler _onStationModeDisconnectedHandler; - WiFiEventHandler _onStationModeGotIPHandler; + WiFiEventHandler _onStationModeDisconnectedHandler; + WiFiEventHandler _onStationModeGotIPHandler; - void onStationModeGotIP(const WiFiEventStationModeGotIP& event); - void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); + void onStationModeGotIP(const WiFiEventStationModeGotIP &event); + void onStationModeDisconnected(const WiFiEventStationModeDisconnected &event); #endif - void configureNTP(); - void configureTime(AsyncWebServerRequest* request, JsonVariant& json); + void configureNTP(); + void configureTime(AsyncWebServerRequest *request, JsonVariant &json); }; -#endif // end NTPSettingsService_h +#endif // end NTPSettingsService_h diff --git a/lib/framework/NTPStatus.cpp b/lib/framework/NTPStatus.cpp index 00dd31cd..b09a6779 100644 --- a/lib/framework/NTPStatus.cpp +++ b/lib/framework/NTPStatus.cpp @@ -1,10 +1,11 @@ #include -NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) { - server->on(NTP_STATUS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), - AuthenticationPredicates::IS_AUTHENTICATED)); +NTPStatus::NTPStatus(AsyncWebServer *server, SecurityManager *securityManager) +{ + server->on(NTP_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); } /* @@ -12,42 +13,46 @@ NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) { * * Uses a 25 byte buffer, large enough to fit an ISO time string with offset. */ -String formatTime(tm* time, const char* format) { - char time_string[25]; - strftime(time_string, 25, format, time); - return String(time_string); +String formatTime(tm *time, const char *format) +{ + char time_string[25]; + strftime(time_string, 25, format, time); + return String(time_string); } -String toUTCTimeString(tm* time) { - return formatTime(time, "%FT%TZ"); +String toUTCTimeString(tm *time) +{ + return formatTime(time, "%FT%TZ"); } -String toLocalTimeString(tm* time) { - return formatTime(time, "%FT%T"); +String toLocalTimeString(tm *time) +{ + return formatTime(time, "%FT%T"); } -void NTPStatus::ntpStatus(AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE); - JsonObject root = response->getRoot(); +void NTPStatus::ntpStatus(AsyncWebServerRequest *request) +{ + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE); + JsonObject root = response->getRoot(); - // grab the current instant in unix seconds - time_t now = time(nullptr); + // grab the current instant in unix seconds + time_t now = time(nullptr); - // only provide enabled/disabled status for now - root["status"] = sntp_enabled() ? 1 : 0; + // only provide enabled/disabled status for now + root["status"] = sntp_enabled() ? 1 : 0; - // the current time in UTC - root["utc_time"] = toUTCTimeString(gmtime(&now)); + // the current time in UTC + root["utc_time"] = toUTCTimeString(gmtime(&now)); - // local time with offset - root["local_time"] = toLocalTimeString(localtime(&now)); + // local time with offset + root["local_time"] = toLocalTimeString(localtime(&now)); - // the sntp server name - root["server"] = sntp_getservername(0); + // the sntp server name + root["server"] = sntp_getservername(0); - // device uptime in seconds - root["uptime"] = millis() / 1000; + // device uptime in seconds + root["uptime"] = millis() / 1000; - response->setLength(); - request->send(response); + response->setLength(); + request->send(response); } diff --git a/lib/framework/NTPStatus.h b/lib/framework/NTPStatus.h index 7bb91805..d9195f1e 100644 --- a/lib/framework/NTPStatus.h +++ b/lib/framework/NTPStatus.h @@ -20,12 +20,13 @@ #define MAX_NTP_STATUS_SIZE 1024 #define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus" -class NTPStatus { - public: - NTPStatus(AsyncWebServer* server, SecurityManager* securityManager); +class NTPStatus +{ +public: + NTPStatus(AsyncWebServer *server, SecurityManager *securityManager); - private: - void ntpStatus(AsyncWebServerRequest* request); +private: + void ntpStatus(AsyncWebServerRequest *request); }; -#endif // end NTPStatus_h +#endif // end NTPStatus_h diff --git a/lib/framework/OTASettingsService.cpp b/lib/framework/OTASettingsService.cpp index 09c648e4..52241e94 100644 --- a/lib/framework/OTASettingsService.cpp +++ b/lib/framework/OTASettingsService.cpp @@ -1,49 +1,59 @@ #include -OTASettingsService::OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : - _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager), - _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE), - _arduinoOTA(nullptr) { +OTASettingsService::OTASettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager) : _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE), + _arduinoOTA(nullptr) +{ #ifdef ESP32 - WiFi.onEvent(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), - WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); + WiFi.onEvent(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); #elif defined(ESP8266) - _onStationModeGotIPHandler = - WiFi.onStationModeGotIP(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1)); + _onStationModeGotIPHandler = + WiFi.onStationModeGotIP(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1)); #endif - addUpdateHandler([&](const String& originId) { configureArduinoOTA(); }, false); + addUpdateHandler([&](const String &originId) + { configureArduinoOTA(); }, + false); } -void OTASettingsService::begin() { - _fsPersistence.readFromFS(); - configureArduinoOTA(); +void OTASettingsService::begin() +{ + _fsPersistence.readFromFS(); + configureArduinoOTA(); } -void OTASettingsService::loop() { - if (_state.enabled && _arduinoOTA) { - _arduinoOTA->handle(); - } +void OTASettingsService::loop() +{ + if (_state.enabled && _arduinoOTA) + { + _arduinoOTA->handle(); + } } -void OTASettingsService::configureArduinoOTA() { - if (_arduinoOTA) { +void OTASettingsService::configureArduinoOTA() +{ + if (_arduinoOTA) + { #ifdef ESP32 - _arduinoOTA->end(); + _arduinoOTA->end(); #endif - delete _arduinoOTA; - _arduinoOTA = nullptr; - } - if (_state.enabled) { - Serial.println(F("Starting OTA Update Service...")); - _arduinoOTA = new ArduinoOTAClass; - _arduinoOTA->setPort(_state.port); - _arduinoOTA->setPassword(_state.password.c_str()); - _arduinoOTA->onStart([]() { Serial.println(F("Starting")); }); - _arduinoOTA->onEnd([]() { Serial.println(F("\r\nEnd")); }); - _arduinoOTA->onProgress([](unsigned int progress, unsigned int total) { - Serial.printf_P(PSTR("Progress: %u%%\r\n"), (progress / (total / 100))); - }); - _arduinoOTA->onError([](ota_error_t error) { + delete _arduinoOTA; + _arduinoOTA = nullptr; + } + if (_state.enabled) + { + Serial.println(F("Starting OTA Update Service...")); + _arduinoOTA = new ArduinoOTAClass; + _arduinoOTA->setPort(_state.port); + _arduinoOTA->setPassword(_state.password.c_str()); + _arduinoOTA->onStart([]() + { Serial.println(F("Starting")); }); + _arduinoOTA->onEnd([]() + { Serial.println(F("\r\nEnd")); }); + _arduinoOTA->onProgress([](unsigned int progress, unsigned int total) + { Serial.printf_P(PSTR("Progress: %u%%\r\n"), (progress / (total / 100))); }); + _arduinoOTA->onError([](ota_error_t error) + { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println(F("Auth Failed")); @@ -54,18 +64,19 @@ void OTASettingsService::configureArduinoOTA() { else if (error == OTA_RECEIVE_ERROR) Serial.println(F("Receive Failed")); else if (error == OTA_END_ERROR) - Serial.println(F("End Failed")); - }); - _arduinoOTA->begin(); - } + Serial.println(F("End Failed")); }); + _arduinoOTA->begin(); + } } #ifdef ESP32 -void OTASettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { - configureArduinoOTA(); +void OTASettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) +{ + configureArduinoOTA(); } #elif defined(ESP8266) -void OTASettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { - configureArduinoOTA(); +void OTASettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP &event) +{ + configureArduinoOTA(); } #endif diff --git a/lib/framework/OTASettingsService.h b/lib/framework/OTASettingsService.h index c8d8609d..7747063b 100644 --- a/lib/framework/OTASettingsService.h +++ b/lib/framework/OTASettingsService.h @@ -28,45 +28,49 @@ #define OTA_SETTINGS_FILE "/config/otaSettings.json" #define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings" -class OTASettings { - public: - bool enabled; - int port; - String password; +class OTASettings +{ +public: + bool enabled; + int port; + String password; - static void read(OTASettings& settings, JsonObject& root) { - root["enabled"] = settings.enabled; - root["port"] = settings.port; - root["password"] = settings.password; - } + static void read(OTASettings &settings, JsonObject &root) + { + root["enabled"] = settings.enabled; + root["port"] = settings.port; + root["password"] = settings.password; + } - static StateUpdateResult update(JsonObject& root, OTASettings& settings) { - settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED; - settings.port = root["port"] | FACTORY_OTA_PORT; - settings.password = root["password"] | FACTORY_OTA_PASSWORD; - return StateUpdateResult::CHANGED; - } + static StateUpdateResult update(JsonObject &root, OTASettings &settings) + { + settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED; + settings.port = root["port"] | FACTORY_OTA_PORT; + settings.password = root["password"] | FACTORY_OTA_PASSWORD; + return StateUpdateResult::CHANGED; + } }; -class OTASettingsService : public StatefulService { - public: - OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); +class OTASettingsService : public StatefulService +{ +public: + OTASettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager); - void begin(); - void loop(); + void begin(); + void loop(); - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; - ArduinoOTAClass* _arduinoOTA; +private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + ArduinoOTAClass *_arduinoOTA; - void configureArduinoOTA(); + void configureArduinoOTA(); #ifdef ESP32 - void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); #elif defined(ESP8266) - WiFiEventHandler _onStationModeGotIPHandler; - void onStationModeGotIP(const WiFiEventStationModeGotIP& event); + WiFiEventHandler _onStationModeGotIPHandler; + void onStationModeGotIP(const WiFiEventStationModeGotIP &event); #endif }; -#endif // end OTASettingsService_h +#endif // end OTASettingsService_h diff --git a/lib/framework/RestartService.cpp b/lib/framework/RestartService.cpp index 9036e40a..76df8e55 100644 --- a/lib/framework/RestartService.cpp +++ b/lib/framework/RestartService.cpp @@ -1,13 +1,15 @@ #include -RestartService::RestartService(AsyncWebServer* server, SecurityManager* securityManager) { - server->on(RESTART_SERVICE_PATH, - HTTP_POST, - securityManager->wrapRequest(std::bind(&RestartService::restart, this, std::placeholders::_1), - AuthenticationPredicates::IS_ADMIN)); +RestartService::RestartService(AsyncWebServer *server, SecurityManager *securityManager) +{ + server->on(RESTART_SERVICE_PATH, + HTTP_POST, + securityManager->wrapRequest(std::bind(&RestartService::restart, this, std::placeholders::_1), + AuthenticationPredicates::IS_ADMIN)); } -void RestartService::restart(AsyncWebServerRequest* request) { - request->onDisconnect(RestartService::restartNow); - request->send(200); +void RestartService::restart(AsyncWebServerRequest *request) +{ + request->onDisconnect(RestartService::restartNow); + request->send(200); } diff --git a/lib/framework/RestartService.h b/lib/framework/RestartService.h index 45a10084..3de01e6f 100644 --- a/lib/framework/RestartService.h +++ b/lib/framework/RestartService.h @@ -14,18 +14,20 @@ #define RESTART_SERVICE_PATH "/rest/restart" -class RestartService { - public: - RestartService(AsyncWebServer* server, SecurityManager* securityManager); +class RestartService +{ +public: + RestartService(AsyncWebServer *server, SecurityManager *securityManager); - static void restartNow() { - WiFi.disconnect(true); - delay(500); - ESP.restart(); - } + static void restartNow() + { + WiFi.disconnect(true); + delay(500); + ESP.restart(); + } - private: - void restart(AsyncWebServerRequest* request); +private: + void restart(AsyncWebServerRequest *request); }; -#endif // end RestartService_h +#endif // end RestartService_h diff --git a/lib/framework/SecurityManager.h b/lib/framework/SecurityManager.h index 2302debe..c026f1af 100644 --- a/lib/framework/SecurityManager.h +++ b/lib/framework/SecurityManager.h @@ -15,83 +15,94 @@ #define MAX_JWT_SIZE 128 -class User { - public: - String username; - String password; - bool admin; - - public: - User(String username, String password, bool admin) : username(username), password(password), admin(admin) { - } +class User +{ +public: + String username; + String password; + bool admin; + +public: + User(String username, String password, bool admin) : username(username), password(password), admin(admin) + { + } }; -class Authentication { - public: - User* user; - boolean authenticated; - - public: - Authentication(User& user) : user(new User(user)), authenticated(true) { - } - Authentication() : user(nullptr), authenticated(false) { - } - ~Authentication() { - delete (user); - } +class Authentication +{ +public: + User *user; + boolean authenticated; + +public: + Authentication(User &user) : user(new User(user)), authenticated(true) + { + } + Authentication() : user(nullptr), authenticated(false) + { + } + ~Authentication() + { + delete (user); + } }; -typedef std::function AuthenticationPredicate; - -class AuthenticationPredicates { - public: - static bool NONE_REQUIRED(Authentication& authentication) { - return true; - }; - static bool IS_AUTHENTICATED(Authentication& authentication) { - return authentication.authenticated; - }; - static bool IS_ADMIN(Authentication& authentication) { - return authentication.authenticated && authentication.user->admin; - }; +typedef std::function AuthenticationPredicate; + +class AuthenticationPredicates +{ +public: + static bool NONE_REQUIRED(Authentication &authentication) + { + return true; + }; + static bool IS_AUTHENTICATED(Authentication &authentication) + { + return authentication.authenticated; + }; + static bool IS_ADMIN(Authentication &authentication) + { + return authentication.authenticated && authentication.user->admin; + }; }; -class SecurityManager { - public: +class SecurityManager +{ +public: #if FT_ENABLED(FT_SECURITY) - /* - * Authenticate, returning the user if found - */ - virtual Authentication authenticate(const String& username, const String& password) = 0; + /* + * Authenticate, returning the user if found + */ + virtual Authentication authenticate(const String &username, const String &password) = 0; - /* - * Generate a JWT for the user provided - */ - virtual String generateJWT(User* user) = 0; + /* + * Generate a JWT for the user provided + */ + virtual String generateJWT(User *user) = 0; #endif - /* - * Check the request header for the Authorization token - */ - virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0; - - /** - * Filter a request with the provided predicate, only returning true if the predicate matches. - */ - virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0; - - /** - * Wrap the provided request to provide validation against an AuthenticationPredicate. - */ - virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, - AuthenticationPredicate predicate) = 0; - - /** - * Wrap the provided json request callback to provide validation against an AuthenticationPredicate. - */ - virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, - AuthenticationPredicate predicate) = 0; + /* + * Check the request header for the Authorization token + */ + virtual Authentication authenticateRequest(AsyncWebServerRequest *request) = 0; + + /** + * Filter a request with the provided predicate, only returning true if the predicate matches. + */ + virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided request to provide validation against an AuthenticationPredicate. + */ + virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided json request callback to provide validation against an AuthenticationPredicate. + */ + virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) = 0; }; -#endif // end SecurityManager_h +#endif // end SecurityManager_h diff --git a/lib/framework/SecuritySettingsService.cpp b/lib/framework/SecuritySettingsService.cpp index 87027db5..378e18a3 100644 --- a/lib/framework/SecuritySettingsService.cpp +++ b/lib/framework/SecuritySettingsService.cpp @@ -2,139 +2,173 @@ #if FT_ENABLED(FT_SECURITY) -SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : - _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this), - _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE), - _jwtHandler(FACTORY_JWT_SECRET) { - addUpdateHandler([&](const String& originId) { configureJWTHandler(); }, false); -} - -void SecuritySettingsService::begin() { - _fsPersistence.readFromFS(); - configureJWTHandler(); -} - -Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) { - AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER); - if (authorizationHeader) { - String value = authorizationHeader->value(); - if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) { - value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN); - return authenticateJWT(value); +SecuritySettingsService::SecuritySettingsService(AsyncWebServer *server, FS *fs) : _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this), + _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE), + _jwtHandler(FACTORY_JWT_SECRET) +{ + addUpdateHandler([&](const String &originId) + { configureJWTHandler(); }, + false); +} + +void SecuritySettingsService::begin() +{ + _fsPersistence.readFromFS(); + configureJWTHandler(); +} + +Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest *request) +{ + AsyncWebHeader *authorizationHeader = request->getHeader(AUTHORIZATION_HEADER); + if (authorizationHeader) + { + String value = authorizationHeader->value(); + if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) + { + value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN); + return authenticateJWT(value); + } } - } else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) { - AsyncWebParameter* tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER); - String value = tokenParamater->value(); - return authenticateJWT(value); - } - return Authentication(); -} - -void SecuritySettingsService::configureJWTHandler() { - _jwtHandler.setSecret(_state.jwtSecret); -} - -Authentication SecuritySettingsService::authenticateJWT(String& jwt) { - DynamicJsonDocument payloadDocument(MAX_JWT_SIZE); - _jwtHandler.parseJWT(jwt, payloadDocument); - if (payloadDocument.is()) { - JsonObject parsedPayload = payloadDocument.as(); - String username = parsedPayload["username"]; - for (User _user : _state.users) { - if (_user.username == username && validatePayload(parsedPayload, &_user)) { - return Authentication(_user); - } + else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) + { + AsyncWebParameter *tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER); + String value = tokenParamater->value(); + return authenticateJWT(value); } - } - return Authentication(); + return Authentication(); +} + +void SecuritySettingsService::configureJWTHandler() +{ + _jwtHandler.setSecret(_state.jwtSecret); +} + +Authentication SecuritySettingsService::authenticateJWT(String &jwt) +{ + DynamicJsonDocument payloadDocument(MAX_JWT_SIZE); + _jwtHandler.parseJWT(jwt, payloadDocument); + if (payloadDocument.is()) + { + JsonObject parsedPayload = payloadDocument.as(); + String username = parsedPayload["username"]; + for (User _user : _state.users) + { + if (_user.username == username && validatePayload(parsedPayload, &_user)) + { + return Authentication(_user); + } + } + } + return Authentication(); } -Authentication SecuritySettingsService::authenticate(const String& username, const String& password) { - for (User _user : _state.users) { - if (_user.username == username && _user.password == password) { - return Authentication(_user); +Authentication SecuritySettingsService::authenticate(const String &username, const String &password) +{ + for (User _user : _state.users) + { + if (_user.username == username && _user.password == password) + { + return Authentication(_user); + } } - } - return Authentication(); + return Authentication(); } -inline void populateJWTPayload(JsonObject& payload, User* user) { - payload["username"] = user->username; - payload["admin"] = user->admin; +inline void populateJWTPayload(JsonObject &payload, User *user) +{ + payload["username"] = user->username; + payload["admin"] = user->admin; } -boolean SecuritySettingsService::validatePayload(JsonObject& parsedPayload, User* user) { - DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); - JsonObject payload = jsonDocument.to(); - populateJWTPayload(payload, user); - return payload == parsedPayload; +boolean SecuritySettingsService::validatePayload(JsonObject &parsedPayload, User *user) +{ + DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); + JsonObject payload = jsonDocument.to(); + populateJWTPayload(payload, user); + return payload == parsedPayload; } -String SecuritySettingsService::generateJWT(User* user) { - DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); - JsonObject payload = jsonDocument.to(); - populateJWTPayload(payload, user); - return _jwtHandler.buildJWT(payload); +String SecuritySettingsService::generateJWT(User *user) +{ + DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); + JsonObject payload = jsonDocument.to(); + populateJWTPayload(payload, user); + return _jwtHandler.buildJWT(payload); } -ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) { - return [this, predicate](AsyncWebServerRequest* request) { - Authentication authentication = authenticateRequest(request); - return predicate(authentication); - }; +ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) +{ + return [this, predicate](AsyncWebServerRequest *request) + { + Authentication authentication = authenticateRequest(request); + return predicate(authentication); + }; } ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, - AuthenticationPredicate predicate) { - return [this, onRequest, predicate](AsyncWebServerRequest* request) { - Authentication authentication = authenticateRequest(request); - if (!predicate(authentication)) { - request->send(401); - return; - } - onRequest(request); - }; + AuthenticationPredicate predicate) +{ + return [this, onRequest, predicate](AsyncWebServerRequest *request) + { + Authentication authentication = authenticateRequest(request); + if (!predicate(authentication)) + { + request->send(401); + return; + } + onRequest(request); + }; } ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, - AuthenticationPredicate predicate) { - return [this, onRequest, predicate](AsyncWebServerRequest* request, JsonVariant& json) { - Authentication authentication = authenticateRequest(request); - if (!predicate(authentication)) { - request->send(401); - return; - } - onRequest(request, json); - }; + AuthenticationPredicate predicate) +{ + return [this, onRequest, predicate](AsyncWebServerRequest *request, JsonVariant &json) + { + Authentication authentication = authenticateRequest(request); + if (!predicate(authentication)) + { + request->send(401); + return; + } + onRequest(request, json); + }; } #else User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true); -SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : SecurityManager() { +SecuritySettingsService::SecuritySettingsService(AsyncWebServer *server, FS *fs) : SecurityManager() +{ } -SecuritySettingsService::~SecuritySettingsService() { +SecuritySettingsService::~SecuritySettingsService() +{ } -ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) { - return [this, predicate](AsyncWebServerRequest* request) { return true; }; +ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) +{ + return [this, predicate](AsyncWebServerRequest *request) + { return true; }; } // Return the admin user on all request - disabling security features -Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) { - return Authentication(ADMIN_USER); +Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest *request) +{ + return Authentication(ADMIN_USER); } // Return the function unwrapped ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, - AuthenticationPredicate predicate) { - return onRequest; + AuthenticationPredicate predicate) +{ + return onRequest; } ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, - AuthenticationPredicate predicate) { - return onRequest; + AuthenticationPredicate predicate) +{ + return onRequest; } #endif diff --git a/lib/framework/SecuritySettingsService.h b/lib/framework/SecuritySettingsService.h index c1a3f17c..ee23ff35 100644 --- a/lib/framework/SecuritySettingsService.h +++ b/lib/framework/SecuritySettingsService.h @@ -32,88 +32,98 @@ #if FT_ENABLED(FT_SECURITY) -class SecuritySettings { - public: - String jwtSecret; - std::list users; - - static void read(SecuritySettings& settings, JsonObject& root) { - // secret - root["jwt_secret"] = settings.jwtSecret; - - // users - JsonArray users = root.createNestedArray("users"); - for (User user : settings.users) { - JsonObject userRoot = users.createNestedObject(); - userRoot["username"] = user.username; - userRoot["password"] = user.password; - userRoot["admin"] = user.admin; +class SecuritySettings +{ +public: + String jwtSecret; + std::list users; + + static void read(SecuritySettings &settings, JsonObject &root) + { + // secret + root["jwt_secret"] = settings.jwtSecret; + + // users + JsonArray users = root.createNestedArray("users"); + for (User user : settings.users) + { + JsonObject userRoot = users.createNestedObject(); + userRoot["username"] = user.username; + userRoot["password"] = user.password; + userRoot["admin"] = user.admin; + } } - } - - static StateUpdateResult update(JsonObject& root, SecuritySettings& settings) { - // secret - settings.jwtSecret = root["jwt_secret"] | SettingValue::format(FACTORY_JWT_SECRET); - - // users - settings.users.clear(); - if (root["users"].is()) { - for (JsonVariant user : root["users"].as()) { - settings.users.push_back(User(user["username"], user["password"], user["admin"])); - } - } else { - settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true)); - settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false)); + + static StateUpdateResult update(JsonObject &root, SecuritySettings &settings) + { + // secret + settings.jwtSecret = root["jwt_secret"] | SettingValue::format(FACTORY_JWT_SECRET); + + // users + settings.users.clear(); + if (root["users"].is()) + { + for (JsonVariant user : root["users"].as()) + { + settings.users.push_back(User(user["username"], user["password"], user["admin"])); + } + } + else + { + settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true)); + settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false)); + } + return StateUpdateResult::CHANGED; } - return StateUpdateResult::CHANGED; - } }; -class SecuritySettingsService : public StatefulService, public SecurityManager { - public: - SecuritySettingsService(AsyncWebServer* server, FS* fs); - - void begin(); - - // Functions to implement SecurityManager - Authentication authenticate(const String& username, const String& password); - Authentication authenticateRequest(AsyncWebServerRequest* request); - String generateJWT(User* user); - ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); - ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); - ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate); - - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; - ArduinoJsonJWT _jwtHandler; - - void configureJWTHandler(); - - /* - * Lookup the user by JWT - */ - Authentication authenticateJWT(String& jwt); - - /* - * Verify the payload is correct - */ - boolean validatePayload(JsonObject& parsedPayload, User* user); +class SecuritySettingsService : public StatefulService, public SecurityManager +{ +public: + SecuritySettingsService(AsyncWebServer *server, FS *fs); + + void begin(); + + // Functions to implement SecurityManager + Authentication authenticate(const String &username, const String &password); + Authentication authenticateRequest(AsyncWebServerRequest *request); + String generateJWT(User *user); + ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); + ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); + ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate); + +private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + ArduinoJsonJWT _jwtHandler; + + void configureJWTHandler(); + + /* + * Lookup the user by JWT + */ + Authentication authenticateJWT(String &jwt); + + /* + * Verify the payload is correct + */ + boolean validatePayload(JsonObject &parsedPayload, User *user); }; #else -class SecuritySettingsService : public SecurityManager { - public: - SecuritySettingsService(AsyncWebServer* server, FS* fs); - ~SecuritySettingsService(); - - // minimal set of functions to support framework with security settings disabled - Authentication authenticateRequest(AsyncWebServerRequest* request); - ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); - ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); - ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate); +class SecuritySettingsService : public SecurityManager +{ +public: + SecuritySettingsService(AsyncWebServer *server, FS *fs); + ~SecuritySettingsService(); + + // minimal set of functions to support framework with security settings disabled + Authentication authenticateRequest(AsyncWebServerRequest *request); + ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); + ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); + ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate); }; -#endif // end FT_ENABLED(FT_SECURITY) -#endif // end SecuritySettingsService_h +#endif // end FT_ENABLED(FT_SECURITY) +#endif // end SecuritySettingsService_h diff --git a/lib/framework/SettingValue.cpp b/lib/framework/SettingValue.cpp index c12e1b04..bd365022 100644 --- a/lib/framework/SettingValue.cpp +++ b/lib/framework/SettingValue.cpp @@ -1,55 +1,62 @@ #include -namespace SettingValue { +namespace SettingValue +{ #ifdef ESP32 -const String PLATFORM = "esp32"; + const String PLATFORM = "esp32"; #elif defined(ESP8266) -const String PLATFORM = "esp8266"; + const String PLATFORM = "esp8266"; #endif -/** - * Returns a new string after replacing each instance of the pattern with a value generated by calling the provided - * callback. - */ -String replaceEach(String value, String pattern, String (*generateReplacement)()) { - while (true) { - int index = value.indexOf(pattern); - if (index == -1) { - break; + /** + * Returns a new string after replacing each instance of the pattern with a value generated by calling the provided + * callback. + */ + String replaceEach(String value, String pattern, String (*generateReplacement)()) + { + while (true) + { + int index = value.indexOf(pattern); + if (index == -1) + { + break; + } + value = value.substring(0, index) + generateReplacement() + value.substring(index + pattern.length()); + } + return value; } - value = value.substring(0, index) + generateReplacement() + value.substring(index + pattern.length()); - } - return value; -} -/** - * Generates a random number, encoded as a hex string. - */ -String getRandom() { - return String(random(2147483647), HEX); -} + /** + * Generates a random number, encoded as a hex string. + */ + String getRandom() + { + return String(random(2147483647), HEX); + } -/** - * Uses the station's MAC address to create a unique id for each device. - */ -String getUniqueId() { - uint8_t mac[6]; + /** + * Uses the station's MAC address to create a unique id for each device. + */ + String getUniqueId() + { + uint8_t mac[6]; #ifdef ESP32 - esp_read_mac(mac, ESP_MAC_WIFI_STA); + esp_read_mac(mac, ESP_MAC_WIFI_STA); #elif defined(ESP8266) - wifi_get_macaddr(STATION_IF, mac); + wifi_get_macaddr(STATION_IF, mac); #endif - char macStr[13] = {0}; - sprintf(macStr, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return String(macStr); -} + char macStr[13] = {0}; + sprintf(macStr, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return String(macStr); + } -String format(String value) { - value = replaceEach(value, "#{random}", getRandom); - value.replace("#{unique_id}", getUniqueId()); - value.replace("#{platform}", PLATFORM); - return value; -} + String format(String value) + { + value = replaceEach(value, "#{random}", getRandom); + value.replace("#{unique_id}", getUniqueId()); + value.replace("#{platform}", PLATFORM); + return value; + } -}; // end namespace SettingValue +}; // end namespace SettingValue diff --git a/lib/framework/SettingValue.h b/lib/framework/SettingValue.h index cc5e0e37..a8b46b22 100644 --- a/lib/framework/SettingValue.h +++ b/lib/framework/SettingValue.h @@ -7,8 +7,9 @@ #include #endif -namespace SettingValue { -String format(String value); +namespace SettingValue +{ + String format(String value); }; -#endif // end SettingValue +#endif // end SettingValue diff --git a/lib/framework/StatefulService.h b/lib/framework/StatefulService.h index 9a38e66a..6d156d27 100644 --- a/lib/framework/StatefulService.h +++ b/lib/framework/StatefulService.h @@ -15,134 +15,156 @@ #define DEFAULT_BUFFER_SIZE 1024 #endif -enum class StateUpdateResult { - CHANGED = 0, // The update changed the state and propagation should take place if required - UNCHANGED, // The state was unchanged, propagation should not take place - ERROR // There was a problem updating the state, propagation should not take place +enum class StateUpdateResult +{ + CHANGED = 0, // The update changed the state and propagation should take place if required + UNCHANGED, // The state was unchanged, propagation should not take place + ERROR // There was a problem updating the state, propagation should not take place }; template -using JsonStateUpdater = std::function; +using JsonStateUpdater = std::function; template -using JsonStateReader = std::function; +using JsonStateReader = std::function; typedef size_t update_handler_id_t; -typedef std::function StateUpdateCallback; - -typedef struct StateUpdateHandlerInfo { - static update_handler_id_t currentUpdatedHandlerId; - update_handler_id_t _id; - StateUpdateCallback _cb; - bool _allowRemove; - StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : - _id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){}; +typedef std::function StateUpdateCallback; + +typedef struct StateUpdateHandlerInfo +{ + static update_handler_id_t currentUpdatedHandlerId; + update_handler_id_t _id; + StateUpdateCallback _cb; + bool _allowRemove; + StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : _id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){}; } StateUpdateHandlerInfo_t; template -class StatefulService { - public: - template +class StatefulService +{ +public: + template #ifdef ESP32 - StatefulService(Args&&... args) : - _state(std::forward(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) { - } + StatefulService(Args &&...args) : _state(std::forward(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) + { + } #else - StatefulService(Args&&... args) : _state(std::forward(args)...) { - } + StatefulService(Args &&...args) : _state(std::forward(args)...) + { + } #endif - update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) { - if (!cb) { - return 0; + update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) + { + if (!cb) + { + return 0; + } + StateUpdateHandlerInfo_t updateHandler(cb, allowRemove); + _updateHandlers.push_back(updateHandler); + return updateHandler._id; + } + + void removeUpdateHandler(update_handler_id_t id) + { + for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) + { + if ((*i)._allowRemove && (*i)._id == id) + { + i = _updateHandlers.erase(i); + } + else + { + ++i; + } + } + } + + StateUpdateResult update(std::function stateUpdater, const String &originId) + { + beginTransaction(); + StateUpdateResult result = stateUpdater(_state); + endTransaction(); + if (result == StateUpdateResult::CHANGED) + { + callUpdateHandlers(originId); + } + return result; + } + + StateUpdateResult updateWithoutPropagation(std::function stateUpdater) + { + beginTransaction(); + StateUpdateResult result = stateUpdater(_state); + endTransaction(); + return result; } - StateUpdateHandlerInfo_t updateHandler(cb, allowRemove); - _updateHandlers.push_back(updateHandler); - return updateHandler._id; - } - - void removeUpdateHandler(update_handler_id_t id) { - for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) { - if ((*i)._allowRemove && (*i)._id == id) { - i = _updateHandlers.erase(i); - } else { - ++i; - } + + StateUpdateResult update(JsonObject &jsonObject, JsonStateUpdater stateUpdater, const String &originId) + { + beginTransaction(); + StateUpdateResult result = stateUpdater(jsonObject, _state); + endTransaction(); + if (result == StateUpdateResult::CHANGED) + { + callUpdateHandlers(originId); + } + return result; + } + + StateUpdateResult updateWithoutPropagation(JsonObject &jsonObject, JsonStateUpdater stateUpdater) + { + beginTransaction(); + StateUpdateResult result = stateUpdater(jsonObject, _state); + endTransaction(); + return result; } - } - - StateUpdateResult update(std::function stateUpdater, const String& originId) { - beginTransaction(); - StateUpdateResult result = stateUpdater(_state); - endTransaction(); - if (result == StateUpdateResult::CHANGED) { - callUpdateHandlers(originId); + + void read(std::function stateReader) + { + beginTransaction(); + stateReader(_state); + endTransaction(); } - return result; - } - - StateUpdateResult updateWithoutPropagation(std::function stateUpdater) { - beginTransaction(); - StateUpdateResult result = stateUpdater(_state); - endTransaction(); - return result; - } - - StateUpdateResult update(JsonObject& jsonObject, JsonStateUpdater stateUpdater, const String& originId) { - beginTransaction(); - StateUpdateResult result = stateUpdater(jsonObject, _state); - endTransaction(); - if (result == StateUpdateResult::CHANGED) { - callUpdateHandlers(originId); + + void read(JsonObject &jsonObject, JsonStateReader stateReader) + { + beginTransaction(); + stateReader(_state, jsonObject); + endTransaction(); } - return result; - } - - StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater stateUpdater) { - beginTransaction(); - StateUpdateResult result = stateUpdater(jsonObject, _state); - endTransaction(); - return result; - } - - void read(std::function stateReader) { - beginTransaction(); - stateReader(_state); - endTransaction(); - } - - void read(JsonObject& jsonObject, JsonStateReader stateReader) { - beginTransaction(); - stateReader(_state, jsonObject); - endTransaction(); - } - - void callUpdateHandlers(const String& originId) { - for (const StateUpdateHandlerInfo_t& updateHandler : _updateHandlers) { - updateHandler._cb(originId); + + void callUpdateHandlers(const String &originId) + { + for (const StateUpdateHandlerInfo_t &updateHandler : _updateHandlers) + { + updateHandler._cb(originId); + } } - } - protected: - T _state; +protected: + T _state; - inline void beginTransaction() { + inline void beginTransaction() + { #ifdef ESP32 - xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); + xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); #endif - } + } - inline void endTransaction() { + inline void endTransaction() + { #ifdef ESP32 - xSemaphoreGiveRecursive(_accessMutex); + xSemaphoreGiveRecursive(_accessMutex); #endif - } + } - private: +private: #ifdef ESP32 - SemaphoreHandle_t _accessMutex; + SemaphoreHandle_t _accessMutex; #endif - std::list _updateHandlers; + std::list _updateHandlers; }; -#endif // end StatefulService_h +#endif // end StatefulService_h diff --git a/lib/framework/SystemStatus.cpp b/lib/framework/SystemStatus.cpp index fa637e56..dcab43ab 100644 --- a/lib/framework/SystemStatus.cpp +++ b/lib/framework/SystemStatus.cpp @@ -1,45 +1,47 @@ #include -SystemStatus::SystemStatus(AsyncWebServer* server, SecurityManager* securityManager) { - server->on(SYSTEM_STATUS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, std::placeholders::_1), - AuthenticationPredicates::IS_AUTHENTICATED)); +SystemStatus::SystemStatus(AsyncWebServer *server, SecurityManager *securityManager) +{ + server->on(SYSTEM_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); } -void SystemStatus::systemStatus(AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE); - JsonObject root = response->getRoot(); +void SystemStatus::systemStatus(AsyncWebServerRequest *request) +{ + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE); + JsonObject root = response->getRoot(); #ifdef ESP32 - root["esp_platform"] = "esp32"; - root["max_alloc_heap"] = ESP.getMaxAllocHeap(); - root["psram_size"] = ESP.getPsramSize(); - root["free_psram"] = ESP.getFreePsram(); + root["esp_platform"] = "esp32"; + root["max_alloc_heap"] = ESP.getMaxAllocHeap(); + root["psram_size"] = ESP.getPsramSize(); + root["free_psram"] = ESP.getFreePsram(); #elif defined(ESP8266) - root["esp_platform"] = "esp8266"; - root["max_alloc_heap"] = ESP.getMaxFreeBlockSize(); - root["heap_fragmentation"] = ESP.getHeapFragmentation(); + root["esp_platform"] = "esp8266"; + root["max_alloc_heap"] = ESP.getMaxFreeBlockSize(); + root["heap_fragmentation"] = ESP.getHeapFragmentation(); #endif - root["cpu_freq_mhz"] = ESP.getCpuFreqMHz(); - root["free_heap"] = ESP.getFreeHeap(); - root["sketch_size"] = ESP.getSketchSize(); - root["free_sketch_space"] = ESP.getFreeSketchSpace(); - root["sdk_version"] = ESP.getSdkVersion(); - root["flash_chip_size"] = ESP.getFlashChipSize(); - root["flash_chip_speed"] = ESP.getFlashChipSpeed(); + root["cpu_freq_mhz"] = ESP.getCpuFreqMHz(); + root["free_heap"] = ESP.getFreeHeap(); + root["sketch_size"] = ESP.getSketchSize(); + root["free_sketch_space"] = ESP.getFreeSketchSpace(); + root["sdk_version"] = ESP.getSdkVersion(); + root["flash_chip_size"] = ESP.getFlashChipSize(); + root["flash_chip_speed"] = ESP.getFlashChipSpeed(); // TODO - Ideally this class will take an *FS and extract the file system information from there. // ESP8266 and ESP32 do not have feature parity in FS.h which currently makes that difficult. #ifdef ESP32 - root["fs_total"] = ESPFS.totalBytes(); - root["fs_used"] = ESPFS.usedBytes(); + root["fs_total"] = ESPFS.totalBytes(); + root["fs_used"] = ESPFS.usedBytes(); #elif defined(ESP8266) - FSInfo fs_info; - ESPFS.info(fs_info); - root["fs_total"] = fs_info.totalBytes; - root["fs_used"] = fs_info.usedBytes; + FSInfo fs_info; + ESPFS.info(fs_info); + root["fs_total"] = fs_info.totalBytes; + root["fs_used"] = fs_info.usedBytes; #endif - response->setLength(); - request->send(response); + response->setLength(); + request->send(response); } diff --git a/lib/framework/SystemStatus.h b/lib/framework/SystemStatus.h index 3175ec43..be965902 100644 --- a/lib/framework/SystemStatus.h +++ b/lib/framework/SystemStatus.h @@ -18,12 +18,13 @@ #define MAX_ESP_STATUS_SIZE 1024 #define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" -class SystemStatus { - public: - SystemStatus(AsyncWebServer* server, SecurityManager* securityManager); +class SystemStatus +{ +public: + SystemStatus(AsyncWebServer *server, SecurityManager *securityManager); - private: - void systemStatus(AsyncWebServerRequest* request); +private: + void systemStatus(AsyncWebServerRequest *request); }; -#endif // end SystemStatus_h +#endif // end SystemStatus_h diff --git a/lib/framework/UploadFirmwareService.cpp b/lib/framework/UploadFirmwareService.cpp index 1858ace2..be8c9634 100644 --- a/lib/framework/UploadFirmwareService.cpp +++ b/lib/framework/UploadFirmwareService.cpp @@ -1,85 +1,102 @@ #include -UploadFirmwareService::UploadFirmwareService(AsyncWebServer* server, SecurityManager* securityManager) : - _securityManager(securityManager) { - server->on(UPLOAD_FIRMWARE_PATH, - HTTP_POST, - std::bind(&UploadFirmwareService::uploadComplete, this, std::placeholders::_1), - std::bind(&UploadFirmwareService::handleUpload, - this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4, - std::placeholders::_5, - std::placeholders::_6)); +UploadFirmwareService::UploadFirmwareService(AsyncWebServer *server, SecurityManager *securityManager) : _securityManager(securityManager) +{ + server->on(UPLOAD_FIRMWARE_PATH, + HTTP_POST, + std::bind(&UploadFirmwareService::uploadComplete, this, std::placeholders::_1), + std::bind(&UploadFirmwareService::handleUpload, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); #ifdef ESP8266 - Update.runAsync(true); + Update.runAsync(true); #endif } -void UploadFirmwareService::handleUpload(AsyncWebServerRequest* request, - const String& filename, +void UploadFirmwareService::handleUpload(AsyncWebServerRequest *request, + const String &filename, size_t index, - uint8_t* data, + uint8_t *data, size_t len, - bool final) { - if (!index) { - Authentication authentication = _securityManager->authenticateRequest(request); - if (AuthenticationPredicates::IS_ADMIN(authentication)) { - if (Update.begin(request->contentLength())) { - // success, let's make sure we end the update if the client hangs up - request->onDisconnect(UploadFirmwareService::handleEarlyDisconnect); - } else { - // failed to begin, send an error response - Update.printError(Serial); - handleError(request, 500); - } - } else { - // send the forbidden response - handleError(request, 403); + bool final) +{ + if (!index) + { + Authentication authentication = _securityManager->authenticateRequest(request); + if (AuthenticationPredicates::IS_ADMIN(authentication)) + { + if (Update.begin(request->contentLength())) + { + // success, let's make sure we end the update if the client hangs up + request->onDisconnect(UploadFirmwareService::handleEarlyDisconnect); + } + else + { + // failed to begin, send an error response + Update.printError(Serial); + handleError(request, 500); + } + } + else + { + // send the forbidden response + handleError(request, 403); + } } - } - // if we haven't delt with an error, continue with the update - if (!request->_tempObject) { - if (Update.write(data, len) != len) { - Update.printError(Serial); - handleError(request, 500); + // if we haven't delt with an error, continue with the update + if (!request->_tempObject) + { + if (Update.write(data, len) != len) + { + Update.printError(Serial); + handleError(request, 500); + } + if (final) + { + if (!Update.end(true)) + { + Update.printError(Serial); + handleError(request, 500); + } + } } - if (final) { - if (!Update.end(true)) { - Update.printError(Serial); - handleError(request, 500); - } - } - } } -void UploadFirmwareService::uploadComplete(AsyncWebServerRequest* request) { - // if no error, send the success response - if (!request->_tempObject) { - request->onDisconnect(RestartService::restartNow); - AsyncWebServerResponse* response = request->beginResponse(200); - request->send(response); - } +void UploadFirmwareService::uploadComplete(AsyncWebServerRequest *request) +{ + // if no error, send the success response + if (!request->_tempObject) + { + request->onDisconnect(RestartService::restartNow); + AsyncWebServerResponse *response = request->beginResponse(200); + request->send(response); + } } -void UploadFirmwareService::handleError(AsyncWebServerRequest* request, int code) { - // if we have had an error already, do nothing - if (request->_tempObject) { - return; - } - // send the error code to the client and record the error code in the temp object - request->_tempObject = new int(code); - AsyncWebServerResponse* response = request->beginResponse(code); - request->send(response); +void UploadFirmwareService::handleError(AsyncWebServerRequest *request, int code) +{ + // if we have had an error already, do nothing + if (request->_tempObject) + { + return; + } + // send the error code to the client and record the error code in the temp object + request->_tempObject = new int(code); + AsyncWebServerResponse *response = request->beginResponse(code); + request->send(response); } -void UploadFirmwareService::handleEarlyDisconnect() { +void UploadFirmwareService::handleEarlyDisconnect() +{ #ifdef ESP32 - Update.abort(); + Update.abort(); #elif defined(ESP8266) - Update.end(); + Update.end(); #endif } diff --git a/lib/framework/UploadFirmwareService.h b/lib/framework/UploadFirmwareService.h index 6312af15..9ba53dbe 100644 --- a/lib/framework/UploadFirmwareService.h +++ b/lib/framework/UploadFirmwareService.h @@ -18,21 +18,22 @@ #define UPLOAD_FIRMWARE_PATH "/rest/uploadFirmware" -class UploadFirmwareService { - public: - UploadFirmwareService(AsyncWebServer* server, SecurityManager* securityManager); +class UploadFirmwareService +{ +public: + UploadFirmwareService(AsyncWebServer *server, SecurityManager *securityManager); - private: - SecurityManager* _securityManager; - void handleUpload(AsyncWebServerRequest* request, - const String& filename, - size_t index, - uint8_t* data, - size_t len, - bool final); - void uploadComplete(AsyncWebServerRequest* request); - void handleError(AsyncWebServerRequest* request, int code); - static void handleEarlyDisconnect(); +private: + SecurityManager *_securityManager; + void handleUpload(AsyncWebServerRequest *request, + const String &filename, + size_t index, + uint8_t *data, + size_t len, + bool final); + void uploadComplete(AsyncWebServerRequest *request); + void handleError(AsyncWebServerRequest *request, int code); + static void handleEarlyDisconnect(); }; -#endif // end UploadFirmwareService_h +#endif // end UploadFirmwareService_h diff --git a/lib/framework/WebSocketTxRx.h b/lib/framework/WebSocketTxRx.h index d2871ad9..14c9f4d7 100644 --- a/lib/framework/WebSocketTxRx.h +++ b/lib/framework/WebSocketTxRx.h @@ -11,263 +11,288 @@ #define WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX "websocket:" template -class WebSocketConnector { - protected: - StatefulService* _statefulService; - AsyncWebServer* _server; - AsyncWebSocket _webSocket; - size_t _bufferSize; +class WebSocketConnector +{ +protected: + StatefulService *_statefulService; + AsyncWebServer *_server; + AsyncWebSocket _webSocket; + size_t _bufferSize; - WebSocketConnector(StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - SecurityManager* securityManager, - AuthenticationPredicate authenticationPredicate, - size_t bufferSize) : - _statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) { - _webSocket.setFilter(securityManager->filterRequest(authenticationPredicate)); - _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, - this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4, - std::placeholders::_5, - std::placeholders::_6)); - _server->addHandler(&_webSocket); - _server->on(webSocketPath, HTTP_GET, std::bind(&WebSocketConnector::forbidden, this, std::placeholders::_1)); - } + WebSocketConnector(StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + SecurityManager *securityManager, + AuthenticationPredicate authenticationPredicate, + size_t bufferSize) : _statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) + { + _webSocket.setFilter(securityManager->filterRequest(authenticationPredicate)); + _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); + _server->addHandler(&_webSocket); + _server->on(webSocketPath, HTTP_GET, std::bind(&WebSocketConnector::forbidden, this, std::placeholders::_1)); + } - WebSocketConnector(StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - size_t bufferSize) : - _statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) { - _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, - this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4, - std::placeholders::_5, - std::placeholders::_6)); - _server->addHandler(&_webSocket); - } + WebSocketConnector(StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + size_t bufferSize) : _statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) + { + _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); + _server->addHandler(&_webSocket); + } - virtual void onWSEvent(AsyncWebSocket* server, - AsyncWebSocketClient* client, - AwsEventType type, - void* arg, - uint8_t* data, - size_t len) = 0; + virtual void onWSEvent(AsyncWebSocket *server, + AsyncWebSocketClient *client, + AwsEventType type, + void *arg, + uint8_t *data, + size_t len) = 0; - String clientId(AsyncWebSocketClient* client) { - return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->id()); - } + String clientId(AsyncWebSocketClient *client) + { + return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->id()); + } - private: - void forbidden(AsyncWebServerRequest* request) { - request->send(403); - } +private: + void forbidden(AsyncWebServerRequest *request) + { + request->send(403); + } }; template -class WebSocketTx : virtual public WebSocketConnector { - public: - WebSocketTx(JsonStateReader stateReader, - StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - SecurityManager* securityManager, - AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - WebSocketConnector(statefulService, - server, - webSocketPath, - securityManager, - authenticationPredicate, - bufferSize), - _stateReader(stateReader) { - WebSocketConnector::_statefulService->addUpdateHandler( - [&](const String& originId) { transmitData(nullptr, originId); }, false); - } +class WebSocketTx : virtual public WebSocketConnector +{ +public: + WebSocketTx(JsonStateReader stateReader, + StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + SecurityManager *securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : WebSocketConnector(statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + _stateReader(stateReader) + { + WebSocketConnector::_statefulService->addUpdateHandler( + [&](const String &originId) + { transmitData(nullptr, originId); }, + false); + } - WebSocketTx(JsonStateReader stateReader, - StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - WebSocketConnector(statefulService, server, webSocketPath, bufferSize), _stateReader(stateReader) { - WebSocketConnector::_statefulService->addUpdateHandler( - [&](const String& originId) { transmitData(nullptr, originId); }, false); - } + WebSocketTx(JsonStateReader stateReader, + StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : WebSocketConnector(statefulService, server, webSocketPath, bufferSize), _stateReader(stateReader) + { + WebSocketConnector::_statefulService->addUpdateHandler( + [&](const String &originId) + { transmitData(nullptr, originId); }, + false); + } - protected: - virtual void onWSEvent(AsyncWebSocket* server, - AsyncWebSocketClient* client, - AwsEventType type, - void* arg, - uint8_t* data, - size_t len) { - if (type == WS_EVT_CONNECT) { - // when a client connects, we transmit it's id and the current payload - transmitId(client); - transmitData(client, WEB_SOCKET_ORIGIN); +protected: + virtual void onWSEvent(AsyncWebSocket *server, + AsyncWebSocketClient *client, + AwsEventType type, + void *arg, + uint8_t *data, + size_t len) + { + if (type == WS_EVT_CONNECT) + { + // when a client connects, we transmit it's id and the current payload + transmitId(client); + transmitData(client, WEB_SOCKET_ORIGIN); + } } - } - private: - JsonStateReader _stateReader; +private: + JsonStateReader _stateReader; - void transmitId(AsyncWebSocketClient* client) { - DynamicJsonDocument jsonDocument = DynamicJsonDocument(WEB_SOCKET_CLIENT_ID_MSG_SIZE); - JsonObject root = jsonDocument.to(); - root["type"] = "id"; - root["id"] = WebSocketConnector::clientId(client); - size_t len = measureJson(jsonDocument); - AsyncWebSocketMessageBuffer* buffer = WebSocketConnector::_webSocket.makeBuffer(len); - if (buffer) { - serializeJson(jsonDocument, (char*)buffer->get(), len + 1); - client->text(buffer); + void transmitId(AsyncWebSocketClient *client) + { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(WEB_SOCKET_CLIENT_ID_MSG_SIZE); + JsonObject root = jsonDocument.to(); + root["type"] = "id"; + root["id"] = WebSocketConnector::clientId(client); + size_t len = measureJson(jsonDocument); + AsyncWebSocketMessageBuffer *buffer = WebSocketConnector::_webSocket.makeBuffer(len); + if (buffer) + { + serializeJson(jsonDocument, (char *)buffer->get(), len + 1); + client->text(buffer); + } } - } - /** - * Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if - * specified. - * - * Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach - * simplifies the client and the server implementation but may not be sufficent for all use-cases. - */ - void transmitData(AsyncWebSocketClient* client, const String& originId) { - DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector::_bufferSize); - JsonObject root = jsonDocument.to(); - root["type"] = "payload"; - root["origin_id"] = originId; - JsonObject payload = root.createNestedObject("payload"); - WebSocketConnector::_statefulService->read(payload, _stateReader); + /** + * Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if + * specified. + * + * Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach + * simplifies the client and the server implementation but may not be sufficent for all use-cases. + */ + void transmitData(AsyncWebSocketClient *client, const String &originId) + { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector::_bufferSize); + JsonObject root = jsonDocument.to(); + root["type"] = "payload"; + root["origin_id"] = originId; + JsonObject payload = root.createNestedObject("payload"); + WebSocketConnector::_statefulService->read(payload, _stateReader); - size_t len = measureJson(jsonDocument); - AsyncWebSocketMessageBuffer* buffer = WebSocketConnector::_webSocket.makeBuffer(len); - if (buffer) { - serializeJson(jsonDocument, (char*)buffer->get(), len + 1); - if (client) { - client->text(buffer); - } else { - WebSocketConnector::_webSocket.textAll(buffer); - } + size_t len = measureJson(jsonDocument); + AsyncWebSocketMessageBuffer *buffer = WebSocketConnector::_webSocket.makeBuffer(len); + if (buffer) + { + serializeJson(jsonDocument, (char *)buffer->get(), len + 1); + if (client) + { + client->text(buffer); + } + else + { + WebSocketConnector::_webSocket.textAll(buffer); + } + } } - } }; template -class WebSocketRx : virtual public WebSocketConnector { - public: - WebSocketRx(JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - SecurityManager* securityManager, - AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - WebSocketConnector(statefulService, - server, - webSocketPath, - securityManager, - authenticationPredicate, - bufferSize), - _stateUpdater(stateUpdater) { - } +class WebSocketRx : virtual public WebSocketConnector +{ +public: + WebSocketRx(JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + SecurityManager *securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : WebSocketConnector(statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + _stateUpdater(stateUpdater) + { + } - WebSocketRx(JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - WebSocketConnector(statefulService, server, webSocketPath, bufferSize), _stateUpdater(stateUpdater) { - } + WebSocketRx(JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : WebSocketConnector(statefulService, server, webSocketPath, bufferSize), _stateUpdater(stateUpdater) + { + } - protected: - virtual void onWSEvent(AsyncWebSocket* server, - AsyncWebSocketClient* client, - AwsEventType type, - void* arg, - uint8_t* data, - size_t len) { - if (type == WS_EVT_DATA) { - AwsFrameInfo* info = (AwsFrameInfo*)arg; - if (info->final && info->index == 0 && info->len == len) { - if (info->opcode == WS_TEXT) { - DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector::_bufferSize); - DeserializationError error = deserializeJson(jsonDocument, (char*)data); - if (!error && jsonDocument.is()) { - JsonObject jsonObject = jsonDocument.as(); - WebSocketConnector::_statefulService->update( - jsonObject, _stateUpdater, WebSocketConnector::clientId(client)); - } +protected: + virtual void onWSEvent(AsyncWebSocket *server, + AsyncWebSocketClient *client, + AwsEventType type, + void *arg, + uint8_t *data, + size_t len) + { + if (type == WS_EVT_DATA) + { + AwsFrameInfo *info = (AwsFrameInfo *)arg; + if (info->final && info->index == 0 && info->len == len) + { + if (info->opcode == WS_TEXT) + { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector::_bufferSize); + DeserializationError error = deserializeJson(jsonDocument, (char *)data); + if (!error && jsonDocument.is()) + { + JsonObject jsonObject = jsonDocument.as(); + WebSocketConnector::_statefulService->update( + jsonObject, _stateUpdater, WebSocketConnector::clientId(client)); + } + } + } } - } } - } - private: - JsonStateUpdater _stateUpdater; +private: + JsonStateUpdater _stateUpdater; }; template -class WebSocketTxRx : public WebSocketTx, public WebSocketRx { - public: - WebSocketTxRx(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - SecurityManager* securityManager, - AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - WebSocketConnector(statefulService, - server, - webSocketPath, - securityManager, - authenticationPredicate, - bufferSize), - WebSocketTx(stateReader, - statefulService, - server, - webSocketPath, - securityManager, - authenticationPredicate, - bufferSize), - WebSocketRx(stateUpdater, - statefulService, - server, - webSocketPath, - securityManager, - authenticationPredicate, - bufferSize) { - } +class WebSocketTxRx : public WebSocketTx, public WebSocketRx +{ +public: + WebSocketTxRx(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + SecurityManager *securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : WebSocketConnector(statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + WebSocketTx(stateReader, + statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + WebSocketRx(stateUpdater, + statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize) + { + } - WebSocketTxRx(JsonStateReader stateReader, - JsonStateUpdater stateUpdater, - StatefulService* statefulService, - AsyncWebServer* server, - const char* webSocketPath, - size_t bufferSize = DEFAULT_BUFFER_SIZE) : - WebSocketConnector(statefulService, server, webSocketPath, bufferSize), - WebSocketTx(stateReader, statefulService, server, webSocketPath, bufferSize), - WebSocketRx(stateUpdater, statefulService, server, webSocketPath, bufferSize) { - } + WebSocketTxRx(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService *statefulService, + AsyncWebServer *server, + const char *webSocketPath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : WebSocketConnector(statefulService, server, webSocketPath, bufferSize), + WebSocketTx(stateReader, statefulService, server, webSocketPath, bufferSize), + WebSocketRx(stateUpdater, statefulService, server, webSocketPath, bufferSize) + { + } - protected: - void onWSEvent(AsyncWebSocket* server, - AsyncWebSocketClient* client, - AwsEventType type, - void* arg, - uint8_t* data, - size_t len) { - WebSocketRx::onWSEvent(server, client, type, arg, data, len); - WebSocketTx::onWSEvent(server, client, type, arg, data, len); - } +protected: + void onWSEvent(AsyncWebSocket *server, + AsyncWebSocketClient *client, + AwsEventType type, + void *arg, + uint8_t *data, + size_t len) + { + WebSocketRx::onWSEvent(server, client, type, arg, data, len); + WebSocketTx::onWSEvent(server, client, type, arg, data, len); + } }; #endif diff --git a/lib/framework/WiFiScanner.cpp b/lib/framework/WiFiScanner.cpp index a6f2b655..820aacce 100644 --- a/lib/framework/WiFiScanner.cpp +++ b/lib/framework/WiFiScanner.cpp @@ -1,49 +1,59 @@ #include -WiFiScanner::WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager) { - server->on(SCAN_NETWORKS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1), - AuthenticationPredicates::IS_ADMIN)); - server->on(LIST_NETWORKS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1), - AuthenticationPredicates::IS_ADMIN)); +WiFiScanner::WiFiScanner(AsyncWebServer *server, SecurityManager *securityManager) +{ + server->on(SCAN_NETWORKS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1), + AuthenticationPredicates::IS_ADMIN)); + server->on(LIST_NETWORKS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1), + AuthenticationPredicates::IS_ADMIN)); }; -void WiFiScanner::scanNetworks(AsyncWebServerRequest* request) { - if (WiFi.scanComplete() != -1) { - WiFi.scanDelete(); - WiFi.scanNetworks(true); - } - request->send(202); +void WiFiScanner::scanNetworks(AsyncWebServerRequest *request) +{ + if (WiFi.scanComplete() != -1) + { + WiFi.scanDelete(); + WiFi.scanNetworks(true); + } + request->send(202); } -void WiFiScanner::listNetworks(AsyncWebServerRequest* request) { - int numNetworks = WiFi.scanComplete(); - if (numNetworks > -1) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE); - JsonObject root = response->getRoot(); - JsonArray networks = root.createNestedArray("networks"); - for (int i = 0; i < numNetworks; i++) { - JsonObject network = networks.createNestedObject(); - network["rssi"] = WiFi.RSSI(i); - network["ssid"] = WiFi.SSID(i); - network["bssid"] = WiFi.BSSIDstr(i); - network["channel"] = WiFi.channel(i); +void WiFiScanner::listNetworks(AsyncWebServerRequest *request) +{ + int numNetworks = WiFi.scanComplete(); + if (numNetworks > -1) + { + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE); + JsonObject root = response->getRoot(); + JsonArray networks = root.createNestedArray("networks"); + for (int i = 0; i < numNetworks; i++) + { + JsonObject network = networks.createNestedObject(); + network["rssi"] = WiFi.RSSI(i); + network["ssid"] = WiFi.SSID(i); + network["bssid"] = WiFi.BSSIDstr(i); + network["channel"] = WiFi.channel(i); #ifdef ESP32 - network["encryption_type"] = (uint8_t)WiFi.encryptionType(i); + network["encryption_type"] = (uint8_t)WiFi.encryptionType(i); #elif defined(ESP8266) - network["encryption_type"] = convertEncryptionType(WiFi.encryptionType(i)); + network["encryption_type"] = convertEncryptionType(WiFi.encryptionType(i)); #endif + } + response->setLength(); + request->send(response); + } + else if (numNetworks == -1) + { + request->send(202); + } + else + { + scanNetworks(request); } - response->setLength(); - request->send(response); - } else if (numNetworks == -1) { - request->send(202); - } else { - scanNetworks(request); - } } #ifdef ESP8266 @@ -52,19 +62,21 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest* request) { * * This allows us to use a single set of mappings in the UI. */ -uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType) { - switch (encryptionType) { +uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType) +{ + switch (encryptionType) + { case ENC_TYPE_NONE: - return AUTH_OPEN; + return AUTH_OPEN; case ENC_TYPE_WEP: - return AUTH_WEP; + return AUTH_WEP; case ENC_TYPE_TKIP: - return AUTH_WPA_PSK; + return AUTH_WPA_PSK; case ENC_TYPE_CCMP: - return AUTH_WPA2_PSK; + return AUTH_WPA2_PSK; case ENC_TYPE_AUTO: - return AUTH_WPA_WPA2_PSK; - } - return -1; + return AUTH_WPA_WPA2_PSK; + } + return -1; } #endif diff --git a/lib/framework/WiFiScanner.h b/lib/framework/WiFiScanner.h index 6f910302..f7547672 100644 --- a/lib/framework/WiFiScanner.h +++ b/lib/framework/WiFiScanner.h @@ -19,17 +19,18 @@ #define MAX_WIFI_SCANNER_SIZE 1024 -class WiFiScanner { - public: - WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager); +class WiFiScanner +{ +public: + WiFiScanner(AsyncWebServer *server, SecurityManager *securityManager); - private: - void scanNetworks(AsyncWebServerRequest* request); - void listNetworks(AsyncWebServerRequest* request); +private: + void scanNetworks(AsyncWebServerRequest *request); + void listNetworks(AsyncWebServerRequest *request); #ifdef ESP8266 - uint8_t convertEncryptionType(uint8_t encryptionType); + uint8_t convertEncryptionType(uint8_t encryptionType); #endif }; -#endif // end WiFiScanner_h +#endif // end WiFiScanner_h diff --git a/lib/framework/WiFiSettingsService.cpp b/lib/framework/WiFiSettingsService.cpp index 1a6b7ad4..fca08932 100644 --- a/lib/framework/WiFiSettingsService.cpp +++ b/lib/framework/WiFiSettingsService.cpp @@ -1,100 +1,118 @@ #include -WiFiSettingsService::WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : - _httpEndpoint(WiFiSettings::read, WiFiSettings::update, this, server, WIFI_SETTINGS_SERVICE_PATH, securityManager), - _fsPersistence(WiFiSettings::read, WiFiSettings::update, this, fs, WIFI_SETTINGS_FILE), - _lastConnectionAttempt(0) { - // We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default. - // If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future. - if (WiFi.getMode() != WIFI_OFF) { - WiFi.mode(WIFI_OFF); - } +WiFiSettingsService::WiFiSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager) : _httpEndpoint(WiFiSettings::read, WiFiSettings::update, this, server, WIFI_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(WiFiSettings::read, WiFiSettings::update, this, fs, WIFI_SETTINGS_FILE), + _lastConnectionAttempt(0) +{ + // We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default. + // If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future. + if (WiFi.getMode() != WIFI_OFF) + { + WiFi.mode(WIFI_OFF); + } - // Disable WiFi config persistance and auto reconnect - WiFi.persistent(false); - WiFi.setAutoReconnect(false); + // Disable WiFi config persistance and auto reconnect + WiFi.persistent(false); + WiFi.setAutoReconnect(false); #ifdef ESP32 - // Init the wifi driver on ESP32 - WiFi.mode(WIFI_MODE_MAX); - WiFi.mode(WIFI_MODE_NULL); - WiFi.onEvent( - std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), - WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); - WiFi.onEvent(std::bind(&WiFiSettingsService::onStationModeStop, this, std::placeholders::_1, std::placeholders::_2), - WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_STOP); + // Init the wifi driver on ESP32 + WiFi.mode(WIFI_MODE_MAX); + WiFi.mode(WIFI_MODE_NULL); + WiFi.onEvent( + std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); + WiFi.onEvent(std::bind(&WiFiSettingsService::onStationModeStop, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_STOP); #elif defined(ESP8266) - _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( - std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( + std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); #endif - addUpdateHandler([&](const String& originId) { reconfigureWiFiConnection(); }, false); + addUpdateHandler([&](const String &originId) + { reconfigureWiFiConnection(); }, + false); } -void WiFiSettingsService::begin() { - _fsPersistence.readFromFS(); - reconfigureWiFiConnection(); +void WiFiSettingsService::begin() +{ + _fsPersistence.readFromFS(); + reconfigureWiFiConnection(); } -void WiFiSettingsService::reconfigureWiFiConnection() { - // reset last connection attempt to force loop to reconnect immediately - _lastConnectionAttempt = 0; +void WiFiSettingsService::reconfigureWiFiConnection() +{ + // reset last connection attempt to force loop to reconnect immediately + _lastConnectionAttempt = 0; // disconnect and de-configure wifi #ifdef ESP32 - if (WiFi.disconnect(true)) { - _stopping = true; - } + if (WiFi.disconnect(true)) + { + _stopping = true; + } #elif defined(ESP8266) - WiFi.disconnect(true); + WiFi.disconnect(true); #endif } -void WiFiSettingsService::loop() { - unsigned long currentMillis = millis(); - if (!_lastConnectionAttempt || (unsigned long)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) { - _lastConnectionAttempt = currentMillis; - manageSTA(); - } +void WiFiSettingsService::loop() +{ + unsigned long currentMillis = millis(); + if (!_lastConnectionAttempt || (unsigned long)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) + { + _lastConnectionAttempt = currentMillis; + manageSTA(); + } } -void WiFiSettingsService::manageSTA() { - // Abort if already connected, or if we have no SSID - if (WiFi.isConnected() || _state.ssid.length() == 0) { - return; - } - // Connect or reconnect as required - if ((WiFi.getMode() & WIFI_STA) == 0) { - Serial.println(F("Connecting to WiFi.")); - if (_state.staticIPConfig) { - // configure for static IP - WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); - } else { - // configure for DHCP +void WiFiSettingsService::manageSTA() +{ + // Abort if already connected, or if we have no SSID + if (WiFi.isConnected() || _state.ssid.length() == 0) + { + return; + } + // Connect or reconnect as required + if ((WiFi.getMode() & WIFI_STA) == 0) + { + Serial.println(F("Connecting to WiFi.")); + if (_state.staticIPConfig) + { + // configure for static IP + WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); + } + else + { + // configure for DHCP #ifdef ESP32 - WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); - WiFi.setHostname(_state.hostname.c_str()); + WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); + WiFi.setHostname(_state.hostname.c_str()); #elif defined(ESP8266) - WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY); - WiFi.hostname(_state.hostname); + WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY); + WiFi.hostname(_state.hostname); #endif + } + // attempt to connect to the network + WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); } - // attempt to connect to the network - WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); - } } #ifdef ESP32 -void WiFiSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { - WiFi.disconnect(true); +void WiFiSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) +{ + WiFi.disconnect(true); } -void WiFiSettingsService::onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info) { - if (_stopping) { - _lastConnectionAttempt = 0; - _stopping = false; - } +void WiFiSettingsService::onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info) +{ + if (_stopping) + { + _lastConnectionAttempt = 0; + _stopping = false; + } } #elif defined(ESP8266) -void WiFiSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { - WiFi.disconnect(true); +void WiFiSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected &event) +{ + WiFi.disconnect(true); } #endif diff --git a/lib/framework/WiFiSettingsService.h b/lib/framework/WiFiSettingsService.h index 1f5036b5..5fecc7b4 100644 --- a/lib/framework/WiFiSettingsService.h +++ b/lib/framework/WiFiSettingsService.h @@ -24,89 +24,95 @@ #define WIFI_RECONNECTION_DELAY 1000 * 30 -class WiFiSettings { - public: - // core wifi configuration - String ssid; - String password; - String hostname; - bool staticIPConfig; - - // optional configuration for static IP address - IPAddress localIP; - IPAddress gatewayIP; - IPAddress subnetMask; - IPAddress dnsIP1; - IPAddress dnsIP2; - - static void read(WiFiSettings& settings, JsonObject& root) { - // connection settings - root["ssid"] = settings.ssid; - root["password"] = settings.password; - root["hostname"] = settings.hostname; - root["static_ip_config"] = settings.staticIPConfig; - - // extended settings - JsonUtils::writeIP(root, "local_ip", settings.localIP); - JsonUtils::writeIP(root, "gateway_ip", settings.gatewayIP); - JsonUtils::writeIP(root, "subnet_mask", settings.subnetMask); - JsonUtils::writeIP(root, "dns_ip_1", settings.dnsIP1); - JsonUtils::writeIP(root, "dns_ip_2", settings.dnsIP2); - } - - static StateUpdateResult update(JsonObject& root, WiFiSettings& settings) { - settings.ssid = root["ssid"] | FACTORY_WIFI_SSID; - settings.password = root["password"] | FACTORY_WIFI_PASSWORD; - settings.hostname = root["hostname"] | SettingValue::format(FACTORY_WIFI_HOSTNAME); - settings.staticIPConfig = root["static_ip_config"] | false; - - // extended settings - JsonUtils::readIP(root, "local_ip", settings.localIP); - JsonUtils::readIP(root, "gateway_ip", settings.gatewayIP); - JsonUtils::readIP(root, "subnet_mask", settings.subnetMask); - JsonUtils::readIP(root, "dns_ip_1", settings.dnsIP1); - JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2); - - // Swap around the dns servers if 2 is populated but 1 is not - if (IPUtils::isNotSet(settings.dnsIP1) && IPUtils::isSet(settings.dnsIP2)) { - settings.dnsIP1 = settings.dnsIP2; - settings.dnsIP2 = INADDR_NONE; +class WiFiSettings +{ +public: + // core wifi configuration + String ssid; + String password; + String hostname; + bool staticIPConfig; + + // optional configuration for static IP address + IPAddress localIP; + IPAddress gatewayIP; + IPAddress subnetMask; + IPAddress dnsIP1; + IPAddress dnsIP2; + + static void read(WiFiSettings &settings, JsonObject &root) + { + // connection settings + root["ssid"] = settings.ssid; + root["password"] = settings.password; + root["hostname"] = settings.hostname; + root["static_ip_config"] = settings.staticIPConfig; + + // extended settings + JsonUtils::writeIP(root, "local_ip", settings.localIP); + JsonUtils::writeIP(root, "gateway_ip", settings.gatewayIP); + JsonUtils::writeIP(root, "subnet_mask", settings.subnetMask); + JsonUtils::writeIP(root, "dns_ip_1", settings.dnsIP1); + JsonUtils::writeIP(root, "dns_ip_2", settings.dnsIP2); } - // Turning off static ip config if we don't meet the minimum requirements - // of ipAddress, gateway and subnet. This may change to static ip only - // as sensible defaults can be assumed for gateway and subnet - if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) || - IPUtils::isNotSet(settings.subnetMask))) { - settings.staticIPConfig = false; + static StateUpdateResult update(JsonObject &root, WiFiSettings &settings) + { + settings.ssid = root["ssid"] | FACTORY_WIFI_SSID; + settings.password = root["password"] | FACTORY_WIFI_PASSWORD; + settings.hostname = root["hostname"] | SettingValue::format(FACTORY_WIFI_HOSTNAME); + settings.staticIPConfig = root["static_ip_config"] | false; + + // extended settings + JsonUtils::readIP(root, "local_ip", settings.localIP); + JsonUtils::readIP(root, "gateway_ip", settings.gatewayIP); + JsonUtils::readIP(root, "subnet_mask", settings.subnetMask); + JsonUtils::readIP(root, "dns_ip_1", settings.dnsIP1); + JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2); + + // Swap around the dns servers if 2 is populated but 1 is not + if (IPUtils::isNotSet(settings.dnsIP1) && IPUtils::isSet(settings.dnsIP2)) + { + settings.dnsIP1 = settings.dnsIP2; + settings.dnsIP2 = INADDR_NONE; + } + + // Turning off static ip config if we don't meet the minimum requirements + // of ipAddress, gateway and subnet. This may change to static ip only + // as sensible defaults can be assumed for gateway and subnet + if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) || + IPUtils::isNotSet(settings.subnetMask))) + { + settings.staticIPConfig = false; + } + return StateUpdateResult::CHANGED; } - return StateUpdateResult::CHANGED; - } }; -class WiFiSettingsService : public StatefulService { - public: - WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); +class WiFiSettingsService : public StatefulService +{ +public: + WiFiSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager); - void begin(); - void loop(); + void begin(); + void loop(); - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; - unsigned long _lastConnectionAttempt; +private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + unsigned long _lastConnectionAttempt; #ifdef ESP32 - bool _stopping; - void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); - void onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info); + bool _stopping; + void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info); #elif defined(ESP8266) - WiFiEventHandler _onStationModeDisconnectedHandler; - void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); + WiFiEventHandler _onStationModeDisconnectedHandler; + void onStationModeDisconnected(const WiFiEventStationModeDisconnected &event); #endif - void reconfigureWiFiConnection(); - void manageSTA(); + void reconfigureWiFiConnection(); + void manageSTA(); }; -#endif // end WiFiSettingsService_h +#endif // end WiFiSettingsService_h diff --git a/lib/framework/WiFiStatus.cpp b/lib/framework/WiFiStatus.cpp index e4f8852d..8ec8f16c 100644 --- a/lib/framework/WiFiStatus.cpp +++ b/lib/framework/WiFiStatus.cpp @@ -1,75 +1,86 @@ #include -WiFiStatus::WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager) { - server->on(WIFI_STATUS_SERVICE_PATH, - HTTP_GET, - securityManager->wrapRequest(std::bind(&WiFiStatus::wifiStatus, this, std::placeholders::_1), - AuthenticationPredicates::IS_AUTHENTICATED)); +WiFiStatus::WiFiStatus(AsyncWebServer *server, SecurityManager *securityManager) +{ + server->on(WIFI_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&WiFiStatus::wifiStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); #ifdef ESP32 - WiFi.onEvent(onStationModeConnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED); - WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); - WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); + WiFi.onEvent(onStationModeConnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED); + WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); + WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); #elif defined(ESP8266) - _onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected); - _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected); - _onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP); + _onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected); + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected); + _onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP); #endif } #ifdef ESP32 -void WiFiStatus::onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.println(F("WiFi Connected.")); +void WiFiStatus::onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info) +{ + Serial.println(F("WiFi Connected.")); } -void WiFiStatus::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.print(F("WiFi Disconnected. Reason code=")); - Serial.println(info.wifi_sta_disconnected.reason); +void WiFiStatus::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) +{ + Serial.print(F("WiFi Disconnected. Reason code=")); + Serial.println(info.wifi_sta_disconnected.reason); } -void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.printf_P( - PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), WiFi.localIP().toString().c_str(), WiFi.getHostname()); +void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) +{ + Serial.printf_P( + PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), WiFi.localIP().toString().c_str(), WiFi.getHostname()); } #elif defined(ESP8266) -void WiFiStatus::onStationModeConnected(const WiFiEventStationModeConnected& event) { - Serial.print(F("WiFi Connected. SSID=")); - Serial.println(event.ssid); +void WiFiStatus::onStationModeConnected(const WiFiEventStationModeConnected &event) +{ + Serial.print(F("WiFi Connected. SSID=")); + Serial.println(event.ssid); } -void WiFiStatus::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { - Serial.print(F("WiFi Disconnected. Reason code=")); - Serial.println(event.reason); +void WiFiStatus::onStationModeDisconnected(const WiFiEventStationModeDisconnected &event) +{ + Serial.print(F("WiFi Disconnected. Reason code=")); + Serial.println(event.reason); } -void WiFiStatus::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { - Serial.printf_P( - PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), event.ip.toString().c_str(), WiFi.hostname().c_str()); +void WiFiStatus::onStationModeGotIP(const WiFiEventStationModeGotIP &event) +{ + Serial.printf_P( + PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), event.ip.toString().c_str(), WiFi.hostname().c_str()); } #endif -void WiFiStatus::wifiStatus(AsyncWebServerRequest* request) { - AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_STATUS_SIZE); - JsonObject root = response->getRoot(); - wl_status_t status = WiFi.status(); - root["status"] = (uint8_t)status; - if (status == WL_CONNECTED) { - root["local_ip"] = WiFi.localIP().toString(); - root["mac_address"] = WiFi.macAddress(); - root["rssi"] = WiFi.RSSI(); - root["ssid"] = WiFi.SSID(); - root["bssid"] = WiFi.BSSIDstr(); - root["channel"] = WiFi.channel(); - root["subnet_mask"] = WiFi.subnetMask().toString(); - root["gateway_ip"] = WiFi.gatewayIP().toString(); - IPAddress dnsIP1 = WiFi.dnsIP(0); - IPAddress dnsIP2 = WiFi.dnsIP(1); - if (IPUtils::isSet(dnsIP1)) { - root["dns_ip_1"] = dnsIP1.toString(); +void WiFiStatus::wifiStatus(AsyncWebServerRequest *request) +{ + AsyncJsonResponse *response = new AsyncJsonResponse(false, MAX_WIFI_STATUS_SIZE); + JsonObject root = response->getRoot(); + wl_status_t status = WiFi.status(); + root["status"] = (uint8_t)status; + if (status == WL_CONNECTED) + { + root["local_ip"] = WiFi.localIP().toString(); + root["mac_address"] = WiFi.macAddress(); + root["rssi"] = WiFi.RSSI(); + root["ssid"] = WiFi.SSID(); + root["bssid"] = WiFi.BSSIDstr(); + root["channel"] = WiFi.channel(); + root["subnet_mask"] = WiFi.subnetMask().toString(); + root["gateway_ip"] = WiFi.gatewayIP().toString(); + IPAddress dnsIP1 = WiFi.dnsIP(0); + IPAddress dnsIP2 = WiFi.dnsIP(1); + if (IPUtils::isSet(dnsIP1)) + { + root["dns_ip_1"] = dnsIP1.toString(); + } + if (IPUtils::isSet(dnsIP2)) + { + root["dns_ip_2"] = dnsIP2.toString(); + } } - if (IPUtils::isSet(dnsIP2)) { - root["dns_ip_2"] = dnsIP2.toString(); - } - } - response->setLength(); - request->send(response); + response->setLength(); + request->send(response); } diff --git a/lib/framework/WiFiStatus.h b/lib/framework/WiFiStatus.h index 213c99bd..63f03188 100644 --- a/lib/framework/WiFiStatus.h +++ b/lib/framework/WiFiStatus.h @@ -18,28 +18,29 @@ #define MAX_WIFI_STATUS_SIZE 1024 #define WIFI_STATUS_SERVICE_PATH "/rest/wifiStatus" -class WiFiStatus { - public: - WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager); +class WiFiStatus +{ +public: + WiFiStatus(AsyncWebServer *server, SecurityManager *securityManager); - private: +private: #ifdef ESP32 - // static functions for logging WiFi events to the UART - static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info); - static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); - static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); + // static functions for logging WiFi events to the UART + static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info); + static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); + static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); #elif defined(ESP8266) - // handler refrences for logging important WiFi events over serial - WiFiEventHandler _onStationModeConnectedHandler; - WiFiEventHandler _onStationModeDisconnectedHandler; - WiFiEventHandler _onStationModeGotIPHandler; - // static functions for logging WiFi events to the UART - static void onStationModeConnected(const WiFiEventStationModeConnected& event); - static void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); - static void onStationModeGotIP(const WiFiEventStationModeGotIP& event); + // handler refrences for logging important WiFi events over serial + WiFiEventHandler _onStationModeConnectedHandler; + WiFiEventHandler _onStationModeDisconnectedHandler; + WiFiEventHandler _onStationModeGotIPHandler; + // static functions for logging WiFi events to the UART + static void onStationModeConnected(const WiFiEventStationModeConnected &event); + static void onStationModeDisconnected(const WiFiEventStationModeDisconnected &event); + static void onStationModeGotIP(const WiFiEventStationModeGotIP &event); #endif - void wifiStatus(AsyncWebServerRequest* request); + void wifiStatus(AsyncWebServerRequest *request); }; -#endif // end WiFiStatus_h +#endif // end WiFiStatus_h diff --git a/platformio.ini b/platformio.ini index 9eb8422e..dcba3853 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ [platformio] description = ESP32 Sveltekit Template -data_dir = c:\Users\elims\Documents\Svelte\ESP32-sveltekit\data +data_dir = /data extra_configs = factory_settings.ini features.ini @@ -52,7 +52,6 @@ lib_deps = ArduinoJson@>=6.0.0,<7.0.0 esphome/ESPAsyncWebServer-esphome @ ^3.0.0 esphome/AsyncTCP-esphome @ ^2.0.0 -; AsyncMqttClient@>=0.9.0,<1.0.0 ottowinter/AsyncMqttClient-esphome @ ^0.8.6 [env:adafruit_feather_esp32_v2] diff --git a/src/LightMqttSettingsService.cpp b/src/LightMqttSettingsService.cpp index bddd9098..88ccec76 100644 --- a/src/LightMqttSettingsService.cpp +++ b/src/LightMqttSettingsService.cpp @@ -1,16 +1,17 @@ #include -LightMqttSettingsService::LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : - _httpEndpoint(LightMqttSettings::read, - LightMqttSettings::update, - this, - server, - LIGHT_BROKER_SETTINGS_PATH, - securityManager, - AuthenticationPredicates::IS_AUTHENTICATED), - _fsPersistence(LightMqttSettings::read, LightMqttSettings::update, this, fs, LIGHT_BROKER_SETTINGS_FILE) { +LightMqttSettingsService::LightMqttSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager) : _httpEndpoint(LightMqttSettings::read, + LightMqttSettings::update, + this, + server, + LIGHT_BROKER_SETTINGS_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _fsPersistence(LightMqttSettings::read, LightMqttSettings::update, this, fs, LIGHT_BROKER_SETTINGS_FILE) +{ } -void LightMqttSettingsService::begin() { - _fsPersistence.readFromFS(); +void LightMqttSettingsService::begin() +{ + _fsPersistence.readFromFS(); } diff --git a/src/LightMqttSettingsService.h b/src/LightMqttSettingsService.h index e7b66b09..81763148 100644 --- a/src/LightMqttSettingsService.h +++ b/src/LightMqttSettingsService.h @@ -8,34 +8,38 @@ #define LIGHT_BROKER_SETTINGS_FILE "/config/brokerSettings.json" #define LIGHT_BROKER_SETTINGS_PATH "/rest/brokerSettings" -class LightMqttSettings { - public: - String mqttPath; - String name; - String uniqueId; - - static void read(LightMqttSettings& settings, JsonObject& root) { - root["mqtt_path"] = settings.mqttPath; - root["name"] = settings.name; - root["unique_id"] = settings.uniqueId; - } - - static StateUpdateResult update(JsonObject& root, LightMqttSettings& settings) { - settings.mqttPath = root["mqtt_path"] | SettingValue::format("homeassistant/light/#{unique_id}"); - settings.name = root["name"] | SettingValue::format("light-#{unique_id}"); - settings.uniqueId = root["unique_id"] | SettingValue::format("light-#{unique_id}"); - return StateUpdateResult::CHANGED; - } +class LightMqttSettings +{ +public: + String mqttPath; + String name; + String uniqueId; + + static void read(LightMqttSettings &settings, JsonObject &root) + { + root["mqtt_path"] = settings.mqttPath; + root["name"] = settings.name; + root["unique_id"] = settings.uniqueId; + } + + static StateUpdateResult update(JsonObject &root, LightMqttSettings &settings) + { + settings.mqttPath = root["mqtt_path"] | SettingValue::format("homeassistant/light/#{unique_id}"); + settings.name = root["name"] | SettingValue::format("light-#{unique_id}"); + settings.uniqueId = root["unique_id"] | SettingValue::format("light-#{unique_id}"); + return StateUpdateResult::CHANGED; + } }; -class LightMqttSettingsService : public StatefulService { - public: - LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); - void begin(); +class LightMqttSettingsService : public StatefulService +{ +public: + LightMqttSettingsService(AsyncWebServer *server, FS *fs, SecurityManager *securityManager); + void begin(); - private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; +private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; }; -#endif // end LightMqttSettingsService_h +#endif // end LightMqttSettingsService_h diff --git a/src/LightStateService.cpp b/src/LightStateService.cpp index 81696222..69dd792e 100644 --- a/src/LightStateService.cpp +++ b/src/LightStateService.cpp @@ -1,73 +1,81 @@ #include -LightStateService::LightStateService(AsyncWebServer* server, - SecurityManager* securityManager, - AsyncMqttClient* mqttClient, - LightMqttSettingsService* lightMqttSettingsService) : - _httpEndpoint(LightState::read, - LightState::update, - this, - server, - LIGHT_SETTINGS_ENDPOINT_PATH, - securityManager, - AuthenticationPredicates::IS_AUTHENTICATED), - _mqttPubSub(LightState::haRead, LightState::haUpdate, this, mqttClient), - _webSocket(LightState::read, - LightState::update, - this, - server, - LIGHT_SETTINGS_SOCKET_PATH, - securityManager, - AuthenticationPredicates::IS_AUTHENTICATED), - _mqttClient(mqttClient), - _lightMqttSettingsService(lightMqttSettingsService) { - // configure led to be output - pinMode(LED_PIN, OUTPUT); +LightStateService::LightStateService(AsyncWebServer *server, + SecurityManager *securityManager, + AsyncMqttClient *mqttClient, + LightMqttSettingsService *lightMqttSettingsService) : _httpEndpoint(LightState::read, + LightState::update, + this, + server, + LIGHT_SETTINGS_ENDPOINT_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _mqttPubSub(LightState::haRead, LightState::haUpdate, this, mqttClient), + _webSocket(LightState::read, + LightState::update, + this, + server, + LIGHT_SETTINGS_SOCKET_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _mqttClient(mqttClient), + _lightMqttSettingsService(lightMqttSettingsService) +{ + // configure led to be output + pinMode(LED_PIN, OUTPUT); - // configure MQTT callback - _mqttClient->onConnect(std::bind(&LightStateService::registerConfig, this)); + // configure MQTT callback + _mqttClient->onConnect(std::bind(&LightStateService::registerConfig, this)); - // configure update handler for when the light settings change - _lightMqttSettingsService->addUpdateHandler([&](const String& originId) { registerConfig(); }, false); + // configure update handler for when the light settings change + _lightMqttSettingsService->addUpdateHandler([&](const String &originId) + { registerConfig(); }, + false); - // configure settings service update handler to update LED state - addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); + // configure settings service update handler to update LED state + addUpdateHandler([&](const String &originId) + { onConfigUpdated(); }, + false); } -void LightStateService::begin() { - _state.ledOn = DEFAULT_LED_STATE; - onConfigUpdated(); +void LightStateService::begin() +{ + _state.ledOn = DEFAULT_LED_STATE; + onConfigUpdated(); } -void LightStateService::onConfigUpdated() { - digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF); +void LightStateService::onConfigUpdated() +{ + digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF); } -void LightStateService::registerConfig() { - if (!_mqttClient->connected()) { - return; - } - String configTopic; - String subTopic; - String pubTopic; +void LightStateService::registerConfig() +{ + if (!_mqttClient->connected()) + { + return; + } + String configTopic; + String subTopic; + String pubTopic; - DynamicJsonDocument doc(256); - _lightMqttSettingsService->read([&](LightMqttSettings& settings) { + DynamicJsonDocument doc(256); + _lightMqttSettingsService->read([&](LightMqttSettings &settings) + { configTopic = settings.mqttPath + "/config"; subTopic = settings.mqttPath + "/set"; pubTopic = settings.mqttPath + "/state"; doc["~"] = settings.mqttPath; doc["name"] = settings.name; - doc["unique_id"] = settings.uniqueId; - }); - doc["cmd_t"] = "~/set"; - doc["stat_t"] = "~/state"; - doc["schema"] = "json"; - doc["brightness"] = false; + doc["unique_id"] = settings.uniqueId; }); + doc["cmd_t"] = "~/set"; + doc["stat_t"] = "~/state"; + doc["schema"] = "json"; + doc["brightness"] = false; - String payload; - serializeJson(doc, payload); - _mqttClient->publish(configTopic.c_str(), 0, false, payload.c_str()); + String payload; + serializeJson(doc, payload); + _mqttClient->publish(configTopic.c_str(), 0, false, payload.c_str()); - _mqttPubSub.configureTopics(pubTopic, subTopic); + _mqttPubSub.configureTopics(pubTopic, subTopic); } diff --git a/src/LightStateService.h b/src/LightStateService.h index f2aab6c7..ed0d35b9 100644 --- a/src/LightStateService.h +++ b/src/LightStateService.h @@ -26,62 +26,73 @@ #define LIGHT_SETTINGS_ENDPOINT_PATH "/rest/lightState" #define LIGHT_SETTINGS_SOCKET_PATH "/ws/lightState" -class LightState { - public: - bool ledOn; +class LightState +{ +public: + bool ledOn; - static void read(LightState& settings, JsonObject& root) { - root["led_on"] = settings.ledOn; - } - - static StateUpdateResult update(JsonObject& root, LightState& lightState) { - boolean newState = root["led_on"] | DEFAULT_LED_STATE; - if (lightState.ledOn != newState) { - lightState.ledOn = newState; - return StateUpdateResult::CHANGED; + static void read(LightState &settings, JsonObject &root) + { + root["led_on"] = settings.ledOn; } - return StateUpdateResult::UNCHANGED; - } - static void haRead(LightState& settings, JsonObject& root) { - root["state"] = settings.ledOn ? ON_STATE : OFF_STATE; - } + static StateUpdateResult update(JsonObject &root, LightState &lightState) + { + boolean newState = root["led_on"] | DEFAULT_LED_STATE; + if (lightState.ledOn != newState) + { + lightState.ledOn = newState; + return StateUpdateResult::CHANGED; + } + return StateUpdateResult::UNCHANGED; + } - static StateUpdateResult haUpdate(JsonObject& root, LightState& lightState) { - String state = root["state"]; - // parse new led state - boolean newState = false; - if (state.equals(ON_STATE)) { - newState = true; - } else if (!state.equals(OFF_STATE)) { - return StateUpdateResult::ERROR; + static void haRead(LightState &settings, JsonObject &root) + { + root["state"] = settings.ledOn ? ON_STATE : OFF_STATE; } - // change the new state, if required - if (lightState.ledOn != newState) { - lightState.ledOn = newState; - return StateUpdateResult::CHANGED; + + static StateUpdateResult haUpdate(JsonObject &root, LightState &lightState) + { + String state = root["state"]; + // parse new led state + boolean newState = false; + if (state.equals(ON_STATE)) + { + newState = true; + } + else if (!state.equals(OFF_STATE)) + { + return StateUpdateResult::ERROR; + } + // change the new state, if required + if (lightState.ledOn != newState) + { + lightState.ledOn = newState; + return StateUpdateResult::CHANGED; + } + return StateUpdateResult::UNCHANGED; } - return StateUpdateResult::UNCHANGED; - } }; -class LightStateService : public StatefulService { - public: - LightStateService(AsyncWebServer* server, - SecurityManager* securityManager, - AsyncMqttClient* mqttClient, - LightMqttSettingsService* lightMqttSettingsService); - void begin(); +class LightStateService : public StatefulService +{ +public: + LightStateService(AsyncWebServer *server, + SecurityManager *securityManager, + AsyncMqttClient *mqttClient, + LightMqttSettingsService *lightMqttSettingsService); + void begin(); - private: - HttpEndpoint _httpEndpoint; - MqttPubSub _mqttPubSub; - WebSocketTxRx _webSocket; - AsyncMqttClient* _mqttClient; - LightMqttSettingsService* _lightMqttSettingsService; +private: + HttpEndpoint _httpEndpoint; + MqttPubSub _mqttPubSub; + WebSocketTxRx _webSocket; + AsyncMqttClient *_mqttClient; + LightMqttSettingsService *_lightMqttSettingsService; - void registerConfig(); - void onConfigUpdated(); + void registerConfig(); + void onConfigUpdated(); }; #endif diff --git a/src/main.cpp b/src/main.cpp index 0b1081a4..4caf6c99 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,24 +13,26 @@ LightStateService lightStateService = LightStateService(&server, esp8266React.getMqttClient(), &lightMqttSettingsService); -void setup() { - // start serial and filesystem - Serial.begin(SERIAL_BAUD_RATE); +void setup() +{ + // start serial and filesystem + Serial.begin(SERIAL_BAUD_RATE); - // start the framework and demo project - esp8266React.begin(); + // start the framework and demo project + esp8266React.begin(); - // load the initial light settings - lightStateService.begin(); + // load the initial light settings + lightStateService.begin(); - // start the light service - lightMqttSettingsService.begin(); + // start the light service + lightMqttSettingsService.begin(); - // start the server - server.begin(); + // start the server + server.begin(); } -void loop() { - // run the framework's loop function - esp8266React.loop(); +void loop() +{ + // run the framework's loop function + esp8266React.loop(); }