From 06e96b5ea7f54598c6d1cd2eff10fbece14b22e4 Mon Sep 17 00:00:00 2001 From: iranl Date: Wed, 9 Oct 2024 21:33:18 +0200 Subject: [PATCH] WiFi portal --- README.md | 6 +- platformio.ini | 1 + src/Config.h | 2 +- src/NukiNetwork.cpp | 42 +- src/NukiNetwork.h | 3 +- src/NukiOpenerWrapper.cpp | 46 +- src/NukiWrapper.cpp | 69 ++- src/PreferencesKeys.h | 33 +- src/RestartReason.h | 3 - src/WebCfgServer.cpp | 692 ++++++++++++++++---------- src/WebCfgServer.h | 19 +- src/main.cpp | 41 +- src/networkDevices/EthernetDevice.cpp | 37 +- src/networkDevices/EthernetDevice.h | 8 +- src/networkDevices/NetworkDevice.h | 10 +- src/networkDevices/WifiDevice.cpp | 457 +++++++++++++---- src/networkDevices/WifiDevice.h | 22 +- updater/platformio.ini | 5 +- 18 files changed, 999 insertions(+), 497 deletions(-) diff --git a/README.md b/README.md index 483d664..421e18f 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,8 @@ Unpack the zip archive and read the included how-to-flash.txt for installation i ## Initial setup (Network and MQTT) -Power up the ESP32 and a new Wi-Fi access point named "ESP32_(8 CHARACTER ALPHANUMERIC)" should appear.
+Power up the ESP32 and a new Wi-Fi access point named "NukiHub" should appear.
+The password of the access point is "NukiHubESP32".
Connect a client device to this access point and in a browser navigate to "http://192.168.4.1".
Use the web interface to connect the ESP to your preferred Wi-Fi network.

