WiFi portal

This commit is contained in:
iranl
2024-10-09 21:33:18 +02:00
parent 220ea7a96d
commit 06e96b5ea7
18 changed files with 999 additions and 497 deletions

View File

@@ -84,7 +84,8 @@ Unpack the zip archive and read the included how-to-flash.txt for installation i
## Initial setup (Network and MQTT) ## Initial setup (Network and MQTT)
Power up the ESP32 and a new Wi-Fi access point named "ESP32_(8 CHARACTER ALPHANUMERIC)" should appear.<br> Power up the ESP32 and a new Wi-Fi access point named "NukiHub" should appear.<br>
The password of the access point is "NukiHubESP32".<br>
Connect a client device to this access point and in a browser navigate to "http://192.168.4.1".<br> Connect a client device to this access point and in a browser navigate to "http://192.168.4.1".<br>
Use the web interface to connect the ESP to your preferred Wi-Fi network.<br> Use the web interface to connect the ESP to your preferred Wi-Fi network.<br>
<br> <br>
@@ -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. 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. 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.<br> Note that there are two builds of Nuki Hub for the ESP32-S3 available.<br>
One for devices with no or Quad SPI PSRAM and one for devices with Octal SPI PSRAM.<br> One for devices with no or Quad SPI PSRAM and one for devices with Octal SPI PSRAM.<br>
If your ESP32-S3 device has PSRAM but it is not detected please flash the other S3 binary. 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 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. - 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. - 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. - 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. - 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. - 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.

View File

@@ -27,6 +27,7 @@ board_build.partitions = partitions.csv
build_unflags = build_unflags =
-DCONFIG_BT_NIMBLE_LOG_LEVEL -DCONFIG_BT_NIMBLE_LOG_LEVEL
-DCONFIG_BTDM_BLE_SCAN_DUPL -DCONFIG_BTDM_BLE_SCAN_DUPL
-DESP32
-Werror=all -Werror=all
-Wall -Wall
build_flags = build_flags =

View File

@@ -4,7 +4,7 @@
#define NUKI_HUB_VERSION "9.01" #define NUKI_HUB_VERSION "9.01"
#define NUKI_HUB_BUILD "unknownbuildnr" #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_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" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json"

View File