@@ -138,7 +139,7 @@ PSRAM is usually 2, 4 or 8MB in size and thus greatly enlarges the 320kb of inte It is basically impossible to run out of RAM when PSRAM is available. You can check on the info page of the Web configurator if PSRAM is available. -Note that there are two build of Nuki Hub for the ESP32-S3 available.
+Note that there are two builds of Nuki Hub for the ESP32-S3 available.
One for devices with no or Quad SPI PSRAM and one for devices with Octal SPI PSRAM.
If your ESP32-S3 device has PSRAM but it is not detected please flash the other S3 binary. @@ -165,7 +166,6 @@ In a browser navigate to the IP address assigned to the ESP32. - MQTT SSL Client Certificate: Optionally set to the Client SSL certificate of the MQTT broker, see the "[MQTT Encryption](#mqtt-encryption-optional)" section of this README. - MQTT SSL Client Key: Optionally set to the Client SSL key of the MQTT broker, see the "[MQTT Encryption](#mqtt-encryption-optional)" section of this README. - Network hardware: "Wi-Fi only" by default, set to one of the specified ethernet modules if available, see the "Supported Ethernet devices" and "[Connecting via Ethernet](#connecting-via-ethernet-optional)" section of this README. -- Disable fallback to Wi-Fi / Wi-Fi config portal: By default the Nuki Hub will fallback to Wi-Fi and open the Wi-Fi configuration portal when the network connection fails. Enable this setting to disable this fallback. - Connect to AP with the best signal in an environment with multiple APs with the same SSID: Enable to perform a scan for the Access Point with the best signal strenght for the specified SSID in a multi AP/Mesh environment. - RSSI Publish interval: Set to a positive integer to set the amount of seconds between updates to the maintenance/wifiRssi MQTT topic with the current Wi-Fi RSSI, set to -1 to disable, default 60. - MQTT Timeout until restart: Set to a positive integer to restart the Nuki Hub after the set amount of seconds has passed without an active connection to the MQTT broker, set to -1 to disable, default 60. diff --git a/platformio.ini b/platformio.ini index a4c1aa0..a357b42 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,6 +27,7 @@ board_build.partitions = partitions.csv build_unflags = -DCONFIG_BT_NIMBLE_LOG_LEVEL -DCONFIG_BTDM_BLE_SCAN_DUPL + -DESP32 -Werror=all -Wall build_flags = diff --git a/src/Config.h b/src/Config.h index e56b8ef..6c6c438 100644 --- a/src/Config.h +++ b/src/Config.h @@ -4,7 +4,7 @@ #define NUKI_HUB_VERSION "9.01" #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2024-09-06" +#define NUKI_HUB_DATE "2024-10-14" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index f564089..e1d33a5 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -69,7 +69,7 @@ void NukiNetwork::setupDevice() { _ipConfiguration = new IPConfiguration(_preferences); int hardwareDetect = _preferences->getInt(preference_network_hardware, 0); - Log->print(F("Hardware detect : ")); + Log->print(F("Hardware detect: ")); Log->println(hardwareDetect); _firstBootAfterDeviceChange = _preferences->getBool(preference_ntw_reconfigure, false); @@ -95,7 +95,7 @@ void NukiNetwork::setupDevice() if(strcmp(WiFi_fallbackDetect, "wifi_fallback") == 0) { #ifndef CONFIG_IDF_TARGET_ESP32H2 - if(_preferences->getBool(preference_network_wifi_fallback_disabled) && !_firstBootAfterDeviceChange) + if(!_firstBootAfterDeviceChange) { Log->println(F("Failed to connect to network. Wi-Fi fallback is disabled, rebooting.")); memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect)); @@ -122,7 +122,7 @@ void NukiNetwork::setupDevice() _device = NetworkDeviceInstantiator::Create(_networkDeviceType, _hostname, _preferences, _ipConfiguration); Log->print(F("Network device: ")); - Log->print(_device->deviceName()); + Log->println(_device->deviceName()); } void NukiNetwork::reconfigureDevice() @@ -130,6 +130,16 @@ void NukiNetwork::reconfigureDevice() _device->reconfigure(); } +void NukiNetwork::scan(bool passive, bool async) +{ + _device->scan(passive, async); +} + +bool NukiNetwork::isApOpen() +{ + return _device->isApOpen(); +} + const String NukiNetwork::networkDeviceName() const { return _device->deviceName(); @@ -317,7 +327,6 @@ void NukiNetwork::readSettings() { _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect, false); _checkUpdates = _preferences->getBool(preference_check_updates, false); - _reconnectNetworkOnMqttDisconnect = _preferences->getBool(preference_recon_netw_on_mqtt_discon, false); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval, 0) * 1000; if(_rssiPublishInterval == 0) @@ -341,38 +350,17 @@ bool NukiNetwork::update() int64_t ts = (esp_timer_get_time() / 1000); _device->update(); - if(!_mqttEnabled) + if(!_mqttEnabled || _device->isApOpen()) { return true; } - if(!_device->isConnected() || (_mqttConnectCounter > 15 && _reconnectNetworkOnMqttDisconnect && !_firstConnect)) + if(!_device->isConnected() || (_mqttConnectCounter > 15 && !_firstConnect)) { _mqttConnectCounter = 0; if(!_webEnabled) forceEnableWebServer = true; if(_restartOnDisconnect && (esp_timer_get_time() / 1000) > 60000) restartEsp(RestartReason::RestartOnDisconnectWatchdog); - - Log->println(F("Network not connected. Trying reconnect.")); - ReconnectStatus reconnectStatus = _device->reconnect(true); - - switch(reconnectStatus) - { - case ReconnectStatus::CriticalFailure: - strcpy(WiFi_fallbackDetect, "wifi_fallback"); - Log->println("Network device has a critical failure, enable fallback to Wi-Fi and reboot."); - delay(200); - restartEsp(RestartReason::NetworkDeviceCriticalFailure); - break; - case ReconnectStatus::Success: - memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect)); - Log->print(F("Reconnect successful: IP: ")); - Log->println(_device->localIP()); - break; - case ReconnectStatus::Failure: - Log->println(F("Reconnect failed")); - break; - } } if(_device->isConnected() && !_mqttClientInitiated && strcmp(_mqttBrokerAddr, "") != 0) diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index fad16f3..602d74b 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -24,6 +24,8 @@ public: void readSettings(); bool update(); void reconfigureDevice(); + void scan(bool passive = false, bool async = true); + bool isApOpen(); void clearWifiFallback(); const String networkDeviceName() const; @@ -168,7 +170,6 @@ private: std::vector _mqttReceivers; bool _restartOnDisconnect = false; bool _checkUpdates = false; - bool _reconnectNetworkOnMqttDisconnect = false; bool _firstConnect = true; bool _publishDebugInfo = false; bool _logIp = true; diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index 3ae17a7..c78e0ed 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -1772,21 +1772,21 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value) String allowedFromTime; String allowedUntilTime; - if(json.containsKey("code")) code = json["code"].as(); + if(json["code"].is()) code = json["code"].as(); else code = 12; - if(json.containsKey("enabled")) enabled = json["enabled"].as(); + if(json["enabled"].is()) enabled = json["enabled"].as(); else enabled = 2; - if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as(); + if(json["timeLimited"].is()) timeLimited = json["timeLimited"].as(); else timeLimited = 2; - if(json.containsKey("name")) name = json["name"].as(); - if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as(); - if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as(); - if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as(); - if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as(); - if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as(); + if(json["name"].is()) name = json["name"].as(); + if(json["allowedFrom"].is()) allowedFrom = json["allowedFrom"].as(); + if(json["allowedUntil"].is()) allowedUntil = json["allowedUntil"].as(); + if(json["allowedWeekdays"].is()) allowedWeekdays = json["allowedWeekdays"].as(); + if(json["allowedFromTime"].is()) allowedFromTime = json["allowedFromTime"].as(); + if(json["allowedUntilTime"].is()) allowedUntilTime = json["allowedUntilTime"].as(); if(action) { @@ -2209,12 +2209,12 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value) String lockAction; NukiOpener::LockAction timeControlLockAction; - if(json.containsKey("enabled")) enabled = json["enabled"].as(); + if(json["enabled"].is()) enabled = json["enabled"].as(); else enabled = 2; - if(json.containsKey("weekdays")) weekdays = json["weekdays"].as(); - if(json.containsKey("time")) time = json["time"].as(); - if(json.containsKey("lockAction")) lockAction = json["lockAction"].as(); + if(json["weekdays"].is()) weekdays = json["weekdays"].as(); + if(json["time"].is()) time = json["time"].as(); + if(json["lockAction"].is()) lockAction = json["lockAction"].as(); if(lockAction.length() > 0) { @@ -2443,22 +2443,22 @@ void NukiOpenerWrapper::onAuthCommandReceived(const char *value) String allowedFromTime; String allowedUntilTime; - if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as(); + if(json["remoteAllowed"].is()) remoteAllowed = json["remoteAllowed"].as(); else remoteAllowed = 2; - if(json.containsKey("enabled")) enabled = json["enabled"].as(); + if(json["enabled"].is()) enabled = json["enabled"].as(); else enabled = 2; - if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as(); + if(json["timeLimited"].is()) timeLimited = json["timeLimited"].as(); else timeLimited = 2; - if(json.containsKey("name")) name = json["name"].as(); - //if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as(); - if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as(); - if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as(); - if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as(); - if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as(); - if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as(); + if(json["name"].is()) name = json["name"].as(); + //if(json["sharedKey"].is()) sharedKey = json["sharedKey"].as(); + if(json["allowedFrom"].is()) allowedFrom = json["allowedFrom"].as(); + if(json["allowedUntil"].is()) allowedUntil = json["allowedUntil"].as(); + if(json["allowedWeekdays"].is()) allowedWeekdays = json["allowedWeekdays"].as(); + if(json["allowedFromTime"].is()) allowedFromTime = json["allowedFromTime"].as(); + if(json["allowedUntilTime"].is()) allowedUntilTime = json["allowedUntilTime"].as(); if(action) { diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 7cc1092..8ed2900 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -1,3 +1,6 @@ +#ifndef CONFIG_IDF_TARGET_ESP32H2 +#include "esp_wifi.h" +#endif #include "NukiWrapper.h" #include "PreferencesKeys.h" #include "MqttTopics.h" @@ -58,8 +61,24 @@ void NukiWrapper::initialize(const bool& firstStart) if(firstStart) { Log->println("First start, setting preference defaults"); - _preferences->putBool(preference_network_wifi_fallback_disabled, false); - _preferences->putBool(preference_find_best_rssi, false); + + #ifndef CONFIG_IDF_TARGET_ESP32H2 + wifi_config_t wifi_cfg; + if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to get Wi-Fi configuration in RAM"); + } + + if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) { + Log->println("Failed to set storage Wi-Fi"); + } + + memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); + memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); + + if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to clear NVS Wi-Fi configuration"); + } + #endif _preferences->putBool(preference_check_updates, true); _preferences->putBool(preference_opener_continuous_mode, false); _preferences->putBool(preference_official_hybrid_enabled, false); @@ -1894,21 +1913,21 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value) String allowedFromTime; String allowedUntilTime; - if(json.containsKey("code")) code = json["code"].as(); + if(json["code"].is()) code = json["code"].as(); else code = 12; - if(json.containsKey("enabled")) enabled = json["enabled"].as(); + if(json["enabled"].is()) enabled = json["enabled"].as(); else enabled = 2; - if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as(); + if(json["timeLimited"].is()) timeLimited = json["timeLimited"].as(); else timeLimited = 2; - if(json.containsKey("name")) name = json["name"].as(); - if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as(); - if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as(); - if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as(); - if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as(); - if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as(); + if(json["name"].is()) name = json["name"].as(); + if(json["allowedFrom"].is()) allowedFrom = json["allowedFrom"].as(); + if(json["allowedUntil"].is()) allowedUntil = json["allowedUntil"].as(); + if(json["allowedWeekdays"].is()) allowedWeekdays = json["allowedWeekdays"].as(); + if(json["allowedFromTime"].is()) allowedFromTime = json["allowedFromTime"].as(); + if(json["allowedUntilTime"].is()) allowedUntilTime = json["allowedUntilTime"].as(); if(action) { @@ -2331,12 +2350,12 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value) String lockAction; NukiLock::LockAction timeControlLockAction; - if(json.containsKey("enabled")) enabled = json["enabled"].as(); + if(json["enabled"].is()) enabled = json["enabled"].as(); else enabled = 2; - if(json.containsKey("weekdays")) weekdays = json["weekdays"].as(); - if(json.containsKey("time")) time = json["time"].as(); - if(json.containsKey("lockAction")) lockAction = json["lockAction"].as(); + if(json["weekdays"].is()) weekdays = json["weekdays"].as(); + if(json["time"].is()) time = json["time"].as(); + if(json["lockAction"].is()) lockAction = json["lockAction"].as(); if(lockAction.length() > 0) { @@ -2567,22 +2586,22 @@ void NukiWrapper::onAuthCommandReceived(const char *value) String allowedFromTime; String allowedUntilTime; - if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as(); + if(json["remoteAllowed"].is()) remoteAllowed = json["remoteAllowed"].as(); else remoteAllowed = 2; - if(json.containsKey("enabled")) enabled = json["enabled"].as(); + if(json["enabled"].is()) enabled = json["enabled"].as(); else enabled = 2; - if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as(); + if(json["timeLimited"].is()) timeLimited = json["timeLimited"].as(); else timeLimited = 2; - if(json.containsKey("name")) name = json["name"].as(); - //if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as(); - if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as(); - if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as(); - if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as(); - if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as(); - if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as(); + if(json["name"].is()) name = json["name"].as(); + //if(json["sharedKey"].is()) sharedKey = json["sharedKey"].as(); + if(json["allowedFrom"].is()) allowedFrom = json["allowedFrom"].as(); + if(json["allowedUntil"].is()) allowedUntil = json["allowedUntil"].as(); + if(json["allowedWeekdays"].is()) allowedWeekdays = json["allowedWeekdays"].as(); + if(json["allowedFromTime"].is()) allowedFromTime = json["allowedFromTime"].as(); + if(json["allowedUntilTime"].is()) allowedUntilTime = json["allowedUntilTime"].as(); if(action) { diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index fc0cb16..63a7986 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -52,9 +52,10 @@ #define preference_update_from_mqtt (char*)"updMqtt" #define preference_disable_non_json (char*)"disnonjson" #define preference_official_hybrid_enabled (char*)"offHybrid" +#define preference_wifi_ssid (char*)"wifiSSID" +#define preference_wifi_pass (char*)"wifiPass" // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT -#define preference_find_best_rssi (char*)"nwbestrssi" #define preference_ntw_reconfigure (char*)"ntwRECONF" #define preference_auth_max_entries (char*)"authmaxentry" #define preference_auth_info_enabled (char*)"authInfoEna" @@ -88,14 +89,12 @@ #define preference_command_retry_delay (char*)"rtryDelay" #define preference_query_interval_hybrid_lockstate (char*)"hybridTimer" #define preference_mqtt_hass_cu_url (char*)"hassConfigUrl" -#define preference_network_wifi_fallback_disabled (char*)"nwwififb" #define preference_check_updates (char*)"checkupdates" #define preference_opener_continuous_mode (char*)"openercont" #define preference_rssi_publish_interval (char*)"rssipb" #define preference_network_timeout (char*)"nettmout" #define preference_restart_on_disconnect (char*)"restdisc" #define preference_publish_debug_info (char*)"pubdbg" -#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis" #define preference_official_hybrid_actions (char*)"hybridAct" #define preference_official_hybrid_retry (char*)"hybridRtry" @@ -118,12 +117,14 @@ #define preference_lock_max_timecontrol_entry_count (char*)"maxtc" #define preference_opener_max_timecontrol_entry_count (char*)"opmaxtc" #define preference_latest_version (char*)"latest" +#define preference_wifi_converted (char*)"wifiConv" //OBSOLETE #define preference_access_level (char*)"accLvl" #define preference_gpio_locking_enabled (char*)"gpiolck" #define preference_network_hardware_gpio (char*)"nwhwdt" #define preference_presence_detection_timeout (char*)"prdtimeout" +#define preference_network_wifi_fallback_disabled (char*)"nwwififb" inline bool initPreferences(Preferences* preferences) { @@ -248,9 +249,15 @@ inline bool initPreferences(Preferences* preferences) if (configVer < 901) { #if defined(CONFIG_IDF_TARGET_ESP32S3) - if (preferences->getInt(preference_network_hardware) == 3) preferences->putInt(preference_network_hardware, 10); + if (preferences->getInt(preference_network_hardware) == 3) + { + preferences->putInt(preference_network_hardware, 10); + } #endif - if (preferences->getInt(preference_network_hardware) == 2) preferences->putInt(preference_network_hardware, 3); + if (preferences->getInt(preference_network_hardware) == 2) + { + preferences->putInt(preference_network_hardware, 3); + } } preferences->putInt(preference_config_version, atof(NUKI_HUB_VERSION) * 100); @@ -271,8 +278,8 @@ private: preference_opener_continuous_mode, preference_mqtt_opener_path, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address, - preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_network_wifi_fallback_disabled, - preference_rssi_publish_interval, preference_hostname, preference_find_best_rssi, preference_network_timeout, preference_restart_on_disconnect, + preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, + preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_conf_info_enabled, @@ -280,26 +287,26 @@ private: preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_official_hybrid_enabled, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, - preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, + preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_webserial_enabled, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count, - preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, + preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, preference_wifi_ssid, preference_wifi_pass }; std::vector _redact = { preference_mqtt_user, preference_mqtt_password, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_cred_user, preference_cred_password, - preference_nuki_id_lock, preference_nuki_id_opener, + preference_nuki_id_lock, preference_nuki_id_opener, preference_wifi_pass }; std::vector _boolPrefs = { preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode, - preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_enable_bootloop_reset, preference_webserver_enabled, preference_find_best_rssi, + preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_enable_bootloop_reset, preference_webserver_enabled, preference_restart_on_disconnect, preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_show_secrets, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_ip_dhcp_enabled, - preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid_enabled, + preference_publish_authdata, preference_publish_debug_info, preference_official_hybrid_enabled, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt, - preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, + preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_webserial_enabled, preference_ntw_reconfigure }; std::vector _bytePrefs = diff --git a/src/RestartReason.h b/src/RestartReason.h index de62376..9d7857d 100644 --- a/src/RestartReason.h +++ b/src/RestartReason.h @@ -33,12 +33,9 @@ enum class RestartReason extern int restartReason; extern uint64_t restartReasonValidDetect; extern bool rebuildGpioRequested; - extern RestartReason currentRestartReason; - extern bool restartReason_isValid; - inline static void restartEsp(RestartReason reason) { if(reason == RestartReason::GpioConfigurationUpdated) diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 8443950..eb72821 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -9,6 +9,7 @@ #endif #ifndef CONFIG_IDF_TARGET_ESP32H2 #include +#include #endif #include @@ -77,12 +78,22 @@ void WebCfgServer::initialize() { _psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - #ifndef NUKI_HUB_UPDATER - return buildHtml(request); - #else - return buildOtaHtml(request); + if(!_network->isApOpen()) + { + #ifndef NUKI_HUB_UPDATER + return buildHtml(request); + #else + return buildOtaHtml(request); + #endif + } + #ifndef CONFIG_IDF_TARGET_ESP32H2 + else + { + return buildWifiConnectHtml(request); + } #endif }); + _psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); return sendCss(request); @@ -91,128 +102,6 @@ void WebCfgServer::initialize() if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); return sendFavicon(request); }); - #ifndef NUKI_HUB_UPDATER - _psychicServer->on("/import", HTTP_POST, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - String message = ""; - bool restart = processImport(request, message); - return buildConfirmHtml(request, message, 3, true); - }); - _psychicServer->on("/export", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return sendSettings(request); - }); - _psychicServer->on("/impexpcfg", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildImportExportHtml(request); - }); - _psychicServer->on("/status", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildStatusHtml(request); - }); - _psychicServer->on("/acclvl", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildAccLvlHtml(request); - }); - _psychicServer->on("/custntw", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildCustomNetworkConfigHtml(request); - }); - _psychicServer->on("/advanced", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildAdvancedConfigHtml(request); - }); - _psychicServer->on("/cred", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildCredHtml(request); - }); - _psychicServer->on("/mqttconfig", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildMqttConfigHtml(request); - }); - _psychicServer->on("/nukicfg", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildNukiConfigHtml(request); - }); - _psychicServer->on("/gpiocfg", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildGpioConfigHtml(request); - }); - #ifndef CONFIG_IDF_TARGET_ESP32H2 - _psychicServer->on("/wifi", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildConfigureWifiHtml(request); - }); - _psychicServer->on("/wifimanager", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - if(_allowRestartToPortal) - { - esp_err_t res = buildConfirmHtml(request, "Restarting. Connect to ESP access point to reconfigure Wi-Fi.", 0); - waitAndProcess(false, 1000); - _network->reconfigureDevice(); - return res; - } - return(ESP_OK); - }); - #endif - _psychicServer->on("/unpairlock", HTTP_POST, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return processUnpair(request, false); - }); - _psychicServer->on("/unpairopener", HTTP_POST, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return processUnpair(request, true); - }); - _psychicServer->on("/factoryreset", HTTP_POST, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return processFactoryReset(request); - }); - _psychicServer->on("/infopg", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildInfoHtml(request); - }); - _psychicServer->on("/debugon", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - _preferences->putBool(preference_publish_debug_info, true); - return buildConfirmHtml(request, "Debug On", 3, true); - }); - _psychicServer->on("/debugoff", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - _preferences->putBool(preference_publish_debug_info, false); - return buildConfirmHtml(request, "Debug Off", 3, true); - }); - _psychicServer->on("/savecfg", HTTP_POST, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - String message = ""; - bool restart = processArgs(request, message); - return buildConfirmHtml(request, message, 3, true); - }); - _psychicServer->on("/savegpiocfg", HTTP_POST, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - processGpioArgs(request); - esp_err_t res = buildConfirmHtml(request, "Saving GPIO configuration. Restarting.", 3, true); - Log->println(F("Restarting")); - waitAndProcess(true, 1000); - restartEsp(RestartReason::GpioConfigurationUpdated); - return res; - }); - #endif - _psychicServer->on("/ota", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildOtaHtml(request); - }); - _psychicServer->on("/otadebug", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return buildOtaHtml(request, true); - }); - _psychicServer->on("/reboottoota", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - esp_err_t res = buildConfirmHtml(request, "Rebooting to other partition", 2, true); - waitAndProcess(true, 1000); - esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL)); - restartEsp(RestartReason::OTAReboot); - return res; - }); _psychicServer->on("/reboot", HTTP_GET, [&](PsychicRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); esp_err_t res = buildConfirmHtml(request, "Rebooting", 2, true); @@ -220,50 +109,371 @@ void WebCfgServer::initialize() restartEsp(RestartReason::RequestedViaWebServer); return res; }); - _psychicServer->on("/autoupdate", HTTP_GET, [&](PsychicRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - #ifndef NUKI_HUB_UPDATER - return processUpdate(request); - #else - return request->redirect("/"); - #endif - }); - PsychicUploadHandler *updateHandler = new PsychicUploadHandler(); - updateHandler->onUpload([&](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final) - { + if(_network->isApOpen()) + { + #ifndef CONFIG_IDF_TARGET_ESP32H2 + _psychicServer->on("/ssidlist", HTTP_GET, [&](PsychicRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - return handleOtaUpload(request, filename, index, data, len, final); - } - ); + return buildSSIDListHtml(request); + }); + _psychicServer->on("/savewifi", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + String message = ""; + bool connected = processWiFi(request, message); + esp_err_t res = buildConfirmHtml(request, message, 10, true); - updateHandler->onRequest([&](PsychicRequest *request) { - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); - - String result; - if (!Update.hasError()) - { - Log->print("Update code or data OK Update.errorString() "); - Log->println(Update.errorString()); - result = "Update OK."; - esp_err_t res = request->reply(200,"text/html",result.c_str()); - restartEsp(RestartReason::OTACompleted); + if(connected) + { + waitAndProcess(true, 3000); + restartEsp(RestartReason::ReconfigureWifi); + //abort(); + } return res; - } - else { - result = " Update.errorString() " + String(Update.errorString()); - Log->print("ERROR : error "); - Log->println(result.c_str()); - esp_err_t res = request->reply(500, "text/html", result.c_str()); - restartEsp(RestartReason::OTAAborted); + }); + #endif + } + else + { + #ifndef NUKI_HUB_UPDATER + _psychicServer->on("/import", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + String message = ""; + bool restart = processImport(request, message); + return buildConfirmHtml(request, message, 3, true); + }); + _psychicServer->on("/export", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return sendSettings(request); + }); + _psychicServer->on("/impexpcfg", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildImportExportHtml(request); + }); + _psychicServer->on("/status", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildStatusHtml(request); + }); + _psychicServer->on("/acclvl", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildAccLvlHtml(request); + }); + _psychicServer->on("/custntw", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildCustomNetworkConfigHtml(request); + }); + _psychicServer->on("/advanced", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildAdvancedConfigHtml(request); + }); + _psychicServer->on("/cred", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildCredHtml(request); + }); + _psychicServer->on("/mqttconfig", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildMqttConfigHtml(request); + }); + _psychicServer->on("/nukicfg", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildNukiConfigHtml(request); + }); + _psychicServer->on("/gpiocfg", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildGpioConfigHtml(request); + }); + #ifndef CONFIG_IDF_TARGET_ESP32H2 + _psychicServer->on("/wifi", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildConfigureWifiHtml(request); + }); + _psychicServer->on("/wifimanager", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + if(_allowRestartToPortal) + { + esp_err_t res = buildConfirmHtml(request, "Restarting. Connect to ESP access point (\"NukiHub\" with password \"NukiHubESP32\") to reconfigure Wi-Fi.", 0); + waitAndProcess(false, 1000); + _network->reconfigureDevice(); + return res; + } + return(ESP_OK); + }); + #endif + _psychicServer->on("/unpairlock", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return processUnpair(request, false); + }); + _psychicServer->on("/unpairopener", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return processUnpair(request, true); + }); + _psychicServer->on("/factoryreset", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return processFactoryReset(request); + }); + _psychicServer->on("/infopg", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildInfoHtml(request); + }); + _psychicServer->on("/debugon", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + _preferences->putBool(preference_publish_debug_info, true); + return buildConfirmHtml(request, "Debug On", 3, true); + }); + _psychicServer->on("/debugoff", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + _preferences->putBool(preference_publish_debug_info, false); + return buildConfirmHtml(request, "Debug Off", 3, true); + }); + _psychicServer->on("/savecfg", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + String message = ""; + bool restart = processArgs(request, message); + return buildConfirmHtml(request, message, 3, true); + }); + _psychicServer->on("/savegpiocfg", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + processGpioArgs(request); + esp_err_t res = buildConfirmHtml(request, "Saving GPIO configuration. Restarting.", 3, true); + Log->println(F("Restarting")); + waitAndProcess(true, 1000); + restartEsp(RestartReason::GpioConfigurationUpdated); return res; - } - }); + }); + #endif + _psychicServer->on("/ota", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildOtaHtml(request); + }); + _psychicServer->on("/otadebug", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildOtaHtml(request, true); + }); + _psychicServer->on("/reboottoota", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + esp_err_t res = buildConfirmHtml(request, "Rebooting to other partition", 2, true); + waitAndProcess(true, 1000); + esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL)); + restartEsp(RestartReason::OTAReboot); + return res; + }); + _psychicServer->on("/autoupdate", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + #ifndef NUKI_HUB_UPDATER + return processUpdate(request); + #else + return request->redirect("/"); + #endif + }); - _psychicServer->on("/uploadota", HTTP_POST, updateHandler); - //Update.onProgress(printProgress); + PsychicUploadHandler *updateHandler = new PsychicUploadHandler(); + updateHandler->onUpload([&](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final) + { + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return handleOtaUpload(request, filename, index, data, len, final); + } + ); + + updateHandler->onRequest([&](PsychicRequest *request) { + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + + String result; + if (!Update.hasError()) + { + Log->print("Update code or data OK Update.errorString() "); + Log->println(Update.errorString()); + result = "Update OK."; + esp_err_t res = request->reply(200,"text/html",result.c_str()); + restartEsp(RestartReason::OTACompleted); + return res; + } + else { + result = " Update.errorString() " + String(Update.errorString()); + Log->print("ERROR : error "); + Log->println(result.c_str()); + esp_err_t res = request->reply(500, "text/html", result.c_str()); + restartEsp(RestartReason::OTAAborted); + return res; + } + }); + + _psychicServer->on("/uploadota", HTTP_POST, updateHandler); + //Update.onProgress(printProgress); + } } +#ifndef CONFIG_IDF_TARGET_ESP32H2 +esp_err_t WebCfgServer::buildSSIDListHtml(PsychicRequest *request) +{ + _network->scan(true, false); + createSsidList(); + + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + + for (int i = 0; i < _ssidList.size(); i++) + { + response.print("" + _ssidList[i] + String(F(" (")) + String(_rssiList[i]) + String(F(" %)")) + ""); + } + return response.endSend(); +} + +void WebCfgServer::createSsidList() +{ + int _foundNetworks = WiFi.scanComplete(); + std::vector _tmpSsidList; + std::vector _tmpRssiList; + + for (int i = 0; i < _foundNetworks; i++) + { + int rssi = constrain((100.0 + WiFi.RSSI(i)) * 2, 0, 100); + auto it1 = std::find(_ssidList.begin(), _ssidList.end(), WiFi.SSID(i)); + auto it2 = std::find(_tmpSsidList.begin(), _tmpSsidList.end(), WiFi.SSID(i)); + + if(it1 == _ssidList.end()) + { + _ssidList.push_back(WiFi.SSID(i)); + _rssiList.push_back(rssi); + _tmpSsidList.push_back(WiFi.SSID(i)); + _tmpRssiList.push_back(rssi); + } + else if (it2 == _tmpSsidList.end()) + { + _tmpSsidList.push_back(WiFi.SSID(i)); + _tmpRssiList.push_back(rssi); + int index = it1 - _ssidList.begin(); + _rssiList[index] = rssi; + } + else + { + int index = it1 - _ssidList.begin(); + int index2 = it2 - _tmpSsidList.begin(); + if (_tmpRssiList[index2] < rssi) + { + _tmpRssiList[index2] = rssi; + _rssiList[index] = rssi; + } + } + } +} + +esp_err_t WebCfgServer::buildWifiConnectHtml(PsychicRequest *request) +{ + String header = ""; + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response, header); + response.print("

Available WiFi networks

"); + response.print(""); + createSsidList(); + for (int i = 0; i < _ssidList.size(); i++) + { + response.print(""); + } + response.print("
" + _ssidList[i] + String(F(" (")) + String(_rssiList[i]) + String(F(" %)")) + "
"); + response.print("
"); + response.print("

WiFi credentials

"); + response.print(""); + printInputField(&response, "WIFISSID", "SSID", "", 32, "id=\"inputssid\"", false, true); + printInputField(&response, "WIFIPASS", "Secret key", "", 63, "id=\"inputpass\"", false, true); + response.print("
"); + response.print("
"); + response.print("
"); + response.print("

"); + response.print(""); + return response.endSend(); +} + +bool WebCfgServer::processWiFi(PsychicRequest *request, String& message) +{ + bool res = false; + int params = request->params(); + String ssid; + String pass; + + for(int index = 0; index < params; index++) + { + const PsychicWebParameter* p = request->getParam(index); + String key = p->name(); + String value = p->value(); + + + if(index < params -1) + { + const PsychicWebParameter* next = request->getParam(index+1); + if(key == next->name()) continue; + } + + if(key == "WIFISSID") + { + ssid = value; + } + else if(key == "WIFIPASS") + { + pass = value; + } + } + + ssid.trim(); + pass.trim(); + + if (ssid.length() > 0 && pass.length() > 0) + { + WiFi.begin(ssid, pass); + + int loop = 0; + while(WiFi.status() != WL_CONNECTED && loop < 150) + { + delay(100); + loop++; + } + + if (WiFi.status() != WL_CONNECTED) + { + message = "Failed to connect to the given SSID with the given secret key, credentials not saved
"; + } + else + { + message = "Connection successful. Rebooting Nuki Hub.
"; + if(WiFi.isConnected()) + { + esp_wifi_disconnect(); + } + + wifi_config_t wifi_cfg; + if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to get Wi-Fi configuration in RAM"); + return res; + } + + if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) { + Log->println("Failed to set storage Wi-Fi"); + return res; + } + + memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); + memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); + + if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to set Wi-Fi configuration"); + return res; + } + + _preferences->putString(preference_wifi_ssid, ssid); + _preferences->putString(preference_wifi_pass, pass); + + res = true; + } + } + else + { + message = "No SSID or secret key entered, credentials not saved
"; + } + + return res; +} +#endif + esp_err_t WebCfgServer::buildOtaHtml(PsychicRequest *request, bool debug) { PsychicStreamResponse response(request, "text/plain"); @@ -648,6 +858,62 @@ String WebCfgServer::generateConfirmCode() return String(code); } +void WebCfgServer::printInputField(PsychicStreamResponse *response, + const char *token, + const char *description, + const char *value, + const size_t& maxLength, + const char *args, + const bool& isPassword, + const bool& showLengthRestriction) +{ + char maxLengthStr[20]; + + itoa(maxLength, maxLengthStr, 10); + + response->print(""); + response->print(description); + + if(showLengthRestriction) + { + response->print(" (Max. "); + response->print(maxLength); + response->print(" characters)"); + } + + response->print(""); + response->print("print(" "); + response->print(args); + } + if(strcmp(value, "") != 0) + { + response->print(" value=\""); + response->print(value); + } + response->print("\" name=\""); + response->print(token); + response->print("\" size=\"25\" maxlength=\""); + response->print(maxLengthStr); + response->print("\"/>"); + response->print(""); +} + +void WebCfgServer::printInputField(PsychicStreamResponse *response, + const char *token, + const char *description, + const int value, + size_t maxLength, + const char *args) +{ + char valueStr[20]; + itoa(value, valueStr, 10); + printInputField(response, token, description, valueStr, maxLength, args); +} + #ifndef NUKI_HUB_UPDATER esp_err_t WebCfgServer::sendSettings(PsychicRequest *request) { @@ -1162,16 +1428,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, String& message) //configChanged = true; } } - else if(key == "BESTRSSI") - { - if(_preferences->getBool(preference_find_best_rssi, false) != (value == "1")) - { - _preferences->putBool(preference_find_best_rssi, (value == "1")); - Log->print(F("Setting changed: ")); - Log->println(key); - //configChanged = true; - } - } else if(key == "HOSTNAME") { if(_preferences->getString(preference_hostname, "") != value) @@ -1202,16 +1458,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, String& message) //configChanged = true; } } - else if(key == "RECNWTMQTTDIS") - { - if(_preferences->getBool(preference_recon_netw_on_mqtt_discon, false) != (value == "1")) - { - _preferences->putBool(preference_recon_netw_on_mqtt_discon, (value == "1")); - Log->print(F("Setting changed: ")); - Log->println(key); - //configChanged = true; - } - } else if(key == "MQTTLOG") { if(_preferences->getBool(preference_mqtt_log_enabled, false) != (value == "1")) @@ -2794,13 +3040,10 @@ esp_err_t WebCfgServer::buildMqttConfigHtml(PsychicRequest *request) printTextarea(&response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", _preferences->getString(preference_mqtt_key).c_str(), TLS_KEY_MAX_SIZE, true, true); printDropDown(&response, "NWHW", "Network hardware", String(_preferences->getInt(preference_network_hardware)), getNetworkDetectionOptions(), ""); #ifndef CONFIG_IDF_TARGET_ESP32H2 - printCheckBox(&response, "NWHWWIFIFB", "Disable fallback to Wi-Fi / Wi-Fi config portal", _preferences->getBool(preference_network_wifi_fallback_disabled), ""); - printCheckBox(&response, "BESTRSSI", "Connect to AP with the best signal in an environment with multiple APs with the same SSID", _preferences->getBool(preference_find_best_rssi), ""); printInputField(&response, "RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6, ""); #endif printInputField(&response, "NETTIMEOUT", "MQTT Timeout until restart (seconds; -1 to disable)", _preferences->getInt(preference_network_timeout), 5, ""); printCheckBox(&response, "RSTDISC", "Restart on disconnect", _preferences->getBool(preference_restart_on_disconnect), ""); - printCheckBox(&response, "RECNWTMQTTDIS", "Reconnect network on MQTT connection failure", _preferences->getBool(preference_recon_netw_on_mqtt_discon), ""); printCheckBox(&response, "MQTTLOG", "Enable MQTT logging", _preferences->getBool(preference_mqtt_log_enabled), ""); printCheckBox(&response, "CHECKUPDATE", "Check for Firmware Updates every 24h", _preferences->getBool(preference_check_updates), ""); printCheckBox(&response, "UPDATEMQTT", "Allow updating using MQTT", _preferences->getBool(preference_update_from_mqtt), ""); @@ -2945,13 +3188,13 @@ String WebCfgServer::pinStateToString(uint8_t value) { switch(value) { case 0: - return (String)"PIN not set"; + return String("PIN not set"); case 1: - return (String)"PIN valid"; + return String("PIN valid"); case 2: - return (String)"PIN set but invalid";; + return String("PIN set but invalid"); default: - return (String)"Unknown"; + return String("Unknown"); } } @@ -3230,7 +3473,7 @@ esp_err_t WebCfgServer::buildConfigureWifiHtml(PsychicRequest *request) response.beginSend(); buildHtmlHeader(&response); response.print("