@@ -69,7 +69,7 @@ void NukiNetwork::setupDevice()
{ {
_ipConfiguration = new IPConfiguration(_preferences); _ipConfiguration = new IPConfiguration(_preferences);
int hardwareDetect = _preferences->getInt(preference_network_hardware, 0); int hardwareDetect = _preferences->getInt(preference_network_hardware, 0);
Log->print(F("Hardware detect : ")); Log->print(F("Hardware detect: "));
Log->println(hardwareDetect); Log->println(hardwareDetect);
_firstBootAfterDeviceChange = _preferences->getBool(preference_ntw_reconfigure, false); _firstBootAfterDeviceChange = _preferences->getBool(preference_ntw_reconfigure, false);
@@ -95,7 +95,7 @@ void NukiNetwork::setupDevice()
if(strcmp(WiFi_fallbackDetect, "wifi_fallback") == 0) if(strcmp(WiFi_fallbackDetect, "wifi_fallback") == 0)
{ {
#ifndef CONFIG_IDF_TARGET_ESP32H2 #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.")); Log->println(F("Failed to connect to network. Wi-Fi fallback is disabled, rebooting."));
memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect)); memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect));
@@ -122,7 +122,7 @@ void NukiNetwork::setupDevice()
_device = NetworkDeviceInstantiator::Create(_networkDeviceType, _hostname, _preferences, _ipConfiguration); _device = NetworkDeviceInstantiator::Create(_networkDeviceType, _hostname, _preferences, _ipConfiguration);
Log->print(F("Network device: ")); Log->print(F("Network device: "));
Log->print(_device->deviceName()); Log->println(_device->deviceName());
} }
void NukiNetwork::reconfigureDevice() void NukiNetwork::reconfigureDevice()
@@ -130,6 +130,16 @@ void NukiNetwork::reconfigureDevice()
_device->reconfigure(); _device->reconfigure();
} }
void NukiNetwork::scan(bool passive, bool async)
{
_device->scan(passive, async);
}
bool NukiNetwork::isApOpen()
{
return _device->isApOpen();
}
const String NukiNetwork::networkDeviceName() const const String NukiNetwork::networkDeviceName() const
{ {
return _device->deviceName(); return _device->deviceName();
@@ -317,7 +327,6 @@ void NukiNetwork::readSettings()
{ {
_restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect, false); _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect, false);
_checkUpdates = _preferences->getBool(preference_check_updates, 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; _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval, 0) * 1000;
if(_rssiPublishInterval == 0) if(_rssiPublishInterval == 0)
@@ -341,38 +350,17 @@ bool NukiNetwork::update()
int64_t ts = (esp_timer_get_time() / 1000); int64_t ts = (esp_timer_get_time() / 1000);
_device->update(); _device->update();
if(!_mqttEnabled) if(!_mqttEnabled || _device->isApOpen())
{ {
return true; return true;
} }
if(!_device->isConnected() || (_mqttConnectCounter > 15 && _reconnectNetworkOnMqttDisconnect && !_firstConnect)) if(!_device->isConnected() || (_mqttConnectCounter > 15 && !_firstConnect))
{ {
_mqttConnectCounter = 0; _mqttConnectCounter = 0;
if(!_webEnabled) forceEnableWebServer = true; if(!_webEnabled) forceEnableWebServer = true;
if(_restartOnDisconnect && (esp_timer_get_time() / 1000) > 60000) restartEsp(RestartReason::RestartOnDisconnectWatchdog); 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) if(_device->isConnected() && !_mqttClientInitiated && strcmp(_mqttBrokerAddr, "") != 0)

View File

@@ -24,6 +24,8 @@ public:
void readSettings(); void readSettings();
bool update(); bool update();
void reconfigureDevice(); void reconfigureDevice();
void scan(bool passive = false, bool async = true);
bool isApOpen();
void clearWifiFallback(); void clearWifiFallback();
const String networkDeviceName() const; const String networkDeviceName() const;
@@ -168,7 +170,6 @@ private:
std::vector<MqttReceiver*> _mqttReceivers; std::vector<MqttReceiver*> _mqttReceivers;
bool _restartOnDisconnect = false; bool _restartOnDisconnect = false;
bool _checkUpdates = false; bool _checkUpdates = false;
bool _reconnectNetworkOnMqttDisconnect = false;
bool _firstConnect = true; bool _firstConnect = true;
bool _publishDebugInfo = false; bool _publishDebugInfo = false;
bool _logIp = true; bool _logIp = true;

View File

@@ -1772,21 +1772,21 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value)
String allowedFromTime; String allowedFromTime;
String allowedUntilTime; String allowedUntilTime;
if(json.containsKey("code")) code = json["code"].as<unsigned int>(); if(json["code"].is<unsigned int>()) code = json["code"].as<unsigned int>();
else code = 12; else code = 12;
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>(); if(json["enabled"].is<unsigned int>()) enabled = json["enabled"].as<unsigned int>();
else enabled = 2; else enabled = 2;
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>(); if(json["timeLimited"].is<unsigned int>()) timeLimited = json["timeLimited"].as<unsigned int>();
else timeLimited = 2; else timeLimited = 2;
if(json.containsKey("name")) name = json["name"].as<String>(); if(json["name"].is<String>()) name = json["name"].as<String>();
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>(); if(json["allowedFrom"].is<String>()) allowedFrom = json["allowedFrom"].as<String>();
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>(); if(json["allowedUntil"].is<String>()) allowedUntil = json["allowedUntil"].as<String>();
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>(); if(json["allowedWeekdays"].is<String>()) allowedWeekdays = json["allowedWeekdays"].as<String>();
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>(); if(json["allowedFromTime"].is<String>()) allowedFromTime = json["allowedFromTime"].as<String>();
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>(); if(json["allowedUntilTime"].is<String>()) allowedUntilTime = json["allowedUntilTime"].as<String>();
if(action) if(action)
{ {
@@ -2209,12 +2209,12 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
String lockAction; String lockAction;
NukiOpener::LockAction timeControlLockAction; NukiOpener::LockAction timeControlLockAction;
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>(); if(json["enabled"].is<unsigned int>()) enabled = json["enabled"].as<unsigned int>();
else enabled = 2; else enabled = 2;
if(json.containsKey("weekdays")) weekdays = json["weekdays"].as<String>(); if(json["weekdays"].is<String>()) weekdays = json["weekdays"].as<String>();
if(json.containsKey("time")) time = json["time"].as<String>(); if(json["time"].is<String>()) time = json["time"].as<String>();
if(json.containsKey("lockAction")) lockAction = json["lockAction"].as<String>(); if(json["lockAction"].is<String>()) lockAction = json["lockAction"].as<String>();
if(lockAction.length() > 0) if(lockAction.length() > 0)
{ {
@@ -2443,22 +2443,22 @@ void NukiOpenerWrapper::onAuthCommandReceived(const char *value)
String allowedFromTime; String allowedFromTime;
String allowedUntilTime; String allowedUntilTime;
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>(); if(json["remoteAllowed"].is<unsigned int>()) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
else remoteAllowed = 2; else remoteAllowed = 2;
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>(); if(json["enabled"].is<unsigned int>()) enabled = json["enabled"].as<unsigned int>();
else enabled = 2; else enabled = 2;
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>(); if(json["timeLimited"].is<unsigned int>()) timeLimited = json["timeLimited"].as<unsigned int>();
else timeLimited = 2; else timeLimited = 2;
if(json.containsKey("name")) name = json["name"].as<String>(); if(json["name"].is<String>()) name = json["name"].as<String>();
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>(); //if(json["sharedKey"].is<String>()) sharedKey = json["sharedKey"].as<String>();
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>(); if(json["allowedFrom"].is<String>()) allowedFrom = json["allowedFrom"].as<String>();
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>(); if(json["allowedUntil"].is<String>()) allowedUntil = json["allowedUntil"].as<String>();
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>(); if(json["allowedWeekdays"].is<String>()) allowedWeekdays = json["allowedWeekdays"].as<String>();
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>(); if(json["allowedFromTime"].is<String>()) allowedFromTime = json["allowedFromTime"].as<String>();
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>(); if(json["allowedUntilTime"].is<String>()) allowedUntilTime = json["allowedUntilTime"].as<String>();
if(action) if(action)
{ {

View File

@@ -1,3 +1,6 @@
#ifndef CONFIG_IDF_TARGET_ESP32H2
#include "esp_wifi.h"
#endif
#include "NukiWrapper.h" #include "NukiWrapper.h"
#include "PreferencesKeys.h" #include "PreferencesKeys.h"
#include "MqttTopics.h" #include "MqttTopics.h"
@@ -58,8 +61,24 @@ void NukiWrapper::initialize(const bool& firstStart)
if(firstStart) if(firstStart)
{ {
Log->println("First start, setting preference defaults"); 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_check_updates, true);
_preferences->putBool(preference_opener_continuous_mode, false); _preferences->putBool(preference_opener_continuous_mode, false);
_preferences->putBool(preference_official_hybrid_enabled, false); _preferences->putBool(preference_official_hybrid_enabled, false);
@@ -1894,21 +1913,21 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value)
String allowedFromTime; String allowedFromTime;
String allowedUntilTime; String allowedUntilTime;
if(json.containsKey("code")) code = json["code"].as<unsigned int>(); if(json["code"].is<unsigned int>()) code = json["code"].as<unsigned int>();
else code = 12; else code = 12;
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>(); if(json["enabled"].is<unsigned int>()) enabled = json["enabled"].as<unsigned int>();
else enabled = 2; else enabled = 2;
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>(); if(json["timeLimited"].is<unsigned int>()) timeLimited = json["timeLimited"].as<unsigned int>();
else timeLimited = 2; else timeLimited = 2;
if(json.containsKey("name")) name = json["name"].as<String>(); if(json["name"].is<String>()) name = json["name"].as<String>();
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>(); if(json["allowedFrom"].is<String>()) allowedFrom = json["allowedFrom"].as<String>();
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>(); if(json["allowedUntil"].is<String>()) allowedUntil = json["allowedUntil"].as<String>();
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>(); if(json["allowedWeekdays"].is<String>()) allowedWeekdays = json["allowedWeekdays"].as<String>();
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>(); if(json["allowedFromTime"].is<String>()) allowedFromTime = json["allowedFromTime"].as<String>();
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>(); if(json["allowedUntilTime"].is<String>()) allowedUntilTime = json["allowedUntilTime"].as<String>();
if(action) if(action)
{ {
@@ -2331,12 +2350,12 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
String lockAction; String lockAction;
NukiLock::LockAction timeControlLockAction; NukiLock::LockAction timeControlLockAction;
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>(); if(json["enabled"].is<unsigned int>()) enabled = json["enabled"].as<unsigned int>();
else enabled = 2; else enabled = 2;
if(json.containsKey("weekdays")) weekdays = json["weekdays"].as<String>(); if(json["weekdays"].is<String>()) weekdays = json["weekdays"].as<String>();
if(json.containsKey("time")) time = json["time"].as<String>(); if(json["time"].is<String>()) time = json["time"].as<String>();
if(json.containsKey("lockAction")) lockAction = json["lockAction"].as<String>(); if(json["lockAction"].is<String>()) lockAction = json["lockAction"].as<String>();
if(lockAction.length() > 0) if(lockAction.length() > 0)
{ {
@@ -2567,22 +2586,22 @@ void NukiWrapper::onAuthCommandReceived(const char *value)
String allowedFromTime; String allowedFromTime;
String allowedUntilTime; String allowedUntilTime;
if(json.containsKey("remoteAllowed")) remoteAllowed = json["remoteAllowed"].as<unsigned int>(); if(json["remoteAllowed"].is<unsigned int>()) remoteAllowed = json["remoteAllowed"].as<unsigned int>();
else remoteAllowed = 2; else remoteAllowed = 2;
if(json.containsKey("enabled")) enabled = json["enabled"].as<unsigned int>(); if(json["enabled"].is<unsigned int>()) enabled = json["enabled"].as<unsigned int>();
else enabled = 2; else enabled = 2;
if(json.containsKey("timeLimited")) timeLimited = json["timeLimited"].as<unsigned int>(); if(json["timeLimited"].is<unsigned int>()) timeLimited = json["timeLimited"].as<unsigned int>();
else timeLimited = 2; else timeLimited = 2;
if(json.containsKey("name")) name = json["name"].as<String>(); if(json["name"].is<String>()) name = json["name"].as<String>();
//if(json.containsKey("sharedKey")) sharedKey = json["sharedKey"].as<String>(); //if(json["sharedKey"].is<String>()) sharedKey = json["sharedKey"].as<String>();
if(json.containsKey("allowedFrom")) allowedFrom = json["allowedFrom"].as<String>(); if(json["allowedFrom"].is<String>()) allowedFrom = json["allowedFrom"].as<String>();
if(json.containsKey("allowedUntil")) allowedUntil = json["allowedUntil"].as<String>(); if(json["allowedUntil"].is<String>()) allowedUntil = json["allowedUntil"].as<String>();
if(json.containsKey("allowedWeekdays")) allowedWeekdays = json["allowedWeekdays"].as<String>(); if(json["allowedWeekdays"].is<String>()) allowedWeekdays = json["allowedWeekdays"].as<String>();
if(json.containsKey("allowedFromTime")) allowedFromTime = json["allowedFromTime"].as<String>(); if(json["allowedFromTime"].is<String>()) allowedFromTime = json["allowedFromTime"].as<String>();
if(json.containsKey("allowedUntilTime")) allowedUntilTime = json["allowedUntilTime"].as<String>(); if(json["allowedUntilTime"].is<String>()) allowedUntilTime = json["allowedUntilTime"].as<String>();
if(action) if(action)
{ {

View File

@@ -52,9 +52,10 @@
#define preference_update_from_mqtt (char*)"updMqtt" #define preference_update_from_mqtt (char*)"updMqtt"
#define preference_disable_non_json (char*)"disnonjson" #define preference_disable_non_json (char*)"disnonjson"
#define preference_official_hybrid_enabled (char*)"offHybrid" #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 // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT
#define preference_find_best_rssi (char*)"nwbestrssi"
#define preference_ntw_reconfigure (char*)"ntwRECONF" #define preference_ntw_reconfigure (char*)"ntwRECONF"
#define preference_auth_max_entries (char*)"authmaxentry" #define preference_auth_max_entries (char*)"authmaxentry"
#define preference_auth_info_enabled (char*)"authInfoEna" #define preference_auth_info_enabled (char*)"authInfoEna"
@@ -88,14 +89,12 @@
#define preference_command_retry_delay (char*)"rtryDelay" #define preference_command_retry_delay (char*)"rtryDelay"
#define preference_query_interval_hybrid_lockstate (char*)"hybridTimer" #define preference_query_interval_hybrid_lockstate (char*)"hybridTimer"
#define preference_mqtt_hass_cu_url (char*)"hassConfigUrl" #define preference_mqtt_hass_cu_url (char*)"hassConfigUrl"
#define preference_network_wifi_fallback_disabled (char*)"nwwififb"
#define preference_check_updates (char*)"checkupdates" #define preference_check_updates (char*)"checkupdates"
#define preference_opener_continuous_mode (char*)"openercont" #define preference_opener_continuous_mode (char*)"openercont"
#define preference_rssi_publish_interval (char*)"rssipb" #define preference_rssi_publish_interval (char*)"rssipb"
#define preference_network_timeout (char*)"nettmout" #define preference_network_timeout (char*)"nettmout"
#define preference_restart_on_disconnect (char*)"restdisc" #define preference_restart_on_disconnect (char*)"restdisc"
#define preference_publish_debug_info (char*)"pubdbg" #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_actions (char*)"hybridAct"
#define preference_official_hybrid_retry (char*)"hybridRtry" #define preference_official_hybrid_retry (char*)"hybridRtry"
@@ -118,12 +117,14 @@
#define preference_lock_max_timecontrol_entry_count (char*)"maxtc" #define preference_lock_max_timecontrol_entry_count (char*)"maxtc"
#define preference_opener_max_timecontrol_entry_count (char*)"opmaxtc" #define preference_opener_max_timecontrol_entry_count (char*)"opmaxtc"
#define preference_latest_version (char*)"latest" #define preference_latest_version (char*)"latest"
#define preference_wifi_converted (char*)"wifiConv"
//OBSOLETE //OBSOLETE
#define preference_access_level (char*)"accLvl" #define preference_access_level (char*)"accLvl"
#define preference_gpio_locking_enabled (char*)"gpiolck" #define preference_gpio_locking_enabled (char*)"gpiolck"
#define preference_network_hardware_gpio (char*)"nwhwdt" #define preference_network_hardware_gpio (char*)"nwhwdt"
#define preference_presence_detection_timeout (char*)"prdtimeout" #define preference_presence_detection_timeout (char*)"prdtimeout"
#define preference_network_wifi_fallback_disabled (char*)"nwwififb"
inline bool initPreferences(Preferences* preferences) inline bool initPreferences(Preferences* preferences)
{ {
@@ -248,9 +249,15 @@ inline bool initPreferences(Preferences* preferences)
if (configVer < 901) if (configVer < 901)
{ {
#if defined(CONFIG_IDF_TARGET_ESP32S3) #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 #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); 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_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_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_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_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware,
preference_rssi_publish_interval, preference_hostname, preference_find_best_rssi, preference_network_timeout, preference_restart_on_disconnect, 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_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_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, 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_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_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_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_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_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_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<char*> _redact = std::vector<char*> _redact =
{ {
preference_mqtt_user, preference_mqtt_password, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_cred_user, preference_cred_password, 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<char*> _boolPrefs = std::vector<char*> _boolPrefs =
{ {
preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode, 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_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_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_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 preference_ntw_reconfigure
}; };
std::vector<char*> _bytePrefs = std::vector<char*> _bytePrefs =

View File

@@ -33,12 +33,9 @@ enum class RestartReason
extern int restartReason; extern int restartReason;
extern uint64_t restartReasonValidDetect; extern uint64_t restartReasonValidDetect;
extern bool rebuildGpioRequested; extern bool rebuildGpioRequested;
extern RestartReason currentRestartReason; extern RestartReason currentRestartReason;
extern bool restartReason_isValid; extern bool restartReason_isValid;
inline static void restartEsp(RestartReason reason) inline static void restartEsp(RestartReason reason)
{ {
if(reason == RestartReason::GpioConfigurationUpdated) if(reason == RestartReason::GpioConfigurationUpdated)

View File

@@ -9,6 +9,7 @@
#endif #endif
#ifndef CONFIG_IDF_TARGET_ESP32H2 #ifndef CONFIG_IDF_TARGET_ESP32H2
#include <esp_wifi.h> #include <esp_wifi.h>
#include <WiFi.h>
#endif #endif
#include <Update.h> #include <Update.h>
@@ -77,12 +78,22 @@ void WebCfgServer::initialize()
{ {
_psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request){ _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."); 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 if(!_network->isApOpen())
return buildHtml(request); {
#else #ifndef NUKI_HUB_UPDATER
return buildOtaHtml(request); return buildHtml(request);
#else
return buildOtaHtml(request);
#endif
}
#ifndef CONFIG_IDF_TARGET_ESP32H2
else
{
return buildWifiConnectHtml(request);
}
#endif #endif
}); });
_psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request){ _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."); 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); 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."); 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); 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){ _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."); 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); esp_err_t res = buildConfirmHtml(request, "Rebooting", 2, true);
@@ -220,50 +109,371 @@ void WebCfgServer::initialize()
restartEsp(RestartReason::RequestedViaWebServer); restartEsp(RestartReason::RequestedViaWebServer);
return res; 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(); if(_network->isApOpen())
updateHandler->onUpload([&](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final) {
{ #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."); 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(connected)
if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); {
waitAndProcess(true, 3000);
String result; restartEsp(RestartReason::ReconfigureWifi);
if (!Update.hasError()) //abort();
{ }
Log->print("Update code or data OK Update.errorString() ");
Log->println(Update.errorString());
result = "<b style='color:green'>Update OK.</b>";
esp_err_t res = request->reply(200,"text/html",result.c_str());
restartEsp(RestartReason::OTACompleted);
return res; return res;
} });
else { #endif
result = " Update.errorString() " + String(Update.errorString()); }
Log->print("ERROR : error "); else
Log->println(result.c_str()); {
esp_err_t res = request->reply(500, "text/html", result.c_str()); #ifndef NUKI_HUB_UPDATER
restartEsp(RestartReason::OTAAborted); _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; 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); PsychicUploadHandler *updateHandler = new PsychicUploadHandler();
//Update.onProgress(printProgress); 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 = "<b style='color:green'>Update OK.</b>";
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("<tr class=\"trssid\" onclick=\"document.getElementById('inputssid').value = '" + _ssidList[i] + "';\"><td colspan=\"2\">" + _ssidList[i] + String(F(" (")) + String(_rssiList[i]) + String(F(" %)")) + "</td></tr>");
}
return response.endSend();
}
void WebCfgServer::createSsidList()
{
int _foundNetworks = WiFi.scanComplete();
std::vector<String> _tmpSsidList;
std::vector<int> _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 = "<style>.trssid:hover { cursor: pointer; color: blue; }</style><script>let intervalId; window.onload = function() { intervalId = setInterval(updateSSID, 3000); }; function updateSSID() { var request = new XMLHttpRequest(); request.open('GET', '/ssidlist', true); request.onload = () => { if (document.getElementById(\"aplist\") !== null) { document.getElementById(\"aplist\").innerHTML = request.responseText; } }; request.send(); }</script>";
PsychicStreamResponse response(request, "text/plain");
response.beginSend();
buildHtmlHeader(&response, header);
response.print("<h3>Available WiFi networks</h3>");
response.print("<table id=\"aplist\">");
createSsidList();
for (int i = 0; i < _ssidList.size(); i++)
{
response.print("<tr class=\"trssid\" onclick=\"document.getElementById('inputssid').value = '" + _ssidList[i] + "';\"><td colspan=\"2\">" + _ssidList[i] + String(F(" (")) + String(_rssiList[i]) + String(F(" %)")) + "</td></tr>");
}
response.print("</table>");
response.print("<form class=\"adapt\" method=\"post\" action=\"savewifi\">");
response.print("<h3>WiFi credentials</h3>");
response.print("<table>");
printInputField(&response, "WIFISSID", "SSID", "", 32, "id=\"inputssid\"", false, true);
printInputField(&response, "WIFIPASS", "Secret key", "", 63, "id=\"inputpass\"", false, true);
response.print("</table>");
response.print("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
response.print("</form>");
response.print("<form action=\"/reboot\" method=\"get\"><br><input type=\"submit\" value=\"Reboot\" /></form>");
response.print("</body></html>");
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<br/>";
}
else
{
message = "Connection successful. Rebooting Nuki Hub.<br/>";
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<br/>";
}
return res;
}
#endif
esp_err_t WebCfgServer::buildOtaHtml(PsychicRequest *request, bool debug) esp_err_t WebCfgServer::buildOtaHtml(PsychicRequest *request, bool debug)
{ {
PsychicStreamResponse response(request, "text/plain"); PsychicStreamResponse response(request, "text/plain");
@@ -648,6 +858,62 @@ String WebCfgServer::generateConfirmCode()
return String(code); 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("<tr><td>");
response->print(description);
if(showLengthRestriction)
{
response->print(" (Max. ");
response->print(maxLength);
response->print(" characters)");
}
response->print("</td><td>");
response->print("<input type=");
response->print(isPassword ? "\"password\"" : "\"text\"");
if(strcmp(args, "") != 0)
{
response->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("</td></tr>");
}
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 #ifndef NUKI_HUB_UPDATER
esp_err_t WebCfgServer::sendSettings(PsychicRequest *request) esp_err_t WebCfgServer::sendSettings(PsychicRequest *request)
{ {
@@ -1162,16 +1428,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, String& message)
//configChanged = true; //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") else if(key == "HOSTNAME")
{ {
if(_preferences->getString(preference_hostname, "") != value) if(_preferences->getString(preference_hostname, "") != value)
@@ -1202,16 +1458,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, String& message)
//configChanged = true; //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") else if(key == "MQTTLOG")
{ {
if(_preferences->getBool(preference_mqtt_log_enabled, false) != (value == "1")) 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); 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(), ""); printDropDown(&response, "NWHW", "Network hardware", String(_preferences->getInt(preference_network_hardware)), getNetworkDetectionOptions(), "");
#ifndef CONFIG_IDF_TARGET_ESP32H2 #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, ""); printInputField(&response, "RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6, "");
#endif #endif
printInputField(&response, "NETTIMEOUT", "MQTT Timeout until restart (seconds; -1 to disable)", _preferences->getInt(preference_network_timeout), 5, ""); 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, "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, "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, "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), ""); 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) switch(value)
{ {
case 0: case 0:
return (String)"PIN not set"; return String("PIN not set");
case 1: case 1:
return (String)"PIN valid"; return String("PIN valid");
case 2: case 2:
return (String)"PIN set but invalid";; return String("PIN set but invalid");
default: default:
return (String)"Unknown"; return String("Unknown");
} }
} }
@@ -3230,7 +3473,7 @@ esp_err_t WebCfgServer::buildConfigureWifiHtml(PsychicRequest *request)
response.beginSend(); response.beginSend();
buildHtmlHeader(&response); buildHtmlHeader(&response);
response.print("<h3>Wi-Fi</h3>"); response.print("<h3>Wi-Fi</h3>");
response.print("Click confirm to restart ESP into Wi-Fi configuration mode. After restart, connect to ESP access point to reconfigure Wi-Fi.<br><br>"); 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.<br><br>");
buildNavigationButton(&response, "Confirm", "/wifimanager"); buildNavigationButton(&response, "Confirm", "/wifimanager");
response.print("</body></html>"); response.print("</body></html>");
return response.endSend(); return response.endSend();
@@ -3366,12 +3609,8 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request)
} }
#ifndef CONFIG_IDF_TARGET_ESP32H2 #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") 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): "); response.print("\nRSSI Publish interval (s): ");
if(_preferences->getInt(preference_rssi_publish_interval, 60) < 0) response.print("Disabled"); if(_preferences->getInt(preference_rssi_publish_interval, 60) < 0) response.print("Disabled");
@@ -3380,8 +3619,6 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request)
#endif #endif
response.print("\nRestart ESP32 on network disconnect enabled: "); response.print("\nRestart ESP32 on network disconnect enabled: ");
response.print(_preferences->getBool(preference_restart_on_disconnect, false) ? "Yes" : "No"); 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): "); response.print("\nMQTT Timeout until restart (s): ");
if(_preferences->getInt(preference_network_timeout, 60) < 0) response.print("Disabled"); if(_preferences->getInt(preference_network_timeout, 60) < 0) response.print("Disabled");
else response.print(_preferences->getInt(preference_network_timeout, 60)); 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 #ifndef CONFIG_IDF_TARGET_ESP32H2
if(resetWifi) if(resetWifi)
{ {
wifi_config_t current_conf;
esp_wifi_get_config((wifi_interface_t)ESP_IF_WIFI_STA, &current_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, &current_conf);
_network->reconfigureDevice(); _network->reconfigureDevice();
} }
#endif #endif
@@ -3981,62 +4213,6 @@ esp_err_t WebCfgServer::processFactoryReset(PsychicRequest *request)
return res; 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("<tr><td>");
response->print(description);
if(showLengthRestriction)
{
response->print(" (Max. ");
response->print(maxLength);
response->print(" characters)");
}
response->print("</td><td>");
response->print("<input type=");
response->print(isPassword ? "\"password\"" : "\"text\"");
if(strcmp(args, "") != 0)
{
response->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("</td></tr>");
}
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) void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *token, const char *description, const bool value, const char *htmlClass)
{ {
response->print("<tr><td>"); response->print("<tr><td>");
@@ -4183,7 +4359,7 @@ const std::vector<std::pair<String, String>> WebCfgServer::getNetworkDetectionOp
{ {
std::vector<std::pair<String, String>> options; std::vector<std::pair<String, String>> 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("2", "Generic W5500"));
options.push_back(std::make_pair("3", "M5Stack Atom POE (W5500)")); options.push_back(std::make_pair("3", "M5Stack Atom POE (W5500)"));
options.push_back(std::make_pair("10", "M5Stack Atom POE S3 (W5500)")); options.push_back(std::make_pair("10", "M5Stack Atom POE S3 (W5500)"));

View File

@@ -55,7 +55,7 @@ private:
esp_err_t buildCredHtml(PsychicRequest *request); esp_err_t buildCredHtml(PsychicRequest *request);
esp_err_t buildImportExportHtml(PsychicRequest *request); esp_err_t buildImportExportHtml(PsychicRequest *request);
esp_err_t buildMqttConfigHtml(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 buildAdvancedConfigHtml(PsychicRequest *request);
esp_err_t buildNukiConfigHtml(PsychicRequest *request); esp_err_t buildNukiConfigHtml(PsychicRequest *request);
esp_err_t buildGpioConfigHtml(PsychicRequest *request); esp_err_t buildGpioConfigHtml(PsychicRequest *request);
@@ -67,8 +67,6 @@ private:
esp_err_t processUnpair(PsychicRequest *request, bool opener); esp_err_t processUnpair(PsychicRequest *request, bool opener);
esp_err_t processUpdate(PsychicRequest *request); esp_err_t processUpdate(PsychicRequest *request);
esp_err_t processFactoryReset(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 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 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<std::pair<String, String>> options, const String className); void printDropDown(PsychicStreamResponse *response, const char *token, const char *description, const String preselectedValue, std::vector<std::pair<String, String>> options, const String className);
@@ -94,19 +92,30 @@ private:
bool _brokerConfigured = false; bool _brokerConfigured = false;
bool _rebootRequired = false; bool _rebootRequired = false;
#endif #endif
std::vector<String> _ssidList;
std::vector<int> _rssiList;
String generateConfirmCode(); String generateConfirmCode();
String _confirmCode = "----"; 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 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 buildOtaHtml(PsychicRequest *request, bool debug = false);
esp_err_t buildOtaCompletedHtml(PsychicRequest *request); esp_err_t buildOtaCompletedHtml(PsychicRequest *request);
esp_err_t sendCss(PsychicRequest *request); esp_err_t sendCss(PsychicRequest *request);
esp_err_t sendFavicon(PsychicRequest *request); esp_err_t sendFavicon(PsychicRequest *request);
void createSsidList();
void buildHtmlHeader(PsychicStreamResponse *response, String additionalHeader = ""); void buildHtmlHeader(PsychicStreamResponse *response, String additionalHeader = "");
void waitAndProcess(const bool blocking, const uint32_t duration); 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); 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); 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; PsychicHttpServer* _psychicServer = nullptr;
NukiNetwork* _network = nullptr; NukiNetwork* _network = nullptr;
Preferences* _preferences = nullptr; Preferences* _preferences = nullptr;

View File

@@ -5,7 +5,7 @@
#include "esp_ota_ops.h" #include "esp_ota_ops.h"
#include "esp_http_client.h" #include "esp_http_client.h"
#include "esp_https_ota.h" #include "esp_https_ota.h"
#include <esp_task_wdt.h> #include "esp_task_wdt.h"
#include "Config.h" #include "Config.h"
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
@@ -78,7 +78,6 @@ TaskHandle_t networkTaskHandle = nullptr;
ssize_t write_fn(void* cookie, const char* buf, ssize_t size) ssize_t write_fn(void* cookie, const char* buf, ssize_t size)
{ {
Log->write((uint8_t *)buf, (size_t)size); Log->write((uint8_t *)buf, (size_t)size);
return size; return size;
} }
@@ -104,7 +103,7 @@ int _log_vprintf(const char *fmt, va_list args) {
void setReroute(){ void setReroute(){
esp_log_set_vprintf(_log_vprintf); 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("*", ESP_LOG_INFO);
esp_log_level_set("mqtt", ESP_LOG_NONE); 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); xTaskCreatePinnedToCore(networkTask, "ntw", preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), NULL, 3, &networkTaskHandle, 1);
esp_task_wdt_add(networkTaskHandle); esp_task_wdt_add(networkTaskHandle);
#ifndef NUKI_HUB_UPDATER #ifndef NUKI_HUB_UPDATER
xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 0); if(!network->isApOpen())
esp_task_wdt_add(nukiTaskHandle); {
xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 0);
esp_task_wdt_add(nukiTaskHandle);
}
#endif #endif
} }
} }
@@ -434,7 +436,7 @@ void setup()
webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer); webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer);
webCfgServer->initialize(); webCfgServer->initialize();
psychicServer->listen(80); psychicServer->listen(80);
psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/"); }); psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/"); });
} }
#else #else
Log->print(F("Nuki Hub version ")); Log->print(F("Nuki Hub version "));
@@ -453,7 +455,6 @@ void setup()
} }
char16_t buffer_size = preferences->getInt(preference_buffer_size, 4096); char16_t buffer_size = preferences->getInt(preference_buffer_size, 4096);
CharBuffer::initialize(buffer_size); CharBuffer::initialize(buffer_size);
gpio = new Gpio(preferences); gpio = new Gpio(preferences);
@@ -461,22 +462,30 @@ void setup()
gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r"); gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r");
Log->print(gpioDesc.c_str()); 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(); bleScanner = new BleScanner::Scanner();
// Scan interval and window according to Nuki recommendations: // Scan interval and window according to Nuki recommendations:
// https://developer.nuki.io/t/bluetooth-specification-questions/1109/27 // https://developer.nuki.io/t/bluetooth-specification-questions/1109/27
bleScanner->initialize("NukiHub", true, 40, 40); bleScanner->initialize("NukiHub", true, 40, 40);
bleScanner->setScanDuration(0); 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); 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 = new NukiNetworkLock(network, nukiOfficial, preferences, CharBuffer::get(), buffer_size);
networkLock->initialize(); networkLock->initialize();
@@ -533,7 +542,7 @@ void setup()
if(doOta) setupTasks(true); if(doOta) setupTasks(true);
else setupTasks(false); else setupTasks(false);
#ifdef DEBUG_NUKIHUB #ifdef DEBUG_NUKIHUB
Log->print("Task Name\tStatus\tPrio\tHWM\tTask\tAffinity\n"); Log->print("Task Name\tStatus\tPrio\tHWM\tTask\tAffinity\n");
char stats_buffer[1024]; char stats_buffer[1024];