Wi-Fi

"); - response.print("Click confirm to restart ESP into Wi-Fi configuration mode. After restart, connect to ESP access point to reconfigure Wi-Fi.

"); + response.print("Click confirm to remove saved WiFi settings and restart ESP into Wi-Fi configuration mode. After restart, connect to ESP access point to reconfigure Wi-Fi.

"); buildNavigationButton(&response, "Confirm", "/wifimanager"); response.print(""); return response.endSend(); @@ -3366,12 +3609,8 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request) } #ifndef CONFIG_IDF_TARGET_ESP32H2 - response.print("\nFallback to Wi-Fi / Wi-Fi config portal disabled: "); - response.print(_preferences->getBool(preference_network_wifi_fallback_disabled, false) ? "Yes" : "No"); if(_network->networkDeviceName() == "Built-in Wi-Fi") { - response.print("\nConnect to AP with the best signal enabled: "); - response.print(_preferences->getBool(preference_find_best_rssi, false) ? "Yes" : "No"); response.print("\nRSSI Publish interval (s): "); if(_preferences->getInt(preference_rssi_publish_interval, 60) < 0) response.print("Disabled"); @@ -3380,8 +3619,6 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request) #endif response.print("\nRestart ESP32 on network disconnect enabled: "); response.print(_preferences->getBool(preference_restart_on_disconnect, false) ? "Yes" : "No"); - response.print("\nReconnect network on MQTT connection failure enabled: "); - response.print(_preferences->getBool(preference_recon_netw_on_mqtt_discon, false) ? "Yes" : "No"); response.print("\nMQTT Timeout until restart (s): "); if(_preferences->getInt(preference_network_timeout, 60) < 0) response.print("Disabled"); else response.print(_preferences->getInt(preference_network_timeout, 60)); @@ -3967,11 +4204,6 @@ esp_err_t WebCfgServer::processFactoryReset(PsychicRequest *request) #ifndef CONFIG_IDF_TARGET_ESP32H2 if(resetWifi) { - wifi_config_t current_conf; - esp_wifi_get_config((wifi_interface_t)ESP_IF_WIFI_STA, ¤t_conf); - memset(current_conf.sta.ssid, 0, sizeof(current_conf.sta.ssid)); - memset(current_conf.sta.password, 0, sizeof(current_conf.sta.password)); - esp_wifi_set_config((wifi_interface_t)ESP_IF_WIFI_STA, ¤t_conf); _network->reconfigureDevice(); } #endif @@ -3981,62 +4213,6 @@ esp_err_t WebCfgServer::processFactoryReset(PsychicRequest *request) return res; } -void WebCfgServer::printInputField(PsychicStreamResponse *response, - const char *token, - const char *description, - const char *value, - const size_t& maxLength, - const char *args, - const bool& isPassword, - const bool& showLengthRestriction) -{ - char maxLengthStr[20]; - - itoa(maxLength, maxLengthStr, 10); - - response->print(""); - response->print(description); - - if(showLengthRestriction) - { - response->print(" (Max. "); - response->print(maxLength); - response->print(" characters)"); - } - - response->print(""); - response->print("print(" "); - response->print(args); - } - if(strcmp(value, "") != 0) - { - response->print(" value=\""); - response->print(value); - } - response->print("\" name=\""); - response->print(token); - response->print("\" size=\"25\" maxlength=\""); - response->print(maxLengthStr); - response->print("\"/>"); - response->print(""); -} - -void WebCfgServer::printInputField(PsychicStreamResponse *response, - const char *token, - const char *description, - const int value, - size_t maxLength, - const char *args) -{ - char valueStr[20]; - itoa(value, valueStr, 10); - printInputField(response, token, description, valueStr, maxLength, args); -} - void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *token, const char *description, const bool value, const char *htmlClass) { response->print(""); @@ -4183,7 +4359,7 @@ const std::vector> WebCfgServer::getNetworkDetectionOp { std::vector> options; - options.push_back(std::make_pair("1", "Wi-Fi only")); + options.push_back(std::make_pair("1", "Wi-Fi")); options.push_back(std::make_pair("2", "Generic W5500")); options.push_back(std::make_pair("3", "M5Stack Atom POE (W5500)")); options.push_back(std::make_pair("10", "M5Stack Atom POE S3 (W5500)")); diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index f9fe598..0a6e75d 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -55,7 +55,7 @@ private: esp_err_t buildCredHtml(PsychicRequest *request); esp_err_t buildImportExportHtml(PsychicRequest *request); esp_err_t buildMqttConfigHtml(PsychicRequest *request); - esp_err_t buildStatusHtml(PsychicRequest *request); + esp_err_t buildStatusHtml(PsychicRequest *request); esp_err_t buildAdvancedConfigHtml(PsychicRequest *request); esp_err_t buildNukiConfigHtml(PsychicRequest *request); esp_err_t buildGpioConfigHtml(PsychicRequest *request); @@ -67,8 +67,6 @@ private: esp_err_t processUnpair(PsychicRequest *request, bool opener); esp_err_t processUpdate(PsychicRequest *request); esp_err_t processFactoryReset(PsychicRequest *request); - void printInputField(PsychicStreamResponse *response, const char* token, const char* description, const char* value, const size_t& maxLength, const char* args, const bool& isPassword = false, const bool& showLengthRestriction = false); - void printInputField(PsychicStreamResponse *response, const char* token, const char* description, const int value, size_t maxLength, const char* args); void printCheckBox(PsychicStreamResponse *response, const char* token, const char* description, const bool value, const char* htmlClass); void printTextarea(PsychicStreamResponse *response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false); void printDropDown(PsychicStreamResponse *response, const char *token, const char *description, const String preselectedValue, std::vector> options, const String className); @@ -94,19 +92,30 @@ private: bool _brokerConfigured = false; bool _rebootRequired = false; #endif - + + std::vector _ssidList; + std::vector _rssiList; String generateConfirmCode(); String _confirmCode = "----"; + esp_err_t buildSSIDListHtml(PsychicRequest *request); esp_err_t buildConfirmHtml(PsychicRequest *request, const String &message, uint32_t redirectDelay = 5, bool redirect = false); esp_err_t buildOtaHtml(PsychicRequest *request, bool debug = false); esp_err_t buildOtaCompletedHtml(PsychicRequest *request); esp_err_t sendCss(PsychicRequest *request); esp_err_t sendFavicon(PsychicRequest *request); + void createSsidList(); void buildHtmlHeader(PsychicStreamResponse *response, String additionalHeader = ""); void waitAndProcess(const bool blocking, const uint32_t duration); esp_err_t handleOtaUpload(PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final); void printProgress(size_t prg, size_t sz); - + #ifndef CONFIG_IDF_TARGET_ESP32H2 + esp_err_t buildWifiConnectHtml(PsychicRequest *request); + bool processWiFi(PsychicRequest *request, String& message); + + #endif + void printInputField(PsychicStreamResponse *response, const char* token, const char* description, const char* value, const size_t& maxLength, const char* args, const bool& isPassword = false, const bool& showLengthRestriction = false); + void printInputField(PsychicStreamResponse *response, const char* token, const char* description, const int value, size_t maxLength, const char* args); + PsychicHttpServer* _psychicServer = nullptr; NukiNetwork* _network = nullptr; Preferences* _preferences = nullptr; diff --git a/src/main.cpp b/src/main.cpp index ab5702a..32d6b4e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,7 @@ #include "esp_ota_ops.h" #include "esp_http_client.h" #include "esp_https_ota.h" -#include +#include "esp_task_wdt.h" #include "Config.h" #ifndef NUKI_HUB_UPDATER @@ -78,7 +78,6 @@ TaskHandle_t networkTaskHandle = nullptr; ssize_t write_fn(void* cookie, const char* buf, ssize_t size) { Log->write((uint8_t *)buf, (size_t)size); - return size; } @@ -104,7 +103,7 @@ int _log_vprintf(const char *fmt, va_list args) { void setReroute(){ esp_log_set_vprintf(_log_vprintf); - if(preferences->getBool(preference_mqtt_log_enabled)) + if(preferences->getBool(preference_mqtt_log_enabled)) { esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt", ESP_LOG_NONE); @@ -377,8 +376,11 @@ void setupTasks(bool ota) xTaskCreatePinnedToCore(networkTask, "ntw", preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), NULL, 3, &networkTaskHandle, 1); esp_task_wdt_add(networkTaskHandle); #ifndef NUKI_HUB_UPDATER - xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 0); - esp_task_wdt_add(nukiTaskHandle); + if(!network->isApOpen()) + { + xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 0); + esp_task_wdt_add(nukiTaskHandle); + } #endif } } @@ -434,7 +436,7 @@ void setup() webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer); webCfgServer->initialize(); psychicServer->listen(80); - psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/"); }); + psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/"); }); } #else Log->print(F("Nuki Hub version ")); @@ -453,7 +455,6 @@ void setup() } char16_t buffer_size = preferences->getInt(preference_buffer_size, 4096); - CharBuffer::initialize(buffer_size); gpio = new Gpio(preferences); @@ -461,22 +462,30 @@ void setup() gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r"); Log->print(gpioDesc.c_str()); + const String mqttLockPath = preferences->getString(preference_mqtt_lock_path); + + network = new NukiNetwork(preferences, gpio, mqttLockPath, CharBuffer::get(), buffer_size); + network->initialize(); + + lockEnabled = preferences->getBool(preference_lock_enabled); + openerEnabled = preferences->getBool(preference_opener_enabled); + + if(network->isApOpen()) + { + forceEnableWebServer = true; + doOta = false; + lockEnabled = false; + openerEnabled = false; + } + bleScanner = new BleScanner::Scanner(); // Scan interval and window according to Nuki recommendations: // https://developer.nuki.io/t/bluetooth-specification-questions/1109/27 bleScanner->initialize("NukiHub", true, 40, 40); bleScanner->setScanDuration(0); - lockEnabled = preferences->getBool(preference_lock_enabled); - openerEnabled = preferences->getBool(preference_opener_enabled); - - const String mqttLockPath = preferences->getString(preference_mqtt_lock_path); - nukiOfficial = new NukiOfficial(preferences); - network = new NukiNetwork(preferences, gpio, mqttLockPath, CharBuffer::get(), buffer_size); - network->initialize(); - networkLock = new NukiNetworkLock(network, nukiOfficial, preferences, CharBuffer::get(), buffer_size); networkLock->initialize(); @@ -533,7 +542,7 @@ void setup() if(doOta) setupTasks(true); else setupTasks(false); - + #ifdef DEBUG_NUKIHUB Log->print("Task Name\tStatus\tPrio\tHWM\tTask\tAffinity\n"); char stats_buffer[1024]; diff --git a/src/networkDevices/EthernetDevice.cpp b/src/networkDevices/EthernetDevice.cpp index 466f569..9a31c7f 100644 --- a/src/networkDevices/EthernetDevice.cpp +++ b/src/networkDevices/EthernetDevice.cpp @@ -3,6 +3,9 @@ #include "../Logger.h" #include "../RestartReason.h" +RTC_NOINIT_ATTR bool criticalEthFailure; +extern char WiFi_fallbackDetect[14]; + EthernetDevice::EthernetDevice(const String& hostname, Preferences* preferences, const IPConfiguration* ipConfiguration, const std::string& deviceName, uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_type_t ethtype, eth_clock_mode_t clock_mode) : NetworkDevice(hostname, ipConfiguration), _deviceName(deviceName), @@ -54,20 +57,34 @@ const String EthernetDevice::deviceName() const void EthernetDevice::initialize() { delay(250); + if(criticalEthFailure) + { + criticalEthFailure = false; + Log->println(F("Failed to initialize ethernet hardware")); + Log->println("Network device has a critical failure, enable fallback to Wi-Fi and reboot."); + strcpy(WiFi_fallbackDetect, "wifi_fallback"); + delay(200); + restartEsp(RestartReason::NetworkDeviceCriticalFailure); + return; + } Log->println(F("Init Ethernet")); if(_useSpi) { Log->println(F("Use SPI")); + criticalEthFailure = true; SPI.begin(_spi_sck, _spi_miso, _spi_mosi); _hardwareInitialized = ETH.begin(_type, _phy_addr, _cs, _irq, _rst, SPI); + criticalEthFailure = false; } #ifdef CONFIG_IDF_TARGET_ESP32 else { Log->println(F("Use RMII")); + criticalEthFailure = true; _hardwareInitialized = ETH.begin(_type, _phy_addr, _mdc, _mdio, _power, _clock_mode); + criticalEthFailure = false; if(!_ipConfiguration->dhcpEnabled()) { _checkIpTs = (esp_timer_get_time() / 1000) + 2000; @@ -78,6 +95,7 @@ void EthernetDevice::initialize() if(_hardwareInitialized) { Log->println(F("Ethernet hardware Initialized")); + memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect)); if(_useSpi && !_ipConfiguration->dhcpEnabled()) { @@ -92,6 +110,11 @@ void EthernetDevice::initialize() else { Log->println(F("Failed to initialize ethernet hardware")); + Log->println("Network device has a critical failure, enable fallback to Wi-Fi and reboot."); + strcpy(WiFi_fallbackDetect, "wifi_fallback"); + delay(200); + restartEsp(RestartReason::NetworkDeviceCriticalFailure); + return; } } @@ -173,25 +196,23 @@ void EthernetDevice::reconfigure() restartEsp(RestartReason::ReconfigureETH); } +void EthernetDevice::scan(bool passive, bool async) +{ +} + bool EthernetDevice::isConnected() { return _connected; } -ReconnectStatus EthernetDevice::reconnect(bool force) +bool EthernetDevice::isApOpen() { - if(!_hardwareInitialized) - { - return ReconnectStatus::CriticalFailure; - } - delay(200); - return isConnected() ? ReconnectStatus::Success : ReconnectStatus::Failure; + return false; } void EthernetDevice::onDisconnected() { if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); - reconnect(); } int8_t EthernetDevice::signalStrength() diff --git a/src/networkDevices/EthernetDevice.h b/src/networkDevices/EthernetDevice.h index 60fb7a2..c68a22e 100644 --- a/src/networkDevices/EthernetDevice.h +++ b/src/networkDevices/EthernetDevice.h @@ -45,19 +45,19 @@ public: virtual void initialize(); virtual void reconfigure(); virtual void update(); - - virtual ReconnectStatus reconnect(bool force = false); + virtual void scan(bool passive = false, bool async = true); virtual bool isConnected(); + virtual bool isApOpen(); int8_t signalStrength() override; - + String localIP() override; String BSSIDstr() override; private: Preferences* _preferences; - + void onDisconnected(); void onNetworkEvent(arduino_event_id_t event, arduino_event_info_t info); diff --git a/src/networkDevices/NetworkDevice.h b/src/networkDevices/NetworkDevice.h index 2c933de..7c32fc4 100644 --- a/src/networkDevices/NetworkDevice.h +++ b/src/networkDevices/NetworkDevice.h @@ -1,13 +1,6 @@ #pragma once #include "IPConfiguration.h" -enum class ReconnectStatus -{ - Failure = 0, - Success = 1, - CriticalFailure = 2 -}; - class NetworkDevice { public: @@ -19,11 +12,12 @@ public: virtual const String deviceName() const = 0; virtual void initialize() = 0; - virtual ReconnectStatus reconnect(bool force = false) = 0; virtual void reconfigure() = 0; virtual void update(); + virtual void scan(bool passive = false, bool async = true) = 0; virtual bool isConnected() = 0; + virtual bool isApOpen() = 0; virtual int8_t signalStrength() = 0; virtual String localIP() = 0; diff --git a/src/networkDevices/WifiDevice.cpp b/src/networkDevices/WifiDevice.cpp index c986b5b..3012fa1 100644 --- a/src/networkDevices/WifiDevice.cpp +++ b/src/networkDevices/WifiDevice.cpp @@ -1,17 +1,14 @@ +#include "esp_wifi.h" #include #include "WifiDevice.h" #include "../PreferencesKeys.h" #include "../Logger.h" #include "../RestartReason.h" -RTC_NOINIT_ATTR char WiFiDevice_reconfdetect[17]; - WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const IPConfiguration* ipConfiguration) : NetworkDevice(hostname, ipConfiguration), - _preferences(preferences), - _wm(preferences->getString(preference_cred_user, "").c_str(), preferences->getString(preference_cred_password, "").c_str()) + _preferences(preferences) { - _startAp = strcmp(WiFiDevice_reconfdetect, "reconfigure_wifi") == 0; } const String WifiDevice::deviceName() const @@ -21,123 +18,399 @@ const String WifiDevice::deviceName() const void WifiDevice::initialize() { - std::vector wm_menu; - wm_menu.push_back("wifi"); - wm_menu.push_back("exit"); - _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); - // reduced timeout if ESP is set to restart on disconnect - _wm.setFindBestRSSI(_preferences->getBool(preference_find_best_rssi)); - _wm.setConnectTimeout(20); - _wm.setConfigPortalTimeout(_preferences->getBool(preference_restart_on_disconnect, false) ? 60 * 3 : 60 * 30); - _wm.setShowInfoUpdate(false); - _wm.setMenu(wm_menu); - _wm.setHostname(_hostname); + String ssid = _preferences->getString(preference_wifi_ssid, ""); + String pass = _preferences->getString(preference_wifi_pass, ""); + WiFi.setHostname(_hostname.c_str()); if(!_ipConfiguration->dhcpEnabled()) { - _wm.setSTAStaticIPConfig(_ipConfiguration->ipAddress(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet(), _ipConfiguration->dnsServer()); - } - - _wm.setAPCallback(clearRtcInitVar); - - bool res = false; - bool connectedFromPortal = false; - - if(_startAp) - { - Log->println(F("Opening Wi-Fi configuration portal.")); - res = _wm.startConfigPortal(); - connectedFromPortal = true; - } - else - { - res = _wm.autoConnect(); // password protected ap - } - - if(!res) - { - esp_wifi_disconnect(); - esp_wifi_stop(); - esp_wifi_deinit(); - - Log->println(F("Failed to connect. Wait for ESP restart.")); - delay(1000); - restartEsp(RestartReason::WifiInitFailed); - } - else { - Log->print(F("Wi-Fi connected: ")); - Log->println(WiFi.localIP().toString()); - - if(connectedFromPortal) - { - Log->println(F("Connected using WifiManager portal. Wait for ESP restart.")); - delay(1000); - restartEsp(RestartReason::ConfigurationUpdated); - } + WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet()); } WiFi.onEvent([&](WiFiEvent_t event, WiFiEventInfo_t info) { if(event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { - onDisconnected(); + if(!_openAP && !_connecting && _connected) + { + onDisconnected(); + } } - else if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP) + else if(event == ARDUINO_EVENT_WIFI_STA_CONNECTED) { onConnected(); } + else if(event == ARDUINO_EVENT_WIFI_SCAN_DONE) + { + Log->println(F("Wi-Fi scan done")); + _foundNetworks = WiFi.scanComplete(); + + for (int i = 0; i < _foundNetworks; i++) + { + Log->println(String(F("SSID ")) + WiFi.SSID(i) + String(F(" found with RSSI: ")) + + String(WiFi.RSSI(i)) + String(F("(")) + + String(constrain((100.0 + WiFi.RSSI(i)) * 2, 0, 100)) + + String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(i) + + String(F(" and channel: ")) + String(WiFi.channel(i))); + } + + if (_connectOnScanDone && _foundNetworks > 0) + { + connect(); + } + else if (_connectOnScanDone) + { + Log->println("No networks found, restarting scan"); + scan(false, true); + } + else if (_openAP) + { + openAP(); + } + else if(_convertOldWiFi) + { + _convertOldWiFi = false; + _preferences->putBool(preference_wifi_converted, true); + + wifi_config_t wifi_cfg; + if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to get Wi-Fi configuration in RAM"); + } + + if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) { + Log->println("Failed to set storage Wi-Fi"); + } + + String tempSSID = String(reinterpret_cast(wifi_cfg.sta.ssid)); + String tempPass = String(reinterpret_cast(wifi_cfg.sta.password)); + tempSSID.trim(); + tempPass.trim(); + bool found = false; + + for (int i = 0; i < _foundNetworks; i++) + { + if(tempSSID.length() > 0 && tempSSID == WiFi.SSID(i) && tempPass.length() > 0) + { + ssid = tempSSID; + pass = tempPass; + _preferences->putString(preference_wifi_ssid, ssid); + _preferences->putString(preference_wifi_pass, pass); + found = true; + break; + } + } + + memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); + memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); + + if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to clear NVS Wi-Fi configuration"); + } + + if(found) + { + Log->println(String("Attempting to connect to saved SSID ") + String(ssid)); + _connectOnScanDone = true; + _openAP = false; + scan(false, true); + } + else + { + Log->println("No SSID or Wifi password saved, opening AP"); + _connectOnScanDone = false; + _openAP = true; + scan(false, true); + } + } + } }); + + ssid.trim(); + pass.trim(); + + if(ssid.length() > 0 && ssid != "~" && pass.length() > 0) + { + Log->println(String("Attempting to connect to saved SSID ") + String(ssid)); + _connectOnScanDone = true; + _openAP = false; + scan(false, true); + } + else + { + if(!_preferences->getBool(preference_wifi_converted, false)) + { + _connectOnScanDone = false; + _openAP = false; + _convertOldWiFi = true; + scan(false, true); + } + + ssid.trim(); + pass.trim(); + + if(ssid.length() > 0 && ssid != "~" && pass.length() > 0) + { + Log->println(String("Attempting to connect to saved SSID ") + String(ssid)); + _connectOnScanDone = true; + _openAP = false; + scan(false, true); + } + else + { + Log->println("No SSID or Wifi password saved, opening AP"); + _connectOnScanDone = false; + _openAP = true; + scan(false, true); + } + } +} + +void WifiDevice::scan(bool passive, bool async) +{ + WiFi.scanDelete(); + + if(async) + { + Log->println(F("Wi-Fi async scan started")); + } + else + { + Log->println(F("Wi-Fi sync scan started")); + } + if(passive) + { + WiFi.scanNetworks(async,false,true,75U); + } + else + { + WiFi.scanNetworks(async); + } +} + +void WifiDevice::openAP() +{ + if(_startAP) + { + WiFi.persistent(false); + WiFi.mode(WIFI_AP_STA); + WiFi.persistent(false); + WiFi.softAPsetHostname(_hostname.c_str()); + WiFi.softAP("NukiHub", "NukiHubESP32"); + WiFi.persistent(false); + _startAP = false; + } +} + +bool WifiDevice::connect() +{ + bool ret = false; + String ssid = _preferences->getString(preference_wifi_ssid, ""); + String pass = _preferences->getString(preference_wifi_pass, ""); + WiFi.persistent(false); + WiFi.mode(WIFI_STA); + WiFi.setHostname(_hostname.c_str()); + delay(500); + + int bestConnection = -1; + for (int i = 0; i < _foundNetworks; i++) + { + if (ssid == WiFi.SSID(i)) + { + Log->println(String(F("Saved SSID ")) + ssid + String(F(" found with RSSI: ")) + + String(WiFi.RSSI(i)) + String(F("(")) + + String(constrain((100.0 + WiFi.RSSI(i)) * 2, 0, 100)) + + String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(i) + + String(F(" and channel: ")) + String(WiFi.channel(i))); + if (bestConnection == -1) + { + bestConnection = i; + } + else + { + if (WiFi.RSSI(i) > WiFi.RSSI(bestConnection)) + { + bestConnection = i; + } + } + } + } + + if (bestConnection == -1) + { + Log->print("No network found with SSID: "); + Log->println(ssid); + if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); + _connectOnScanDone = true; + _openAP = false; + scan(false, true); + return false; + } + else + { + _connecting = true; + Log->println(String(F("Trying to connect to SSID ")) + ssid + String(F(" found with RSSI: ")) + + String(WiFi.RSSI(bestConnection)) + String(F("(")) + + String(constrain((100.0 + WiFi.RSSI(bestConnection)) * 2, 0, 100)) + + String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(bestConnection) + + String(F(" and channel: ")) + String(WiFi.channel(bestConnection))); + ret = WiFi.begin(ssid.c_str(), pass.c_str(), WiFi.channel(bestConnection), WiFi.BSSID(bestConnection), true); + WiFi.persistent(false); + _connecting = false; + } + + if(!ret) + { + int loop = 0; + + while(!isConnected() && loop < 200) + { + loop++; + delay(100); + } + + if(!isConnected()) + { + esp_wifi_disconnect(); + esp_wifi_stop(); + esp_wifi_deinit(); + + Log->println(F("Failed to connect. Wait for ESP restart.")); + delay(1000); + restartEsp(RestartReason::WifiInitFailed); + } + } + else + { + if(!_preferences->getBool(preference_wifi_converted, false)) + { + _preferences->putBool(preference_wifi_converted, true); + } + + int loop = 0; + + while(!isConnected() && loop < 200) + { + loop++; + delay(100); + } + + if(!isConnected()) + { + if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) + { + restartEsp(RestartReason::RestartOnDisconnectWatchdog); + return false; + } + Log->print("Connection failed, retrying"); + _connectOnScanDone = true; + _openAP = false; + scan(false, true); + return false; + } + } + + return ret; } void WifiDevice::reconfigure() { - strcpy(WiFiDevice_reconfdetect, "reconfigure_wifi"); + bool changed = false; + wifi_config_t wifi_cfg; + if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to get Wi-Fi configuration in RAM"); + } + + if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) { + Log->println("Failed to set storage Wi-Fi"); + } + + if(sizeof(wifi_cfg.sta.ssid) > 0) + { + memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); + changed = true; + } + if(sizeof(wifi_cfg.sta.password) > 0) + { + memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); + changed = true; + } + if(changed) + { + if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { + Log->println("Failed to clear NVS Wi-Fi configuration"); + } + } + + _preferences->putString(preference_wifi_ssid, ""); + _preferences->putString(preference_wifi_pass, ""); delay(200); restartEsp(RestartReason::ReconfigureWifi); } bool WifiDevice::isConnected() { - return WiFi.isConnected(); -} - -ReconnectStatus WifiDevice::reconnect(bool force) -{ - _wm.setFindBestRSSI(_preferences->getBool(preference_find_best_rssi)); - - if((!isConnected() || force) && !_isReconnecting) - { - _isReconnecting = true; - WiFi.disconnect(); - int loop = 0; - - while(isConnected() && loop <20) - { - delay(100); - loop++; - } - - _wm.resetScan(); - _wm.autoConnect(); - _isReconnecting = false; - } - - if(!isConnected() && _disconnectTs > (esp_timer_get_time() / 1000) - 120000) _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); - return isConnected() ? ReconnectStatus::Success : ReconnectStatus::Failure; + return (WiFi.status() == WL_CONNECTED); } void WifiDevice::onConnected() { - _isReconnecting = false; - _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); + Log->println(F("Wi-Fi connected")); + _connectedChannel = WiFi.channel(); + _connectedBSSID = WiFi.BSSID(); + _connected = true; } void WifiDevice::onDisconnected() { - _disconnectTs = (esp_timer_get_time() / 1000); - if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); - _wm.setEnableConfigPortal(false); - reconnect(); + if(_connected) + { + _connected = false; + _disconnectTs = (esp_timer_get_time() / 1000); + Log->println(F("Wi-Fi disconnected")); + + //QUICK RECONNECT + _connecting = true; + String ssid = _preferences->getString(preference_wifi_ssid, ""); + String pass = _preferences->getString(preference_wifi_pass, ""); + WiFi.begin(ssid.c_str(), pass.c_str(), _connectedChannel, _connectedBSSID, true); + WiFi.persistent(false); + + int loop = 0; + + while(!isConnected() && loop < 50) + { + loop++; + delay(100); + } + + _connecting = false; + //END QUICK RECONECT + + if(!isConnected()) + { + if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); + + WiFi.persistent(false); + WiFi.disconnect(true); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(500); + + wifi_mode_t wifiMode; + esp_wifi_get_mode(&wifiMode); + + while (wifiMode != WIFI_MODE_STA || WiFi.status() == WL_CONNECTED) + { + delay(500); + Log->println(F("Waiting for WiFi mode change or disconnection.")); + esp_wifi_get_mode(&wifiMode); + } + + _connectOnScanDone = true; + _openAP = false; + scan(false, true); + } + } } int8_t WifiDevice::signalStrength() @@ -155,7 +428,7 @@ String WifiDevice::BSSIDstr() return WiFi.BSSIDstr(); } -void WifiDevice::clearRtcInitVar(WiFiManager *) +bool WifiDevice::isApOpen() { - memset(WiFiDevice_reconfdetect, 0, sizeof WiFiDevice_reconfdetect); + return _openAP; } \ No newline at end of file diff --git a/src/networkDevices/WifiDevice.h b/src/networkDevices/WifiDevice.h index 5a9787f..55da63f 100644 --- a/src/networkDevices/WifiDevice.h +++ b/src/networkDevices/WifiDevice.h @@ -4,7 +4,6 @@ #include #include #include "NetworkDevice.h" -#include "WiFiManager.h" #include "IPConfiguration.h" class WifiDevice : public NetworkDevice @@ -16,25 +15,32 @@ public: virtual void initialize(); virtual void reconfigure(); - virtual ReconnectStatus reconnect(bool force = false); + virtual void scan(bool passive = false, bool async = true); virtual bool isConnected(); + virtual bool isApOpen(); int8_t signalStrength() override; String localIP() override; String BSSIDstr() override; - private: - static void clearRtcInitVar(WiFiManager*); - + void openAP(); void onDisconnected(); void onConnected(); + bool connect(); - WiFiManager _wm; Preferences* _preferences = nullptr; - bool _startAp = false; - bool _isReconnecting = false; + int _foundNetworks = 0; + int _disconnectCount = 0; + bool _connectOnScanDone = false; + bool _connecting = false; + bool _openAP = false; + bool _startAP = true; + bool _convertOldWiFi = false; + bool _connected = false; + uint8_t _connectedChannel = 0; + uint8_t* _connectedBSSID; int64_t _disconnectTs = 0; }; diff --git a/updater/platformio.ini b/updater/platformio.ini index 1918816..41ca4ca 100644 --- a/updater/platformio.ini +++ b/updater/platformio.ini @@ -24,7 +24,8 @@ board_build.embed_txtfiles = build_type = release custom_build = release board_build.partitions = partitions.csv -build_unflags = +build_unflags = + -DESP32 -Werror=all -Wall build_flags = @@ -54,7 +55,7 @@ lib_ignore = WiFiProv lib_deps = PsychicHttp=symlink://../lib/PsychicHttp - WiFiManager=symlink://../lib/WiFiManager + ArduinoJson=symlink://../lib/ArduinoJson monitor_speed = 115200 monitor_filters =