View File

@@ -3,6 +3,9 @@
#include "../Logger.h" #include "../Logger.h"
#include "../RestartReason.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) 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), : NetworkDevice(hostname, ipConfiguration),
_deviceName(deviceName), _deviceName(deviceName),
@@ -54,20 +57,34 @@ const String EthernetDevice::deviceName() const
void EthernetDevice::initialize() void EthernetDevice::initialize()
{ {
delay(250); 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")); Log->println(F("Init Ethernet"));
if(_useSpi) if(_useSpi)
{ {
Log->println(F("Use SPI")); Log->println(F("Use SPI"));
criticalEthFailure = true;
SPI.begin(_spi_sck, _spi_miso, _spi_mosi); SPI.begin(_spi_sck, _spi_miso, _spi_mosi);
_hardwareInitialized = ETH.begin(_type, _phy_addr, _cs, _irq, _rst, SPI); _hardwareInitialized = ETH.begin(_type, _phy_addr, _cs, _irq, _rst, SPI);
criticalEthFailure = false;
} }
#ifdef CONFIG_IDF_TARGET_ESP32 #ifdef CONFIG_IDF_TARGET_ESP32
else else
{ {
Log->println(F("Use RMII")); Log->println(F("Use RMII"));
criticalEthFailure = true;
_hardwareInitialized = ETH.begin(_type, _phy_addr, _mdc, _mdio, _power, _clock_mode); _hardwareInitialized = ETH.begin(_type, _phy_addr, _mdc, _mdio, _power, _clock_mode);
criticalEthFailure = false;
if(!_ipConfiguration->dhcpEnabled()) if(!_ipConfiguration->dhcpEnabled())
{ {
_checkIpTs = (esp_timer_get_time() / 1000) + 2000; _checkIpTs = (esp_timer_get_time() / 1000) + 2000;
@@ -78,6 +95,7 @@ void EthernetDevice::initialize()
if(_hardwareInitialized) if(_hardwareInitialized)
{ {
Log->println(F("Ethernet hardware Initialized")); Log->println(F("Ethernet hardware Initialized"));
memset(WiFi_fallbackDetect, 0, sizeof(WiFi_fallbackDetect));
if(_useSpi && !_ipConfiguration->dhcpEnabled()) if(_useSpi && !_ipConfiguration->dhcpEnabled())
{ {
@@ -92,6 +110,11 @@ void EthernetDevice::initialize()
else else
{ {
Log->println(F("Failed to initialize ethernet hardware")); 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); restartEsp(RestartReason::ReconfigureETH);
} }
void EthernetDevice::scan(bool passive, bool async)
{
}
bool EthernetDevice::isConnected() bool EthernetDevice::isConnected()
{ {
return _connected; return _connected;
} }
ReconnectStatus EthernetDevice::reconnect(bool force) bool EthernetDevice::isApOpen()
{ {
if(!_hardwareInitialized) return false;
{
return ReconnectStatus::CriticalFailure;
}
delay(200);
return isConnected() ? ReconnectStatus::Success : ReconnectStatus::Failure;
} }
void EthernetDevice::onDisconnected() void EthernetDevice::onDisconnected()
{ {
if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog);
reconnect();
} }
int8_t EthernetDevice::signalStrength() int8_t EthernetDevice::signalStrength()

View File

@@ -45,19 +45,19 @@ public:
virtual void initialize(); virtual void initialize();
virtual void reconfigure(); virtual void reconfigure();
virtual void update(); virtual void update();
virtual void scan(bool passive = false, bool async = true);
virtual ReconnectStatus reconnect(bool force = false);
virtual bool isConnected(); virtual bool isConnected();
virtual bool isApOpen();
int8_t signalStrength() override; int8_t signalStrength() override;
String localIP() override; String localIP() override;
String BSSIDstr() override; String BSSIDstr() override;
private: private:
Preferences* _preferences; Preferences* _preferences;
void onDisconnected(); void onDisconnected();
void onNetworkEvent(arduino_event_id_t event, arduino_event_info_t info); void onNetworkEvent(arduino_event_id_t event, arduino_event_info_t info);

View File

@@ -1,13 +1,6 @@
#pragma once #pragma once
#include "IPConfiguration.h" #include "IPConfiguration.h"
enum class ReconnectStatus
{
Failure = 0,
Success = 1,
CriticalFailure = 2
};
class NetworkDevice class NetworkDevice
{ {
public: public:
@@ -19,11 +12,12 @@ public:
virtual const String deviceName() const = 0; virtual const String deviceName() const = 0;
virtual void initialize() = 0; virtual void initialize() = 0;
virtual ReconnectStatus reconnect(bool force = false) = 0;
virtual void reconfigure() = 0; virtual void reconfigure() = 0;
virtual void update(); virtual void update();
virtual void scan(bool passive = false, bool async = true) = 0;
virtual bool isConnected() = 0; virtual bool isConnected() = 0;
virtual bool isApOpen() = 0;
virtual int8_t signalStrength() = 0; virtual int8_t signalStrength() = 0;
virtual String localIP() = 0; virtual String localIP() = 0;

View File

@@ -1,17 +1,14 @@
#include "esp_wifi.h"
#include <WiFi.h> #include <WiFi.h>
#include "WifiDevice.h" #include "WifiDevice.h"
#include "../PreferencesKeys.h" #include "../PreferencesKeys.h"
#include "../Logger.h" #include "../Logger.h"
#include "../RestartReason.h" #include "../RestartReason.h"
RTC_NOINIT_ATTR char WiFiDevice_reconfdetect[17];
WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const IPConfiguration* ipConfiguration) WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const IPConfiguration* ipConfiguration)
: NetworkDevice(hostname, ipConfiguration), : NetworkDevice(hostname, ipConfiguration),
_preferences(preferences), _preferences(preferences)
_wm(preferences->getString(preference_cred_user, "").c_str(), preferences->getString(preference_cred_password, "").c_str())
{ {
_startAp = strcmp(WiFiDevice_reconfdetect, "reconfigure_wifi") == 0;
} }
const String WifiDevice::deviceName() const const String WifiDevice::deviceName() const
@@ -21,123 +18,399 @@ const String WifiDevice::deviceName() const
void WifiDevice::initialize() void WifiDevice::initialize()
{ {
std::vector<const char *> wm_menu; String ssid = _preferences->getString(preference_wifi_ssid, "");
wm_menu.push_back("wifi"); String pass = _preferences->getString(preference_wifi_pass, "");
wm_menu.push_back("exit"); WiFi.setHostname(_hostname.c_str());
_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);
if(!_ipConfiguration->dhcpEnabled()) if(!_ipConfiguration->dhcpEnabled())
{ {
_wm.setSTAStaticIPConfig(_ipConfiguration->ipAddress(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet(), _ipConfiguration->dnsServer()); WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet());
}
_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.onEvent([&](WiFiEvent_t event, WiFiEventInfo_t info) WiFi.onEvent([&](WiFiEvent_t event, WiFiEventInfo_t info)
{ {
if(event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) 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(); 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<const char*>(wifi_cfg.sta.ssid));
String tempPass = String(reinterpret_cast<const char*>(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() 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); delay(200);
restartEsp(RestartReason::ReconfigureWifi); restartEsp(RestartReason::ReconfigureWifi);
} }
bool WifiDevice::isConnected() bool WifiDevice::isConnected()
{ {
return WiFi.isConnected(); return (WiFi.status() == WL_CONNECTED);
}
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;
} }
void WifiDevice::onConnected() void WifiDevice::onConnected()
{ {
_isReconnecting = false; Log->println(F("Wi-Fi connected"));
_wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); _connectedChannel = WiFi.channel();
_connectedBSSID = WiFi.BSSID();
_connected = true;
} }
void WifiDevice::onDisconnected() void WifiDevice::onDisconnected()
{ {
_disconnectTs = (esp_timer_get_time() / 1000); if(_connected)
if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); {
_wm.setEnableConfigPortal(false); _connected = false;
reconnect(); _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() int8_t WifiDevice::signalStrength()
@@ -155,7 +428,7 @@ String WifiDevice::BSSIDstr()
return WiFi.BSSIDstr(); return WiFi.BSSIDstr();
} }
void WifiDevice::clearRtcInitVar(WiFiManager *) bool WifiDevice::isApOpen()
{ {
memset(WiFiDevice_reconfdetect, 0, sizeof WiFiDevice_reconfdetect); return _openAP;
} }

View File

@@ -4,7 +4,6 @@
#include <NetworkClientSecure.h> #include <NetworkClientSecure.h>
#include <Preferences.h> #include <Preferences.h>
#include "NetworkDevice.h" #include "NetworkDevice.h"
#include "WiFiManager.h"
#include "IPConfiguration.h" #include "IPConfiguration.h"
class WifiDevice : public NetworkDevice class WifiDevice : public NetworkDevice
@@ -16,25 +15,32 @@ public:
virtual void initialize(); virtual void initialize();
virtual void reconfigure(); virtual void reconfigure();
virtual ReconnectStatus reconnect(bool force = false); virtual void scan(bool passive = false, bool async = true);
virtual bool isConnected(); virtual bool isConnected();
virtual bool isApOpen();
int8_t signalStrength() override; int8_t signalStrength() override;
String localIP() override; String localIP() override;
String BSSIDstr() override; String BSSIDstr() override;
private: private:
static void clearRtcInitVar(WiFiManager*); void openAP();
void onDisconnected(); void onDisconnected();
void onConnected(); void onConnected();
bool connect();
WiFiManager _wm;
Preferences* _preferences = nullptr; Preferences* _preferences = nullptr;
bool _startAp = false; int _foundNetworks = 0;
bool _isReconnecting = false; 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; int64_t _disconnectTs = 0;
}; };

View File

@@ -24,7 +24,8 @@ board_build.embed_txtfiles =
build_type = release build_type = release
custom_build = release custom_build = release
board_build.partitions = partitions.csv board_build.partitions = partitions.csv
build_unflags = build_unflags =
-DESP32
-Werror=all -Werror=all
-Wall -Wall
build_flags = build_flags =
@@ -54,7 +55,7 @@ lib_ignore =
WiFiProv WiFiProv
lib_deps = lib_deps =
PsychicHttp=symlink://../lib/PsychicHttp PsychicHttp=symlink://../lib/PsychicHttp
WiFiManager=symlink://../lib/WiFiManager ArduinoJson=symlink://../lib/ArduinoJson
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = monitor_filters =