Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into feature/AtlasEZOPlugins_pH_EC

This commit is contained in:
Peter Kretz
2021-04-16 17:16:06 +02:00
54 changed files with 1383 additions and 360 deletions
+41
View File
@@ -111,6 +111,47 @@ This is often used to perform the initial configuration like connecting to the l
Can also be set via the command ``WiFiAPKey``.
Custom Build WiFi credentials
-----------------------------
For custom builds, one can set default WiFi credentials by defining some default credentials at build.
See the examples in `Custom-sample.h`:
* ``#define DEFAULT_SSID "MyHomeSSID" // Enter your network SSID``
* ``#define DEFAULT_KEY "MySuperSecretPassword" // Enter your network WPA key``
* ``#define DEFAULT_SSID2 "" // Enter your fallback network SSID``
* ``#define DEFAULT_KEY2 "" // Enter your fallback network WPA key``
Custom builds can also be customized a bit more to allow for a deployment SSID configuration and an emergency fallback.
Deployment & Support SSID will be used only when the configured SSIDs are not reachable and/or no credentials are set.
This to make deployment or support of large number of nodes easier.
This configured set of credentials will be considered a "low priority" set, thus it will be tried as last resort.
Therefore it may take a while for a unit to connect to it if there are lots of 'hidden SSID' APs and connecting to hidden SSIDs is allowed.
* ``#define CUSTOM_DEPLOYMENT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease deployment``
* ``#define CUSTOM_DEPLOYMENT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease deployment``
* ``#define CUSTOM_SUPPORT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease support``
* ``#define CUSTOM_SUPPORT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease support``
Emergency fallback SSID will only be attempted in the first 10 minutes after reboot.
When found, the unit will connect to it and depending on the built in flag, it will either just connect to it, or clear set credentials.
Use case: User connects to a public AP which does need to agree on an agreement page for the rules of conduct (e.g. open APs)
This is seen as a valid connection, so the unit will not reconnect to another node and thus becomes inaccessible.
The AP configured with these fallback credentials will then act as a master key to regain access to a node.
These will never be set in nightly builds and only allowed for custom builds for obvious reasons.
* ``#define CUSTOM_EMERGENCY_FALLBACK_SSID "" // Enter SSID not shown in UI, to be used to regain access to the node``
* ``#define CUSTOM_EMERGENCY_FALLBACK_KEY "" // Enter key not shown in UI, to be used to regain access to the node``
* ``#define CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS false``
* ``#define CUSTOM_EMERGENCY_FALLBACK_START_AP false``
* ``#define CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME 10``
Client IP filtering
===================
+41
View File
@@ -385,6 +385,47 @@ Therefore the default value of +3dB margin will attempt to let the access point
Of course nodes with an already high signal attenuation cannot send with more than the max allowed TX power of roughly 20.5 dBm.
Trying to reach this sweet spot in signal strength is just a best effort and not a guarantee.
Extra WiFi scan loops
^^^^^^^^^^^^^^^^^^^^^
Added: 2021-04-16
A single WiFi scan does loop over all channels only once and waits per channel only for a fixed amount of time for APs to reply.
It is an "active" WiFi scan, meaning the node does send out a packet for access points to reply to.
Per scan, an AP may be too busy handling other traffic so it may not even receive the request, or does not reply in due time and the node already switched over to another channel and thus does not receive the reply from the AP.
This may lead to the situation where a node which is configured to connect to multiple APs, to connect to the least optimal AP as the AP which would be the better choice did not reply.
A scan can be "sync" or "async". A "sync" scan is blocking, meaning it will halt execution of other code on the ESP.
An "async" scan is just started and when finished it fires an event to fetch the scan results and thus is not blocking.
Blocking code may affect timing critical actions, which are sometimes essential to interact with some sensors.
This setting (default = 0) may help in finding the best AP when a sync scan needs to be performed, but it also may block execution of other code over a longer period.
Sync scans are performed when:
* No recent scan results are present and the node needs to (re)connect (thus always at a cold boot)
* When loading the WiFi scanner and setup page with no recent scan results present.
As an alternative, the next setting can be used to perform an async scan every minute and thus prevent blocking code on a reconnect.
Periodical Scan WiFi
^^^^^^^^^^^^^^^^^^^^
Added: 2021-04-16
When checked, the ESP will perform an async scan (see previous setting too) every minute to keep the list of known APs up-to-date.
This has several advantages:
* More likely the best AP will be known when the node needs to reconnect.
* No "sync" scan is needed to reconnect.
* The node remains known among other network devices, so it remains more responsive (see also Gratuitous ARP setting)
The drawback may be that overall the node may consume slightly more energy as it may not enter the low power state when "ECO" mode is enabled.
Also it is yet unknown if it does have a negative impact on overall WiFi performance if a lot of nodes perform Periodical scans. ("a lot" meaning several tens of nodes in a small area)
During a scan the node is listening on a different channel, so it may not respond to requests sent to it for roughly a 1.6 seconds.
Show JSON
+61
View File
@@ -38,6 +38,8 @@
// --- Wifi Client Mode -----------------------------------------------------------------------------
#define DEFAULT_SSID "MyHomeSSID" // Enter your network SSID
#define DEFAULT_KEY "MySuperSecretPassword" // Enter your network WPA key
#define DEFAULT_SSID2 "" // Enter your fallback network SSID
#define DEFAULT_KEY2 "" // Enter your fallback network WPA key
#define DEFAULT_USE_STATIC_IP false // (true|false) enabled or disabled static IP
#define DEFAULT_IP "192.168.0.50" // Enter your IP address
#define DEFAULT_DNS "192.168.0.1" // Enter your DNS
@@ -140,6 +142,28 @@
#define BUILD_NO_DEBUG
// Special SSID/key setup only to be used in custom builds.
// Deployment SSID will be used only when the configured SSIDs are not reachable and/or no credentials are set.
// This to make deployment of large number of nodes easier
#define CUSTOM_DEPLOYMENT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease deployment
#define CUSTOM_DEPLOYMENT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease deployment
#define CUSTOM_SUPPORT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease support
#define CUSTOM_SUPPORT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease support
// Emergency fallback SSID will only be attempted in the first 10 minutes after reboot.
// When found, the unit will connect to it and depending on the built in flag, it will either just connect to it, or clear set credentials.
// Use case: User connects to a public AP which does need to agree on an agreement page for the rules of conduct (e.g. open APs)
// This is seen as a valid connection, so the unit will not reconnect to another node and thus becomes inaccessible.
#define CUSTOM_EMERGENCY_FALLBACK_SSID "" // Enter SSID not shown in UI, to be used to regain access to the node
#define CUSTOM_EMERGENCY_FALLBACK_KEY "" // Enter key not shown in UI, to be used to regain access to the node
#define CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS false
#define CUSTOM_EMERGENCY_FALLBACK_START_AP false
#define CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME 10
#define USES_SSDP
@@ -147,6 +171,43 @@
// #define FEATURE_I2CMULTIPLEXER
// #define USE_TRIGONOMETRIC_FUNCTIONS_RULES
/*
#######################################################################################################
Defining web interface
#######################################################################################################
*/
#define MENU_INDEX_MAIN_VISIBLE true
/*
#define MENU_INDEX_CONFIG_VISIBLE false
#define MENU_INDEX_CONTROLLERS_VISIBLE false
#define MENU_INDEX_HARDWARE_VISIBLE false
#define MENU_INDEX_DEVICES_VISIBLE false
#define MENU_INDEX_RULES_VISIBLE false
#define MENU_INDEX_NOTIFICATIONS_VISIBLE false
#define MENU_INDEX_TOOLS_VISIBLE false
*/
#define MAIN_PAGE_SHOW_SYSINFO_BUTTON true
#define MAIN_PAGE_SHOW_WiFi_SETUP_BUTTON true
#define MAIN_PAGE_SHOW_BASIC_INFO_NOT_LOGGED_IN false
#define SETUP_PAGE_SHOW_CONFIG_BUTTON true
//#define WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON
/*
#######################################################################################################
CSS / template
#######################################################################################################
*/
/*
#define WEBPAGE_TEMPLATE_DEFAULT_HEADER "<header class='headermenu'><h1>ESP Easy Mega: {{title}}</h1><BR>"
#define WEBPAGE_TEMPLATE_DEFAULT_FOOTER "<footer><br><h6>Powered by <a href='http://www.letscontrolit.com' style='font-size: 15px; text-decoration: none'>Let's Control It</a> community</h6></footer></body></html>"
#define WEBPAGE_TEMPLATE_AP_HEADER "<body><header class='apheader'><h1>Welcome to ESP Easy Mega AP</h1>"
#define WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON
*/
/*
#######################################################################################################
+7 -1
View File
@@ -351,7 +351,10 @@ void setup()
// Wait until scan has finished to make sure as many as possible are found
// We're still in the setup phase, so nothing else is taking resources of the ESP.
WifiScan(false);
WiFiEventData.lastScanMoment.clear();
}
// Start an extra async scan so we can continue, but we may find more APs by scanning twice.
WifiScan(true);
}
// setWifiMode(WIFI_STA);
@@ -548,6 +551,9 @@ int getLoopCountPerSec() {
return loopCounterLast / 30;
}
int getUptimeMinutes() {
return wdcounter / 2;
}
@@ -586,7 +592,7 @@ void loop()
sendSysInfoUDP(1);
}
// Work around for nodes that do not have WiFi connection for a long time and may reboot after N unsuccessful connect attempts
if ((wdcounter / 2) > 2) {
if (getUptimeMinutes() > 2) {
// Apparently the uptime is already a few minutes. Let's consider it a successful boot.
RTC.bootFailedCount = 0;
saveToRTC();
+1
View File
@@ -15,6 +15,7 @@ void flushAndDisconnectAllClients();
float getCPUload();
int getLoopCountPerSec();
int getUptimeMinutes();
+1 -1
View File
@@ -181,7 +181,7 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String&
#ifdef CPLUGIN_014_V3
// $stats/uptime Device → Controller Time elapsed in seconds since the boot of the device Yes Yes
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex,"$stats/uptime",toString((wdcounter / 2)*60,0).c_str(),errorCounter);
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex,"$stats/uptime",toString(getUptimeMinutes()*60,0).c_str(),errorCounter);
// $stats/signal Device → Controller Signal strength in % Yes No
float RssI = WiFi.RSSI();
+1 -1
View File
@@ -211,7 +211,7 @@ float P026_get_value(int type)
{
case 0:
{
value = (wdcounter / 2);
value = getUptimeMinutes();
break;
}
case 1:
+1 -1
View File
@@ -34,7 +34,7 @@ void ReportStatus()
root[F("chipId")] = ESP.getChipId();
root[F("flashId")] = ESP.getFlashChipId();
root[F("uptime")] = wdcounter / 2;
root[F("uptime")] = getUptimeMinutes();
String body;
+6
View File
@@ -42,6 +42,12 @@
#ifndef DEFAULT_KEY
#define DEFAULT_KEY "wpakey" // Enter your Wifi network WPA key
#endif
#ifndef DEFAULT_SSID2
#define DEFAULT_SSID2 "" // Enter your fallback Wifi network SSID
#endif
#ifndef DEFAULT_KEY2
#define DEFAULT_KEY2 "" // Enter your fallback Wifi network WPA key
#endif
#ifndef DEFAULT_USE_STATIC_IP
#define DEFAULT_USE_STATIC_IP false // (true|false) enabled or disabled static IP
#endif
+1 -1
View File
@@ -20,7 +20,7 @@
#endif // if defined(ESP32)
#define BUILD 20112 // git version e.g. "20103" can be read as "2.1.03" (stored in int16_t)
#define BUILD 20113 // git version e.g. "20103" can be read as "2.1.03" (stored in int16_t)
#ifndef BUILD_NOTES
#if defined(ESP8266)
# define BUILD_NOTES " - Mega"
+7
View File
@@ -1540,5 +1540,12 @@ To create/register a plugin, you have to :
#endif
#endif
#ifdef WEBSERVER_SETUP
#ifndef FEATURE_DNS_SERVER
#define FEATURE_DNS_SERVER
#endif
#endif
#endif // DEFINE_PLUGIN_SETS_H
+37
View File
@@ -2,6 +2,7 @@
#include "../../ESPEasy_common.h"
#include "../CustomBuild/ESPEasyLimits.h"
#include "../ESPEasyCore/ESPEasy_Log.h"
#include "../Globals/CPlugins.h"
SecurityStruct::SecurityStruct() {
@@ -31,3 +32,39 @@ void SecurityStruct::validate() {
}
ZERO_TERMINATE(Password);
}
void SecurityStruct::clearWiFiCredentials() {
ZERO_FILL(WifiSSID);
ZERO_FILL(WifiKey);
ZERO_FILL(WifiSSID2);
ZERO_FILL(WifiKey2);
addLog(LOG_LEVEL_INFO, F("WiFi : Clear WiFi credentials from settings"));
}
void SecurityStruct::clearWiFiCredentials(SecurityStruct::WiFiCredentialsSlot slot) {
switch (slot) {
case SecurityStruct::WiFiCredentialsSlot::first:
ZERO_FILL(WifiSSID);
ZERO_FILL(WifiKey);
break;
case SecurityStruct::WiFiCredentialsSlot::second:
ZERO_FILL(WifiSSID2);
ZERO_FILL(WifiKey2);
break;
}
}
bool SecurityStruct::hasWiFiCredentials() const {
return hasWiFiCredentials(SecurityStruct::WiFiCredentialsSlot::first) ||
hasWiFiCredentials(SecurityStruct::WiFiCredentialsSlot::second);
}
bool SecurityStruct::hasWiFiCredentials(SecurityStruct::WiFiCredentialsSlot slot) const {
switch (slot) {
case SecurityStruct::WiFiCredentialsSlot::first:
return (WifiSSID[0] != 0 && !String(WifiSSID).equalsIgnoreCase(F("ssid")));
case SecurityStruct::WiFiCredentialsSlot::second:
return (WifiSSID2[0] != 0 && !String(WifiSSID2).equalsIgnoreCase(F("ssid")));
}
return false;
}
+14
View File
@@ -9,10 +9,24 @@
\*********************************************************************************************/
struct SecurityStruct
{
enum class WiFiCredentialsSlot {
first,
second
};
SecurityStruct();
void validate();
void clearWiFiCredentials();
void clearWiFiCredentials(WiFiCredentialsSlot slot);
bool hasWiFiCredentials() const;
bool hasWiFiCredentials(WiFiCredentialsSlot slot) const;
char WifiSSID[32];
char WifiKey[64];
char WifiSSID2[32];
+16 -2
View File
@@ -158,17 +158,31 @@ void SettingsStruct_tmpl<N_TASKS>::ApDontForceSetup(bool value) {
bitWrite(VariousBits1, 14, value);
}
template<unsigned int N_TASKS>
bool SettingsStruct_tmpl<N_TASKS>::PeriodicalScanWiFi() const {
// Invert to enable it by default
return !bitRead(VariousBits1, 15);
}
template<unsigned int N_TASKS>
void SettingsStruct_tmpl<N_TASKS>::PeriodicalScanWiFi(bool value) {
// Invert to enable it by default
bitWrite(VariousBits1, 15, !value);
}
template<unsigned int N_TASKS>
bool SettingsStruct_tmpl<N_TASKS>::CombineTaskValues_SingleEvent(taskIndex_t taskIndex) const {
if (validTaskIndex(taskIndex))
if (validTaskIndex(taskIndex)) {
return bitRead(TaskDeviceSendDataFlags[taskIndex], 0);
}
return false;
}
template<unsigned int N_TASKS>
void SettingsStruct_tmpl<N_TASKS>::CombineTaskValues_SingleEvent(taskIndex_t taskIndex, bool value) {
if (validTaskIndex(taskIndex))
if (validTaskIndex(taskIndex)) {
bitWrite(TaskDeviceSendDataFlags[taskIndex], 0, value);
}
}
template<unsigned int N_TASKS>
+6
View File
@@ -102,11 +102,16 @@ class SettingsStruct_tmpl
bool ApDontForceSetup() const;
void ApDontForceSetup(bool value);
// Perform periodical WiFi scans so that in case of a WiFi disconnect a node may reconnect to a better AP
bool PeriodicalScanWiFi() const;
void PeriodicalScanWiFi(bool value);
// Flag indicating whether all task values should be sent in a single event or one event per task value (default behavior)
bool CombineTaskValues_SingleEvent(taskIndex_t taskIndex) const;
void CombineTaskValues_SingleEvent(taskIndex_t taskIndex, bool value);
void validate();
bool networkSettingsEmpty() const;
@@ -264,6 +269,7 @@ class SettingsStruct_tmpl
#endif
uint8_t WiFi_TX_power = 70; // 70 = 17.5dBm. unit: 0.25 dBm
int8_t WiFi_sensitivity_margin = 3; // Margin in dBm on top of sensitivity.
uint8_t NumberExtraWiFiScans = 0;
};
/*
+13 -2
View File
@@ -1,14 +1,21 @@
#include "WiFiEventData.h"
#include "../ESPEasyCore/ESPEasy_Log.h"
#include "../Globals/RTC.h"
#include "../Globals/SecuritySettings.h"
#include "../Globals/WiFi_AP_Candidates.h"
#include "../Helpers/ESPEasy_Storage.h"
// Bit numbers for WiFi status
#define ESPEASY_WIFI_CONNECTED 0
#define ESPEASY_WIFI_GOT_IP 1
#define ESPEASY_WIFI_SERVICES_INITIALIZED 2
#define WIFI_RECONNECT_WAIT 20000 // in milliSeconds
bool WiFiEventData_t::WiFiConnectAllowed() const {
if (!wifiConnectAttemptNeeded) return false;
if (wifiSetupConnect) return true;
@@ -51,6 +58,7 @@ void WiFiEventData_t::clearAll() {
processedScanDone = true;
wifiConnectAttemptNeeded = true;
wifi_TX_pwr = 0;
usedChannel = 0;
}
void WiFiEventData_t::markWiFiBegin() {
@@ -61,9 +69,10 @@ void WiFiEventData_t::markWiFiBegin() {
last_wifi_connect_attempt_moment.setNow();
wifi_considered_stable = false;
wifiConnectInProgress = true;
usedChannel = 0;
++wifi_connect_attempt;
if (!timerAPstart.isSet()) {
timerAPstart.setNow();
timerAPstart.setMillisFromNow(WIFI_RECONNECT_WAIT);
}
}
@@ -120,6 +129,7 @@ void WiFiEventData_t::markLostIP() {
void WiFiEventData_t::markDisconnect(WiFiDisconnectReason reason) {
lastDisconnectMoment.setNow();
usedChannel = 0;
if (last_wifi_connect_attempt_moment.isSet() && !lastConnectMoment.isSet()) {
// There was an unsuccessful connection attempt
@@ -132,14 +142,15 @@ void WiFiEventData_t::markDisconnect(WiFiDisconnectReason reason) {
}
void WiFiEventData_t::markConnected(const String& ssid, const uint8_t bssid[6], byte channel) {
usedChannel = channel;
lastConnectMoment.setNow();
processedConnect = false;
channel_changed = RTC.lastWiFiChannel != channel;
RTC.lastWiFiChannel = channel;
last_ssid = ssid;
bssid_changed = false;
auth_mode = WiFi_AP_Candidates.getCurrent().enc_type;
RTC.lastWiFiChannel = channel;
for (byte i = 0; i < 6; ++i) {
if (RTC.lastBSSID[i] != bssid[i]) {
bssid_changed = true;
+8
View File
@@ -68,8 +68,12 @@ struct WiFiEventData_t {
bool channel_changed = false;
uint8_t auth_mode = 0;
uint8_t lastScanChannel = 0;
uint8_t usedChannel = 0;
WiFiDisconnectReason lastDisconnectReason = WIFI_DISCONNECT_REASON_UNSPECIFIED;
LongTermTimer lastScanMoment;
LongTermTimer lastConnectMoment;
LongTermTimer lastDisconnectMoment;
LongTermTimer lastWiFiResetMoment;
@@ -95,7 +99,11 @@ struct WiFiEventData_t {
bool wifiConnectInProgress = false;
bool warnedNoValidWiFiSettings = false;
bool performedClearWiFiCredentials = false;
unsigned long connectionFailures = 0;
};
#endif // ifndef DATASTRUCTS_WIFIEVENTDATA_H
+45
View File
@@ -1,7 +1,18 @@
#include "../DataStructs/WiFi_AP_Candidate.h"
#include "../Globals/ESPEasyWiFiEvent.h"
#include "../Globals/SecuritySettings.h"
#include "../Globals/Statistics.h"
#include "../Helpers/ESPEasy_time_calc.h"
#include "../Helpers/StringConverter.h"
#include "../Helpers/StringGenerator_WiFi.h"
#include "../../ESPEasy_common.h"
#include "../../ESPEasy_fdwdecl.h"
#define WIFI_AP_CANDIDATE_MAX_AGE 300000 // 5 minutes in msec
WiFi_AP_Candidate::WiFi_AP_Candidate(byte index_c, const String& ssid_c, const String& pass) :
rssi(0), channel(0), index(index_c), isHidden(false)
@@ -32,11 +43,22 @@ WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t networkItem) : index(0) {
#ifdef ESP32
isHidden = ssid.length() == 0;
#endif // ifdef ESP32
last_seen = millis();
}
WiFi_AP_Candidate::WiFi_AP_Candidate() {}
bool WiFi_AP_Candidate::operator<(const WiFi_AP_Candidate& other) const {
if (isEmergencyFallback != other.isEmergencyFallback) {
return isEmergencyFallback;
}
if (lowPriority != other.lowPriority) {
return !lowPriority;
}
if (isHidden != other.isHidden) {
return !isHidden;
}
// RSSI values >= 0 are invalid
if (rssi >= 0) { return false; }
@@ -54,12 +76,15 @@ WiFi_AP_Candidate& WiFi_AP_Candidate::operator=(const WiFi_AP_Candidate& other)
if (this != &other) { // not a self-assignment
ssid = other.ssid;
key = other.key;
last_seen = other.last_seen;
rssi = other.rssi;
channel = other.channel;
setBSSID(other.bssid);
isHidden = other.isHidden;
index = other.index;
enc_type = other.enc_type;
lowPriority = other.lowPriority;
isEmergencyFallback = other.isEmergencyFallback;
}
return *this;
}
@@ -73,10 +98,30 @@ void WiFi_AP_Candidate::setBSSID(const uint8_t *bssid_c) {
bool WiFi_AP_Candidate::usable() const {
// Allow for empty pass
// if (key.length() == 0) return false;
if (isEmergencyFallback) {
int allowedUptimeMinutes = 10;
#ifdef CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME
allowedUptimeMinutes = CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME;
#endif
if (getUptimeMinutes() > allowedUptimeMinutes ||
!SecuritySettings.hasWiFiCredentials() ||
WiFiEventData.performedClearWiFiCredentials ||
lastBootCause != BOOT_CAUSE_COLD_BOOT) {
return false;
}
}
if (!isHidden && (ssid.length() == 0)) { return false; }
return true;
}
bool WiFi_AP_Candidate::expired() const {
if (last_seen == 0) {
// Not set, so cannot expire
return false;
}
return timePassedSince(last_seen) > WIFI_AP_CANDIDATE_MAX_AGE;
}
bool WiFi_AP_Candidate::allowQuickConnect() const {
if (channel == 0) { return false; }
return bssid_set();
+8 -1
View File
@@ -19,7 +19,7 @@ struct WiFi_AP_Candidate {
// Default constructor
WiFi_AP_Candidate();
// Return true when this one has the best RSSI.
// Return true when this one is preferred over 'other'.
bool operator<(const WiFi_AP_Candidate& other) const;
bool operator==(const WiFi_AP_Candidate& other) const;
@@ -31,6 +31,9 @@ struct WiFi_AP_Candidate {
// Check if the candidate data can be used to actually connect to an AP.
bool usable() const;
// Check if the candidate was recently seen
bool expired() const;
// For quick connection the channel and BSSID are needed
bool allowQuickConnect() const;
@@ -46,12 +49,16 @@ struct WiFi_AP_Candidate {
String ssid;
String key;
unsigned long last_seen = 0;
int32_t rssi = 0;
int32_t channel = 0;
uint8_t bssid[6] = { 0 };
byte index = 0; // Index of the matching credentials
byte enc_type = 0; // Encryption used (e.g. WPA2)
bool isHidden = false; // Hidden SSID
bool lowPriority = false; // Try as last attempt
bool isEmergencyFallback = false;
};
#endif // ifndef DATASTRUCTS_WIFI_AP_CANDIDATES_H
+1 -1
View File
@@ -27,7 +27,7 @@ void setNetworkMedium(NetworkMedium_t medium) {
#endif
break;
case NetworkMedium_t::WIFI:
WiFiEventData.timerAPoff.setNow();
WiFiEventData.timerAPoff.setMillisFromNow(WIFI_AP_OFF_TIMER_DURATION);
WiFiEventData.timerAPstart.clear();
WifiDisconnect();
break;
+125 -44
View File
@@ -20,6 +20,8 @@
#include "../Helpers/StringConverter.h"
#include "../Helpers/StringGenerator_WiFi.h"
#include "../../ESPEasy_fdwdecl.h"
// ********************************************************************************
// WiFi state
@@ -134,6 +136,11 @@ bool WiFiConnected() {
// For ESP82xx, do not rely on WiFi.status() with event based wifi.
const int32_t wifi_rssi = WiFi.RSSI();
bool validWiFi = (wifi_rssi < 0) && wifi_isconnected && hasIPaddr();
/*
if (validWiFi && WiFi.channel() != WiFiEventData.usedChannel) {
validWiFi = false;
}
*/
if (validWiFi != WiFiEventData.WiFiServicesInitialized()) {
// else wifiStatus is no longer in sync.
if (checkAndResetWiFi()) {
@@ -154,7 +161,7 @@ bool WiFiConnected() {
return WiFiEventData.wifi_considered_stable || WiFiEventData.lastConnectMoment.timeoutReached(100);
}
if ((WiFiEventData.timerAPstart.isSet()) && WiFiEventData.timerAPstart.timeoutReached(WIFI_RECONNECT_WAIT)) {
if ((WiFiEventData.timerAPstart.isSet()) && WiFiEventData.timerAPstart.timeReached()) {
// Timer reached, so enable AP mode.
if (!WifiIsAP(WiFi.getMode())) {
setAP(true);
@@ -167,10 +174,10 @@ bool WiFiConnected() {
if (!WiFiEventData.timerAPstart.isSet() && !WifiIsAP(WiFi.getMode())) {
// First run we do not have WiFi connection any more, set timer to start AP mode
// Only allow the automatic AP mode in the first N minutes after boot.
if ((wdcounter / 2) < WIFI_ALLOW_AP_AFTERBOOT_PERIOD) {
WiFiEventData.timerAPstart.setNow();
if (getUptimeMinutes() < WIFI_ALLOW_AP_AFTERBOOT_PERIOD) {
WiFiEventData.timerAPstart.setMillisFromNow(WIFI_RECONNECT_WAIT);
// Fixme TD-er: Make this more elegant as it now needs to know about the extra time needed for the AP start timer.
WiFiEventData.timerAPoff.set(WiFiEventData.timerAPstart.get() + (WIFI_RECONNECT_WAIT * 1000ll));
WiFiEventData.timerAPoff.setMillisFromNow(WIFI_RECONNECT_WAIT + WIFI_AP_OFF_TIMER_DURATION);
}
}
@@ -221,9 +228,12 @@ void AttemptWiFiConnect() {
RTC.clearLastWiFi(); // Force slow connect
WiFiEventData.wifi_connect_attempt = 0;
WiFiEventData.wifiSetupConnect = false;
if (WiFiEventData.timerAPoff.isSet()) {
WiFiEventData.timerAPoff.setMillisFromNow(WIFI_RECONNECT_WAIT + WIFI_AP_OFF_TIMER_DURATION);
}
}
if (WiFi_AP_Candidates.getNext()) {
if (WiFi_AP_Candidates.getNext(WiFiScanAllowed())) {
const WiFi_AP_Candidate& candidate = WiFi_AP_Candidates.getCurrent();
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
@@ -305,8 +315,10 @@ bool checkAndResetWiFi() {
switch(status) {
case STATION_GOT_IP:
if (WiFi.RSSI() < 0) {
// This is a valid status, no need to reset
return false;
//if (WiFi.channel() == WiFiEventData.usedChannel || WiFiEventData.usedChannel == 0) {
// This is a valid status, no need to reset
return false;
//}
}
break;
case STATION_NO_AP_FOUND:
@@ -321,33 +333,29 @@ bool checkAndResetWiFi() {
}
break;
}
#endif
#ifdef ESP32
if (WiFi.isConnected()) {
//if (WiFi.channel() == WiFiEventData.usedChannel || WiFiEventData.usedChannel == 0) {
return false;
//}
}
if (!WiFiEventData.last_wifi_connect_attempt_moment.timeoutReached(15000)) {
return false;
}
#endif
String log = F("WiFi : WiFiConnected() out of sync: ");
log += ESPeasyWifiStatusToString();
log += F(" RSSI: ");
log += String(WiFi.RSSI());
#ifdef ESP8266
log += F(" status: ");
log += SDKwifiStatusToString(status);
#endif
// Call for reset first, to make sure a syslog call will not try to send.
resetWiFi();
addLog(LOG_LEVEL_INFO, log);
#endif
#ifdef ESP32
if (WiFi.isConnected()) {
return false;
} else {
if (!WiFiEventData.last_wifi_connect_attempt_moment.timeoutReached(15000)) {
return false;
}
String log = F("WiFi : WiFiConnected() out of sync: ");
log += ESPeasyWifiStatusToString();
log += F(" RSSI: ");
log += String(WiFi.RSSI());
// Call for reset first, to make sure a syslog call will not try to send.
resetWiFi();
addLog(LOG_LEVEL_INFO, log);
}
#endif
return true;
}
@@ -598,23 +606,85 @@ void WifiDisconnect()
// ********************************************************************************
// Scan WiFi network
// ********************************************************************************
void WifiScan(bool async, uint8_t channel) {
if (WiFi.scanComplete() == WIFI_SCAN_RUNNING) {
// Scan still busy
void WiFiScanPeriodical() {
if (!Settings.PeriodicalScanWiFi()) {
return;
}
addLog(LOG_LEVEL_INFO, F("WiFi : Start network scan"));
if (active_network_medium == NetworkMedium_t::Ethernet) {
return;
}
const bool async = true;
WifiScan(async);
}
bool WiFiScanAllowed() {
if (WiFi.scanComplete() == WIFI_SCAN_RUNNING) {
// Scan still busy
return false;
}
if (!WiFiEventData.processedScanDone) {
processScanDone();
}
if (WiFiEventData.unprocessedWifiEvents()) {
return false;
}
/*
if (!wifiAPmodeActivelyUsed() && !NetworkConnected()) {
return true;
}
*/
if (WiFi_AP_Candidates.scanComplete() <= 0) {
return true;
}
if (WiFiEventData.lastScanMoment.isSet()) {
const LongTermTimer::Duration scanInterval = wifiAPmodeActivelyUsed() ? WIFI_SCAN_INTERVAL_AP_USED : WIFI_SCAN_INTERVAL_MINIMAL;
if (WiFiEventData.lastScanMoment.millisPassedSince() < scanInterval) {
return false;
}
}
return true;
}
void WifiScan(bool async, uint8_t channel) {
if (!WiFiScanAllowed()) {
return;
}
WiFiEventData.lastScanMoment.setNow();
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
if (channel == 0) {
addLog(LOG_LEVEL_INFO, F("WiFi : Start network scan all channels"));
} else {
String log;
log = F("WiFi : Start network scan channel ");
log += channel;
addLog(LOG_LEVEL_INFO, log);
}
}
bool show_hidden = true;
WiFiEventData.processedScanDone = false;
WiFiEventData.lastGetScanMoment.setNow();
#ifdef ESP8266
WiFi.scanNetworks(async, show_hidden, channel);
#endif
#ifdef ESP32
const bool passive = false;
const uint32_t max_ms_per_chan = 300;
WiFi.scanNetworks(async, show_hidden, passive, max_ms_per_chan /*, channel */);
#endif
WiFiEventData.lastScanChannel = channel;
unsigned int nrScans = 1 + (async ? 0 : Settings.NumberExtraWiFiScans);
while (nrScans > 0) {
if (!async) {
WiFi_AP_Candidates.begin_sync_scan();
}
--nrScans;
#ifdef ESP8266
WiFi.scanNetworks(async, show_hidden, channel);
#endif
#ifdef ESP32
const bool passive = false;
const uint32_t max_ms_per_chan = 300;
WiFi.scanNetworks(async, show_hidden, passive, max_ms_per_chan /*, channel */);
#endif
if (!async) {
processScanDone();
}
}
}
// ********************************************************************************
@@ -624,8 +694,13 @@ void WifiScan()
{
// Direct Serial is allowed here, since this function will only be called from serial input.
serialPrintln(F("WIFI : SSID Scan start"));
WifiScan(false);
const int8_t scanCompleteStatus = WiFi.scanComplete();
if (WiFi_AP_Candidates.scanComplete() <= 0) {
WiFiMode_t cur_wifimode = WiFi.getMode();
WifiScan(false);
setWifiMode(cur_wifimode);
}
const int8_t scanCompleteStatus = WiFi_AP_Candidates.scanComplete();
if (scanCompleteStatus <= 0) {
serialPrintln(F("WIFI : No networks found"));
}
@@ -635,13 +710,16 @@ void WifiScan()
serialPrint(String(scanCompleteStatus));
serialPrintln(F(" networks found"));
for (int i = 0; i < scanCompleteStatus; ++i)
int i = 0;
for (auto it = WiFi_AP_Candidates.scanned_begin(); it != WiFi_AP_Candidates.scanned_end(); ++it)
{
++i;
// Print SSID and RSSI for each network found
serialPrint(F("WIFI : "));
serialPrint(String(i + 1));
serialPrint(String(i));
serialPrint(": ");
serialPrintln(formatScanResult(i, " "));
serialPrintln(it->toString());
delay(10);
}
}
@@ -680,7 +758,10 @@ void setAP(bool enable) {
switch (wifimode) {
case WIFI_OFF:
if (enable) { setWifiMode(WIFI_AP); }
if (enable) {
WifiScan(false);
setWifiMode(WIFI_AP);
}
break;
case WIFI_STA:
@@ -741,7 +822,7 @@ void setAPinternal(bool enable)
}
}
#endif // ifdef ESP32
WiFiEventData.timerAPoff.setNow();
WiFiEventData.timerAPoff.setMillisFromNow(WIFI_AP_OFF_TIMER_DURATION);
} else {
#ifdef FEATURE_DNS_SERVER
if (dnsServerActive) {
+5 -1
View File
@@ -15,9 +15,11 @@
#include "../DataTypes/WiFiConnectionProtocol.h"
#define WIFI_RECONNECT_WAIT 20000 // in milliSeconds
#define WIFI_AP_OFF_TIMER_DURATION 60000 // in milliSeconds
#define WIFI_AP_OFF_TIMER_DURATION 300000 // in milliSeconds
#define WIFI_CONNECTION_CONSIDERED_STABLE 300000 // in milliSeconds
#define WIFI_ALLOW_AP_AFTERBOOT_PERIOD 5 // in minutes
#define WIFI_SCAN_INTERVAL_AP_USED 180000 // in milliSeconds
#define WIFI_SCAN_INTERVAL_MINIMAL 60000 // in milliSeconds
bool WiFiConnected();
void WiFiConnectRelaxed();
@@ -32,6 +34,8 @@ void SetWiFiTXpower(float dBm, float rssi);
float GetRSSIthreshold(float& maxTXpwr);
WiFiConnectionProtocol getConnectionProtocol();
void WifiDisconnect();
void WiFiScanPeriodical();
bool WiFiScanAllowed();
void WifiScan(bool async, uint8_t channel = 0);
void WifiScan();
void setSTA(bool enable);
@@ -3,6 +3,7 @@
// FIXME TD-er: Rename this to ESPEasyNetwork_ProcessEvent
#include "../../ESPEasy-Globals.h"
#include "../../ESPEasy_fdwdecl.h"
#include "../ESPEasyCore/ESPEasy_Log.h"
#include "../ESPEasyCore/ESPEasyNetwork.h"
#include "../ESPEasyCore/ESPEasyWifi.h"
@@ -14,6 +15,7 @@
#include "../Globals/MQTT.h"
#include "../Globals/NetworkState.h"
#include "../Globals/RTC.h"
#include "../Globals/SecuritySettings.h"
#include "../Globals/Settings.h"
#include "../Globals/Services.h"
#include "../Globals/WiFi_AP_Candidates.h"
@@ -237,6 +239,34 @@ void processConnect() {
WiFiEventData.setWiFiConnected();
++WiFiEventData.wifi_reconnects;
if (WiFi_AP_Candidates.getCurrent().isEmergencyFallback) {
bool mustResetCredentials = false;
#ifdef CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS
mustResetCredentials = CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS;
#endif
bool mustStartAP = false;
#ifdef CUSTOM_EMERGENCY_FALLBACK_START_AP
mustStartAP = CUSTOM_EMERGENCY_FALLBACK_START_AP;
#endif
if (mustStartAP) {
int allowedUptimeMinutes = 10;
#ifdef CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME
allowedUptimeMinutes = CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME;
#endif
if (getUptimeMinutes() < allowedUptimeMinutes) {
WiFiEventData.timerAPstart.setNow();
}
}
if (mustResetCredentials && !WiFiEventData.performedClearWiFiCredentials) {
WiFiEventData.performedClearWiFiCredentials = true;
SecuritySettings.clearWiFiCredentials();
SaveSecuritySettings();
WiFiEventData.markDisconnect(WIFI_DISCONNECT_REASON_AUTH_EXPIRE);
WiFi_AP_Candidates.force_reload();
}
}
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
const LongTermTimer::Duration connect_duration = WiFiEventData.last_wifi_connect_attempt_moment.timeDiff(WiFiEventData.lastConnectMoment);
String log = F("WIFI : Connected! AP: ");
@@ -388,7 +418,7 @@ void processConnectAPmode() {
if (WiFiEventData.processedConnectAPmode) { return; }
WiFiEventData.processedConnectAPmode = true;
// Extend timer to switch off AP.
WiFiEventData.timerAPoff.setNow();
WiFiEventData.timerAPoff.setMillisFromNow(WIFI_AP_OFF_TIMER_DURATION);
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("AP Mode: Client connected: ");
@@ -415,7 +445,7 @@ void processDisableAPmode() {
if (WifiIsAP(WiFi.getMode())) {
// disable AP after timeout and no clients connected.
if (WiFiEventData.timerAPoff.timeoutReached(WIFI_AP_OFF_TIMER_DURATION) && (WiFi.softAPgetStationNum() == 0)) {
if (WiFiEventData.timerAPoff.timeReached() && (WiFi.softAPgetStationNum() == 0)) {
setAP(false);
}
}
@@ -454,13 +484,6 @@ void processScanDone() {
}
WiFi_AP_Candidates.process_WiFiscan(scanCompleteStatus);
const WiFi_AP_Candidate bestCandidate = WiFi_AP_Candidates.getBestScanResult();
if (bestCandidate.usable() && loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("WiFi : Selected: ");
log += bestCandidate.toString();
addLog(LOG_LEVEL_INFO, log);
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
#include "NetworkState.h"
#include "../Globals/NetworkState.h"
#include "../../ESPEasy_common.h"
@@ -137,6 +137,8 @@ void ResetFactory()
if (!ResetFactoryDefaultPreference.keepWiFi()) {
strcpy_P(SecuritySettings.WifiSSID, PSTR(DEFAULT_SSID));
strcpy_P(SecuritySettings.WifiKey, PSTR(DEFAULT_KEY));
strcpy_P(SecuritySettings.WifiSSID2, PSTR(DEFAULT_SSID2));
strcpy_P(SecuritySettings.WifiKey2, PSTR(DEFAULT_KEY2));
strcpy_P(SecuritySettings.WifiAPKey, PSTR(DEFAULT_AP_KEY));
SecuritySettings.WifiSSID2[0] = 0;
SecuritySettings.WifiKey2[0] = 0;
+32 -19
View File
@@ -1,4 +1,4 @@
#include "ESPEasy_Storage.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../../ESPEasy_common.h"
@@ -277,6 +277,9 @@ String BuildFixes()
Settings.WiFi_TX_power = 70; // 70 = 17.5dBm. unit: 0.25 dBm
Settings.WiFi_sensitivity_margin = 3; // Margin in dBm on top of sensitivity.
}
if (Settings.Build < 20113) {
Settings.NumberExtraWiFiScans = 0;
}
Settings.Build = BUILD;
return SaveSettings();
@@ -359,31 +362,32 @@ bool GarbageCollection() {
/********************************************************************************************\
Save settings to file system
\*********************************************************************************************/
String SaveSettings(void)
String SaveSettings()
{
#ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("SaveSettings"));
#endif
MD5Builder md5;
uint8_t tmp_md5[16] = { 0 };
String err;
{
Settings.StructSize = sizeof(Settings);
Settings.StructSize = sizeof(Settings);
// FIXME @TD-er: As discussed in #1292, the CRC for the settings is now disabled.
// FIXME @TD-er: As discussed in #1292, the CRC for the settings is now disabled.
/*
memcpy( Settings.ProgmemMd5, CRCValues.runTimeMD5, 16);
md5.begin();
md5.add((uint8_t *)&Settings, sizeof(Settings)-16);
md5.calculate();
md5.getBytes(tmp_md5);
if (memcmp(tmp_md5, Settings.md5, 16) != 0) {
// Settings have changed, save to file.
memcpy(Settings.md5, tmp_md5, 16);
*/
Settings.validate();
err = SaveToFile(SettingsType::getSettingsFileName(SettingsType::Enum::BasicSettings_Type).c_str(), 0, (byte *)&Settings, sizeof(Settings));
/*
MD5Builder md5;
uint8_t tmp_md5[16] = { 0 };
memcpy( Settings.ProgmemMd5, CRCValues.runTimeMD5, 16);
md5.begin();
md5.add((uint8_t *)&Settings, sizeof(Settings)-16);
md5.calculate();
md5.getBytes(tmp_md5);
if (memcmp(tmp_md5, Settings.md5, 16) != 0) {
// Settings have changed, save to file.
memcpy(Settings.md5, tmp_md5, 16);
*/
Settings.validate();
err = SaveToFile(SettingsType::getSettingsFileName(SettingsType::Enum::BasicSettings_Type).c_str(), 0, (byte *)&Settings, sizeof(Settings));
}
if (err.length()) {
return err;
@@ -395,6 +399,15 @@ String SaveSettings(void)
// }
err = SaveSecuritySettings();
return err;
}
String SaveSecuritySettings() {
MD5Builder md5;
uint8_t tmp_md5[16] = { 0 };
String err;
SecuritySettings.validate();
memcpy(SecuritySettings.ProgmemMd5, CRCValues.runTimeMD5, 16);
md5.begin();
+3 -1
View File
@@ -53,7 +53,9 @@ bool GarbageCollection();
/********************************************************************************************\
Save settings to file system
\*********************************************************************************************/
String SaveSettings(void);
String SaveSettings();
String SaveSecuritySettings();
void afterloadSettings();
+4
View File
@@ -46,6 +46,10 @@ public:
_timer_usec = start_time;
}
void setMillisFromNow(uint32_t millisFromNow) {
_timer_usec = getMicros64() + (millisFromNow * 1000ull);
}
bool isSet() const {
return _timer_usec > 0ull;
}
+4 -2
View File
@@ -1,4 +1,4 @@
#include "PeriodicalActions.h"
#include "../Helpers/PeriodicalActions.h"
#include "../../ESPEasy_common.h"
#include "../../ESPEasy_fdwdecl.h"
@@ -200,7 +200,7 @@ void runEach30Seconds()
String log;
log.reserve(80);
log = F("WD : Uptime ");
log += wdcounter / 2;
log += getUptimeMinutes();
log += F(" ConnectFailures ");
log += WiFiEventData.connectionFailures;
log += F(" FreeMem ");
@@ -224,6 +224,7 @@ void runEach30Seconds()
// log += WiFi.getListenInterval();
addLog(LOG_LEVEL_INFO, log);
}
WiFiScanPeriodical();
sendSysInfoUDP(1);
refreshNodeList();
@@ -371,6 +372,7 @@ void runPeriodicalMQTT() {
}
}
// FIXME TD-er: Must move to a more logical part of the code
controllerIndex_t firstEnabledMQTT_ControllerIndex() {
for (controllerIndex_t i = 0; i < CONTROLLER_MAX; ++i) {
protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(i);
+14 -9
View File
@@ -78,11 +78,16 @@ bool string2float(const String& string, float& floatvalue) {
/********************************************************************************************\
Convert a char string to IP byte array
\*********************************************************************************************/
boolean str2ip(const String& string, byte *IP) {
bool isIP(const String& string) {
IPAddress tmpip;
return (tmpip.fromString(string));
}
bool str2ip(const String& string, byte *IP) {
return str2ip(string.c_str(), IP);
}
boolean str2ip(const char *string, byte *IP)
bool str2ip(const char *string, byte *IP)
{
IPAddress tmpip; // Default constructor => set to 0.0.0.0
@@ -644,7 +649,7 @@ String URLEncode(const char *msg)
return encodedMsg;
}
void repl(const String& key, const String& val, String& s, boolean useURLencode)
void repl(const String& key, const String& val, String& s, bool useURLencode)
{
if (useURLencode) {
// URLEncode does take resources, so check first if needed.
@@ -656,7 +661,7 @@ void repl(const String& key, const String& val, String& s, boolean useURLencode)
}
#ifndef BUILD_NO_SPECIAL_CHARACTERS_STRINGCONVERTER
void parseSpecialCharacters(String& s, boolean useURLencode)
void parseSpecialCharacters(String& s, bool useURLencode)
{
bool no_accolades = s.indexOf('{') == -1 || s.indexOf('}') == -1;
bool no_html_entity = s.indexOf('&') == -1 || s.indexOf(';') == -1;
@@ -740,7 +745,7 @@ void parseSpecialCharacters(String& s, boolean useURLencode)
/********************************************************************************************\
replace other system variables like %sysname%, %systime%, %ip%
\*********************************************************************************************/
void parseControllerVariables(String& s, struct EventStruct *event, boolean useURLencode) {
void parseControllerVariables(String& s, struct EventStruct *event, bool useURLencode) {
s = parseTemplate(s, useURLencode);
parseEventVariables(s, event, useURLencode);
}
@@ -748,7 +753,7 @@ void parseControllerVariables(String& s, struct EventStruct *event, boolean useU
void parseSingleControllerVariable(String & s,
struct EventStruct *event,
byte taskValueIndex,
boolean useURLencode) {
bool useURLencode) {
if (validTaskIndex(event->TaskIndex)) {
LoadTaskSettings(event->TaskIndex);
repl(F("%valname%"), ExtraTaskSettings.TaskDeviceValueNames[taskValueIndex], s, useURLencode);
@@ -761,7 +766,7 @@ void parseSingleControllerVariable(String & s,
// Simple macro to create the replacement string only when needed.
#define SMART_REPL(T, S) \
if (s.indexOf(T) != -1) { repl((T), (S), s, useURLencode); }
void parseSystemVariables(String& s, boolean useURLencode)
void parseSystemVariables(String& s, bool useURLencode)
{
#ifndef BUILD_NO_SPECIAL_CHARACTERS_STRINGCONVERTER
parseSpecialCharacters(s, useURLencode);
@@ -770,7 +775,7 @@ void parseSystemVariables(String& s, boolean useURLencode)
SystemVariables::parseSystemVariables(s, useURLencode);
}
void parseEventVariables(String& s, struct EventStruct *event, boolean useURLencode)
void parseEventVariables(String& s, struct EventStruct *event, bool useURLencode)
{
repl(F("%id%"), String(event->idx), s, useURLencode);
@@ -867,7 +872,7 @@ bool getConvertArgumentString(const String& marker, const String& s, String& arg
// Parse conversions marked with "%conv_marker%(float)"
// Must be called last, since all sensor values must be converted, processed, etc.
void parseStandardConversions(String& s, boolean useURLencode) {
void parseStandardConversions(String& s, bool useURLencode) {
if (s.indexOf(F("%c_")) == -1) {
return; // Nothing to replace
}
+11 -9
View File
@@ -32,10 +32,12 @@ bool string2float(const String& string,
/********************************************************************************************\
Convert a char string to IP byte array
\*********************************************************************************************/
boolean str2ip(const String& string,
bool isIP(const String& string);
bool str2ip(const String& string,
byte *IP);
boolean str2ip(const char *string,
bool str2ip(const char *string,
byte *IP);
String formatIP(const IPAddress& ip);
@@ -198,11 +200,11 @@ String URLEncode(const char *msg);
void repl(const String& key,
const String& val,
String & s,
boolean useURLencode);
bool useURLencode);
#ifndef BUILD_NO_SPECIAL_CHARACTERS_STRINGCONVERTER
void parseSpecialCharacters(String& s,
boolean useURLencode);
bool useURLencode);
#endif // ifndef BUILD_NO_SPECIAL_CHARACTERS_STRINGCONVERTER
/********************************************************************************************\
@@ -210,19 +212,19 @@ void parseSpecialCharacters(String& s,
\*********************************************************************************************/
void parseControllerVariables(String & s,
struct EventStruct *event,
boolean useURLencode);
bool useURLencode);
void parseSingleControllerVariable(String & s,
struct EventStruct *event,
byte taskValueIndex,
boolean useURLencode);
bool useURLencode);
void parseSystemVariables(String& s,
boolean useURLencode);
bool useURLencode);
void parseEventVariables(String & s,
struct EventStruct *event,
boolean useURLencode);
bool useURLencode);
bool getConvertArgument(const String& marker,
const String& s,
@@ -246,7 +248,7 @@ bool getConvertArgumentString(const String& marker,
// Parse conversions marked with "%conv_marker%(float)"
// Must be called last, since all sensor values must be converted, processed, etc.
void parseStandardConversions(String& s,
boolean useURLencode);
bool useURLencode);
bool HasArgv(const char *string,
+13 -2
View File
@@ -16,6 +16,7 @@
#include "../Globals/ESPEasy_time.h"
#include "../Globals/ESPEasyWiFiEvent.h"
#include "../Globals/NetworkState.h"
#include "../Globals/SecuritySettings.h"
#include "../Globals/Settings.h"
#include "../Globals/WiFi_AP_Candidates.h"
@@ -49,6 +50,8 @@ String getLabel(LabelType::Enum label) {
case LabelType::WIFI_CUR_TX_PWR: return F("Current WiFi TX Power");
case LabelType::WIFI_SENS_MARGIN: return F("WiFi Sensitivity Margin");
case LabelType::WIFI_SEND_AT_MAX_TX_PWR:return F("Send With Max TX Power");
case LabelType::WIFI_NR_EXTRA_SCANS: return F("Extra WiFi scan loops");
case LabelType::WIFI_PERIODICAL_SCAN: return F("Periodical Scan WiFi");
case LabelType::FREE_MEM: return F("Free RAM");
case LabelType::FREE_STACK: return F("Free Stack");
@@ -105,6 +108,9 @@ String getLabel(LabelType::Enum label) {
case LabelType::LAST_DISCONNECT_REASON: return F("Last Disconnect Reason");
case LabelType::LAST_DISC_REASON_STR: return F("Last Disconnect Reason str");
case LabelType::NUMBER_RECONNECTS: return F("Number Reconnects");
case LabelType::WIFI_STORED_SSID1: return F("Configured SSID1");
case LabelType::WIFI_STORED_SSID2: return F("Configured SSID2");
case LabelType::FORCE_WIFI_BG: return F("Force WiFi B/G");
case LabelType::RESTART_WIFI_LOST_CONN: return F("Restart WiFi Lost Conn");
@@ -189,7 +195,7 @@ String getValue(LabelType::Enum label) {
case LabelType::LOCAL_TIME: return node_time.getDateTimeString('-', ':', ' ');
case LabelType::UPTIME: return String(wdcounter / 2);
case LabelType::UPTIME: return String(getUptimeMinutes());
case LabelType::LOAD_PCT: return String(getCPUload());
case LabelType::LOOP_COUNT: return String(getLoopCountPerSec());
case LabelType::CPU_ECO_MODE: return jsonBool(Settings.EcoPowerMode());
@@ -197,6 +203,8 @@ String getValue(LabelType::Enum label) {
case LabelType::WIFI_CUR_TX_PWR: return String(WiFiEventData.wifi_TX_pwr, 2);
case LabelType::WIFI_SENS_MARGIN: return String(Settings.WiFi_sensitivity_margin);
case LabelType::WIFI_SEND_AT_MAX_TX_PWR:return jsonBool(Settings.UseMaxTXpowerForSending());
case LabelType::WIFI_NR_EXTRA_SCANS: return String(Settings.NumberExtraWiFiScans);
case LabelType::WIFI_PERIODICAL_SCAN: return jsonBool(Settings.PeriodicalScanWiFi());
case LabelType::FREE_MEM: return String(ESP.getFreeHeap());
case LabelType::FREE_STACK: return String(getCurrentFreeStack());
@@ -260,6 +268,9 @@ String getValue(LabelType::Enum label) {
case LabelType::LAST_DISCONNECT_REASON: return String(WiFiEventData.lastDisconnectReason);
case LabelType::LAST_DISC_REASON_STR: return getLastDisconnectReason();
case LabelType::NUMBER_RECONNECTS: return String(WiFiEventData.wifi_reconnects);
case LabelType::WIFI_STORED_SSID1: return String(SecuritySettings.WifiSSID);
case LabelType::WIFI_STORED_SSID2: return String(SecuritySettings.WifiSSID2);
case LabelType::FORCE_WIFI_BG: return jsonBool(Settings.ForceWiFi_bg_mode());
case LabelType::RESTART_WIFI_LOST_CONN: return jsonBool(Settings.WiFiRestart_connection_lost());
@@ -365,7 +376,7 @@ String getExtendedValue(LabelType::Enum label) {
{
String result;
result.reserve(40);
int minutes = wdcounter / 2;
int minutes = getUptimeMinutes();
int days = minutes / 1440;
minutes = minutes % 1440;
int hrs = minutes / 60;
+4
View File
@@ -22,6 +22,8 @@ struct LabelType {
WIFI_CUR_TX_PWR, // Unit dBm of current WiFi TX power.
WIFI_SENS_MARGIN, // Margin in dB on top of sensitivity
WIFI_SEND_AT_MAX_TX_PWR,
WIFI_NR_EXTRA_SCANS,
WIFI_PERIODICAL_SCAN,
FREE_MEM, // 9876
FREE_STACK, // 3456
@@ -77,6 +79,8 @@ struct LabelType {
LAST_DISCONNECT_REASON, // 200
LAST_DISC_REASON_STR, // Beacon timeout
NUMBER_RECONNECTS, // 5
WIFI_STORED_SSID1,
WIFI_STORED_SSID2,
FORCE_WIFI_BG,
RESTART_WIFI_LOST_CONN,
+1 -1
View File
@@ -165,7 +165,7 @@ void SystemVariables::parseSystemVariables(String& s, boolean useURLencode)
case UNIXDAY: value = String(node_time.getUnixTime() / 86400); break;
case UNIXDAY_SEC: value = String(node_time.getUnixTime() % 86400); break;
case UNIXTIME: value = String(node_time.getUnixTime()); break;
case UPTIME: value = String(wdcounter / 2); break;
case UPTIME: value = String(getUptimeMinutes()); break;
case UPTIME_MS: value = ull2String(getMicros64() / 1000); break;
#if FEATURE_ADC_VCC
case VCC: value = String(vcc); break;
+239 -79
View File
@@ -4,6 +4,12 @@
#include "../Globals/RTC.h"
#include "../Globals/SecuritySettings.h"
#include "../Globals/Settings.h"
#include "../../ESPEasy_common.h"
#include "../../ESPEasy_fdwdecl.h"
#define WIFI_CUSTOM_DEPLOYMENT_KEY_INDEX 3
#define WIFI_CUSTOM_SUPPORT_KEY_INDEX 4
#define WIFI_CREDENTIALS_FALLBACK_SSID_INDEX 5
WiFi_AP_CandidatesList::WiFi_AP_CandidatesList() {
known.clear();
@@ -17,20 +23,34 @@ void WiFi_AP_CandidatesList::load_knownCredentials() {
_mustLoadCredentials = false;
known.clear();
candidates.clear();
addFromRTC();
{
// Add the known SSIDs
String ssid, key;
byte index = 1; // Index 0 is the "unset" value
while (get_SSID_key(index, ssid, key)) {
known.emplace_back(index, ssid, key);
++index;
bool done = false;
while (!done) {
if (get_SSID_key(index, ssid, key)) {
known.emplace_back(index, ssid, key);
if (index == WIFI_CUSTOM_DEPLOYMENT_KEY_INDEX ||
index == WIFI_CUSTOM_SUPPORT_KEY_INDEX) {
known.back().lowPriority = true;
} else if (index == WIFI_CREDENTIALS_FALLBACK_SSID_INDEX) {
known.back().isEmergencyFallback = true;
}
++index;
} else {
if (SettingsIndexMatchCustomCredentials(index)) {
++index;
} else {
done = true;
}
}
}
}
known_it = known.begin();
purge_unusable();
loadCandidatesFromScanned();
}
void WiFi_AP_CandidatesList::clearCache() {
@@ -43,36 +63,51 @@ void WiFi_AP_CandidatesList::clearCache() {
void WiFi_AP_CandidatesList::force_reload() {
clearCache();
RTC.clearLastWiFi(); // Invalidate the RTC WiFi data.
process_WiFiscan(WiFi.scanComplete());
candidates.clear();
loadCandidatesFromScanned();
}
void WiFi_AP_CandidatesList::begin_sync_scan() {
candidates.clear();
}
void WiFi_AP_CandidatesList::process_WiFiscan(uint8_t scancount) {
load_knownCredentials();
candidates.clear();
known_it = known.begin();
// Now try to merge the known SSIDs, or add a new one if it is a hidden SSID
// Append or update found APs from scan.
for (uint8_t i = 0; i < scancount; ++i) {
add(i);
const WiFi_AP_Candidate tmp(i);
// Remove previous scan result if present
for (auto it = scanned.begin(); it != scanned.end(); ) {
if (tmp == *it || it->expired()) {
it = scanned.erase(it);
} else {
++it;
}
}
// if (Settings.IncludeHiddenSSID() || !tmp.isHidden) {
scanned.push_back(tmp);
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("WiFi : Scan result: ");
log += tmp.toString();
addLog(LOG_LEVEL_DEBUG, log);
}
#endif // ifndef BUILD_NO_DEBUG
// }
}
addFromRTC();
purge_unusable();
#ifndef BUILD_NO_DEBUG
for (auto it = candidates.begin(); it != candidates.end(); ++it) {
String log = F("WiFi : Scan result: ");
log += it->toString();
addLog(LOG_LEVEL_INFO, log);
}
#endif // ifndef BUILD_NO_DEBUG
scanned.sort();
loadCandidatesFromScanned();
}
bool WiFi_AP_CandidatesList::getNext() {
bool WiFi_AP_CandidatesList::getNext(bool scanAllowed) {
load_knownCredentials();
if (candidates.empty()) { return false; }
if (candidates.empty()) {
if (scanAllowed) {
return false;
}
loadCandidatesFromScanned();
if (candidates.empty()) { return false; }
}
bool mustPop = true;
@@ -118,7 +153,7 @@ const WiFi_AP_Candidate& WiFi_AP_CandidatesList::getCurrent() const {
return currentCandidate;
}
WiFi_AP_Candidate WiFi_AP_CandidatesList::getBestScanResult() const {
WiFi_AP_Candidate WiFi_AP_CandidatesList::getBestCandidate() const {
for (auto it = candidates.begin(); it != candidates.end(); ++it) {
if (it->rssi < -1) { return *it; }
}
@@ -147,63 +182,36 @@ void WiFi_AP_CandidatesList::markCurrentConnectionStable() {
addFromRTC(); // Store the current one from RTC as the first candidate for a reconnect.
}
void WiFi_AP_CandidatesList::add(uint8_t networkItem) {
WiFi_AP_Candidate tmp(networkItem);
if (tmp.isHidden && Settings.IncludeHiddenSSID()) {
candidates.push_back(tmp);
return;
}
if (tmp.ssid.length() == 0) { return; }
for (auto it = known.begin(); it != known.end(); ++it) {
if (it->ssid.equals(tmp.ssid)) {
tmp.key = it->key;
tmp.index = it->index;
if (tmp.usable()) {
candidates.push_back(tmp);
// Do not return as we may have several AP's with the same SSID and different passwords.
}
int8_t WiFi_AP_CandidatesList::scanComplete() const {
size_t found = 0;
for (auto scan = scanned.begin(); scan != scanned.end(); ++scan) {
if (!scan->expired()) {
++found;
}
}
if (found > 0) {
return found;
}
const int8_t scanCompleteStatus = WiFi.scanComplete();
if (scanCompleteStatus <= 0) {
return scanCompleteStatus;
}
return 0;
}
void WiFi_AP_CandidatesList::addFromRTC() {
if (!RTC.lastWiFi_set()) { return; }
String ssid, key;
if (!get_SSID_key(RTC.lastWiFiSettingsIndex, ssid, key)) {
return;
}
candidates.emplace_front(RTC.lastWiFiSettingsIndex, ssid, key);
candidates.front().setBSSID(RTC.lastBSSID);
candidates.front().rssi = -1; // Set to best possible RSSI so it is tried first.
candidates.front().channel = RTC.lastWiFiChannel;
if (!candidates.front().usable() || !candidates.front().allowQuickConnect()) {
candidates.pop_front();
return;
}
// This is not taken from a scan, so no idea of the used encryption.
// Try to find a matching BSSID to get the encryption.
for (auto it = candidates.begin(); it != candidates.end(); ++it) {
if ((it->rssi != -1) && candidates.front() == *it) {
candidates.front().enc_type = it->enc_type;
return;
}
}
if (currentCandidate == candidates.front()) {
candidates.front().enc_type = currentCandidate.enc_type;
}
bool WiFi_AP_CandidatesList::SettingsIndexMatchCustomCredentials(uint8_t index)
{
return (WIFI_CUSTOM_DEPLOYMENT_KEY_INDEX == index ||
WIFI_CUSTOM_SUPPORT_KEY_INDEX == index ||
WIFI_CREDENTIALS_FALLBACK_SSID_INDEX == index);
}
void WiFi_AP_CandidatesList::purge_unusable() {
void WiFi_AP_CandidatesList::loadCandidatesFromScanned() {
if (candidates.size() > 1) {
// Do not mess with the current candidates order if > 1 present
return;
}
// Purge unusable from known list.
for (auto it = known.begin(); it != known.end();) {
if (it->usable()) {
++it;
@@ -213,7 +221,125 @@ void WiFi_AP_CandidatesList::purge_unusable() {
}
known.sort();
known.unique();
known_it = known.begin();
for (auto scan = scanned.begin(); scan != scanned.end();) {
if (scan->expired()) {
scan = scanned.erase(scan);
} else {
if (scan->isHidden) {
if (Settings.IncludeHiddenSSID()) {
if (SecuritySettings.hasWiFiCredentials()) {
candidates.push_back(*scan);
}
}
} else if (scan->ssid.length() > 0) {
for (auto kn_it = known.begin(); kn_it != known.end(); ++kn_it) {
if (scan->ssid.equals(kn_it->ssid)) {
WiFi_AP_Candidate tmp = *scan;
tmp.key = kn_it->key;
tmp.index = kn_it->index;
tmp.lowPriority = kn_it->lowPriority;
tmp.isEmergencyFallback = kn_it->isEmergencyFallback;
if (tmp.usable()) {
candidates.push_back(tmp);
// Check all knowns as we may have several AP's with the same SSID and different passwords.
}
}
}
}
++scan;
}
}
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
const WiFi_AP_Candidate bestCandidate = getBestCandidate();
if (bestCandidate.usable()) {
String log = F("WiFi : Best AP candidate: ");
log += bestCandidate.toString();
addLog(LOG_LEVEL_INFO, log);
}
}
candidates.sort();
addFromRTC();
purge_unusable();
}
void WiFi_AP_CandidatesList::addFromRTC() {
if (!RTC.lastWiFi_set()) { return; }
if (SettingsIndexMatchCustomCredentials(RTC.lastWiFiSettingsIndex))
{
return;
}
String ssid, key;
if (!get_SSID_key(RTC.lastWiFiSettingsIndex, ssid, key)) {
return;
}
WiFi_AP_Candidate fromRTC(RTC.lastWiFiSettingsIndex, ssid, key);
fromRTC.setBSSID(RTC.lastBSSID);
fromRTC.channel = RTC.lastWiFiChannel;
if (candidates.size() > 0 && candidates.front().ssid.equals(fromRTC.ssid)) {
// Front candidate was already from RTC.
candidates.pop_front();
}
// See if we may have a better candidate for the current network, with a significant better RSSI.
auto bestMatch = candidates.end();
auto lastUsed = candidates.end();
for (auto it = candidates.begin(); lastUsed == candidates.end() && it != candidates.end(); ++it) {
if (it->usable() && it->ssid.equals(fromRTC.ssid)) {
const bool foundLastUsed = fromRTC.bssid_match(it->bssid);
if (foundLastUsed) {
lastUsed = it;
} else if (bestMatch == candidates.end()) {
bestMatch = it;
}
}
}
bool matchAdded = false;
if (bestMatch != candidates.end()) {
// Found a best match, possibly better than the last used.
if (lastUsed == candidates.end() || (bestMatch->rssi > (lastUsed->rssi + 10))) {
// Last used was not found or
// Other candidate has significant better RSSI
matchAdded = true;
candidates.push_front(*bestMatch);
}
} else if (lastUsed != candidates.end()) {
matchAdded = true;
candidates.push_front(*lastUsed);
}
if (!matchAdded) {
candidates.push_front(fromRTC);
// This is not taken from a scan, so no idea of the used encryption.
// Try to find a matching BSSID to get the encryption.
for (auto it = candidates.begin(); it != candidates.end(); ++it) {
if ((it->rssi != -1) && candidates.front() == *it) {
candidates.front().enc_type = it->enc_type;
return;
}
}
}
candidates.front().rssi = -1; // Set to best possible RSSI so it is tried first.
if (!candidates.front().usable() || !candidates.front().allowQuickConnect()) {
candidates.pop_front();
return;
}
if (currentCandidate == candidates.front()) {
candidates.front().enc_type = currentCandidate.enc_type;
}
}
void WiFi_AP_CandidatesList::purge_unusable() {
for (auto it = candidates.begin(); it != candidates.end();) {
if (it->usable()) {
++it;
@@ -235,6 +361,40 @@ bool WiFi_AP_CandidatesList::get_SSID_key(byte index, String& ssid, String& key)
ssid = SecuritySettings.WifiSSID2;
key = SecuritySettings.WifiKey2;
break;
case WIFI_CUSTOM_DEPLOYMENT_KEY_INDEX:
#if !defined(CUSTOM_DEPLOYMENT_SSID) || !defined(CUSTOM_DEPLOYMENT_KEY)
return false;
#else
ssid = F(CUSTOM_DEPLOYMENT_SSID);
key = F(CUSTOM_DEPLOYMENT_KEY);
#endif
break;
case WIFI_CUSTOM_SUPPORT_KEY_INDEX:
#if !defined(CUSTOM_SUPPORT_SSID) || !defined(CUSTOM_SUPPORT_KEY)
return false;
#else
ssid = F(CUSTOM_SUPPORT_SSID);
key = F(CUSTOM_SUPPORT_KEY);
#endif
break;
case WIFI_CREDENTIALS_FALLBACK_SSID_INDEX:
{
#if !defined(CUSTOM_EMERGENCY_FALLBACK_SSID) || !defined(CUSTOM_EMERGENCY_FALLBACK_KEY)
return false;
#else
int allowedUptimeMinutes = 10;
#ifdef CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME
allowedUptimeMinutes = CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME;
#endif
if (getUptimeMinutes() < allowedUptimeMinutes && SecuritySettings.hasWiFiCredentials()) {
ssid = F(CUSTOM_EMERGENCY_FALLBACK_SSID);
key = F(CUSTOM_EMERGENCY_FALLBACK_KEY);
} else {
return false;
}
#endif
break;
}
default:
return false;
}
+26 -7
View File
@@ -5,6 +5,8 @@
#include <list>
typedef std::list<WiFi_AP_Candidate>::const_iterator WiFi_AP_Candidate_const_iterator;
struct WiFi_AP_CandidatesList {
WiFi_AP_CandidatesList();
@@ -16,16 +18,18 @@ struct WiFi_AP_CandidatesList {
// Called after WiFi credentials have changed.
void force_reload();
void begin_sync_scan();
// Add found WiFi access points to the list if they are possible candidates.
void process_WiFiscan(uint8_t scancount);
// Get the next candidate to connect
// Return true when a valid next candidate was found.
bool getNext();
bool getNext(bool scanAllowed);
const WiFi_AP_Candidate& getCurrent() const;
WiFi_AP_Candidate getBestScanResult() const;
WiFi_AP_Candidate getBestCandidate() const;
bool hasKnownCredentials();
@@ -33,10 +37,22 @@ struct WiFi_AP_CandidatesList {
// This will force a reconnect to the current AP if connection is lost.
void markCurrentConnectionStable();
int8_t scanComplete() const;
WiFi_AP_Candidate_const_iterator scanned_begin() const {
return scanned.begin();
}
WiFi_AP_Candidate_const_iterator scanned_end() const {
return scanned.end();
}
static bool SettingsIndexMatchCustomCredentials(uint8_t index);
private:
// Add item from WiFi scan.
void add(uint8_t networkItem);
// Pick the possible
void loadCandidatesFromScanned();
void addFromRTC();
@@ -47,15 +63,18 @@ private:
String& ssid,
String& key) const;
std::list<WiFi_AP_Candidate>candidates;
std::list<WiFi_AP_Candidate> candidates;
std::list<WiFi_AP_Candidate>known;
std::list<WiFi_AP_Candidate> known;
std::list<WiFi_AP_Candidate>::const_iterator known_it;
std::list<WiFi_AP_Candidate> scanned;
WiFi_AP_Candidate_const_iterator known_it;
WiFi_AP_Candidate currentCandidate;
bool _mustLoadCredentials = true;
};
#endif // ifndef HELPERS_WIFI_AP_CANDIDATESLIST_H
+1 -1
View File
@@ -13,7 +13,7 @@
#define P044_CHECKSUM_LENGTH 4
#define P044_DATAGRAM_START_CHAR '/'
#define P044_DATAGRAM_END_CHAR '!'
#define P044_DATAGRAM_MAX_SIZE 2048
#define P044_DATAGRAM_MAX_SIZE 2048u
struct P044_Task : public PluginTaskData_base {
+4 -2
View File
@@ -17,6 +17,10 @@ void handleNotFound() {
checkRAM(F("handleNotFound"));
#endif
if (captivePortal()) { // If captive portal redirect instead of displaying the error page.
return;
}
// if Wifi setup, launch setup wizard if AP_DONT_FORCE_SETUP is not set.
if (WiFiEventData.wifiSetup && !Settings.ApDontForceSetup())
{
@@ -24,8 +28,6 @@ void handleNotFound() {
return;
}
if (!isLoggedIn()) { return; }
#ifdef WEBSERVER_RULES
if (handle_rules_edit(web_server.uri())) { return; }
#endif
+9
View File
@@ -92,6 +92,8 @@ void handle_advanced() {
Settings.setWiFi_TX_power(getFormItemFloat(getInternalLabel(LabelType::WIFI_TX_MAX_PWR)));
Settings.WiFi_sensitivity_margin = getFormItemInt(getInternalLabel(LabelType::WIFI_SENS_MARGIN));
Settings.UseMaxTXpowerForSending(isFormItemChecked(getInternalLabel(LabelType::WIFI_SEND_AT_MAX_TX_PWR)));
Settings.NumberExtraWiFiScans = getFormItemInt(getInternalLabel(LabelType::WIFI_NR_EXTRA_SCANS));
Settings.PeriodicalScanWiFi(isFormItemChecked(getInternalLabel(LabelType::WIFI_PERIODICAL_SCAN)));
addHtmlError(SaveSettings());
@@ -235,6 +237,13 @@ void handle_advanced() {
addFormNote(note);
}
addFormCheckBox(LabelType::WIFI_SEND_AT_MAX_TX_PWR, Settings.UseMaxTXpowerForSending());
{
addFormNumericBox(LabelType::WIFI_NR_EXTRA_SCANS, Settings.NumberExtraWiFiScans, 0, 5);
String note = F("Number of extra times to scan all channels to have higher chance of finding the desired AP");
addFormNote(note);
}
addFormCheckBox(LabelType::WIFI_PERIODICAL_SCAN, Settings.PeriodicalScanWiFi());
addFormSeparator(2);
+2
View File
@@ -49,6 +49,8 @@ boolean handle_custom(String path) {
return false; // unknown file that does not exist...
}
if (!isLoggedIn()) { return false; }
if (dashboardPage) // for the dashboard page, create a default unit dropdown selector
{
// handle page redirects to other unit's as requested by the unit dropdown selector
+4
View File
@@ -195,6 +195,8 @@ void handle_json()
stream_next_json_object_value(LabelType::LAST_DISCONNECT_REASON);
stream_next_json_object_value(LabelType::LAST_DISC_REASON_STR);
stream_next_json_object_value(LabelType::NUMBER_RECONNECTS);
stream_next_json_object_value(LabelType::WIFI_STORED_SSID1);
stream_next_json_object_value(LabelType::WIFI_STORED_SSID2);
stream_next_json_object_value(LabelType::FORCE_WIFI_BG);
stream_next_json_object_value(LabelType::RESTART_WIFI_LOST_CONN);
#ifdef ESP8266
@@ -208,6 +210,8 @@ void handle_json()
stream_next_json_object_value(LabelType::WIFI_CUR_TX_PWR);
stream_next_json_object_value(LabelType::WIFI_SENS_MARGIN);
stream_next_json_object_value(LabelType::WIFI_SEND_AT_MAX_TX_PWR);
stream_next_json_object_value(LabelType::WIFI_NR_EXTRA_SCANS);
stream_next_json_object_value(LabelType::WIFI_PERIODICAL_SCAN);
stream_last_json_object_value(LabelType::WIFI_RSSI);
// TODO: PKR: Add ETH Objects
addHtml(F(",\n"));
+12 -4
View File
@@ -19,11 +19,10 @@ bool loadFromFS(boolean spiffs, String path) {
checkRAM(F("loadFromFS"));
#endif
if (!isLoggedIn()) { return false; }
statusLED(true);
String dataType = F("text/plain");
bool mustCheckCredentials = false;
if (!path.startsWith(F("/"))) {
path = String(F("/")) + path;
@@ -42,11 +41,20 @@ bool loadFromFS(boolean spiffs, String path) {
else if (path.endsWith(F(".svg"))) { dataType = F("image/svg+xml"); }
else if (path.endsWith(F(".json"))) { dataType = F("application/json"); }
else if (path.endsWith(F(".txt")) ||
path.endsWith(F(".dat"))) { dataType = F("application/octet-stream"); }
path.endsWith(F(".dat"))) {
mustCheckCredentials = true;
dataType = F("application/octet-stream");
}
#ifdef WEBSERVER_CUSTOM
else if (path.endsWith(F(".esp"))) { return handle_custom(path); }
else if (path.endsWith(F(".esp"))) {
return handle_custom(path);
}
#endif
if (mustCheckCredentials) {
if (!isLoggedIn()) { return false; }
}
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
+5
View File
@@ -173,6 +173,7 @@ void addRowLabel(const String& label, const String& id)
html += ':';
addHtml(html);
}
addHtml(F("</td>"));
html_TD();
}
@@ -404,11 +405,13 @@ void addTextArea(const String& id, const String& value, int maxlength, int rows,
// adds a Help Button with points to the the given Wiki Subpage
// If url starts with "RTD", it will be considered as a Read-the-docs link
void addHelpButton(const String& url) {
#ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON
if (url.startsWith("RTD")) {
addRTDHelpButton(url.substring(3));
} else {
addHelpButton(url, false);
}
#endif
}
void addRTDHelpButton(const String& url)
@@ -418,10 +421,12 @@ void addRTDHelpButton(const String& url)
void addHelpButton(const String& url, bool isRTD)
{
#ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON
addHtmlLink(
F("button help"),
makeDocLink(url, isRTD),
isRTD ? F("&#8505;") : F("&#10068;"));
#endif
}
void addRTDPluginButton(pluginID_t taskDeviceNumber) {
+1 -1
View File
@@ -137,7 +137,7 @@ void addSubmitButton(const String& value, const String& name, const String& clas
fullClasses.reserve(12 + classes.length());
fullClasses = F("button link");
if (classes.length() == 0) {
if (classes.length() > 0) {
fullClasses += ' ';
fullClasses += classes;
}
+82 -24
View File
@@ -25,6 +25,26 @@
#include "../../ESPEasy_fdwdecl.h"
#include "../../ESPEasy-Globals.h"
#ifdef USES_MQTT
# include "../Globals/MQTT.h"
# include "../Helpers/PeriodicalActions.h" // For finding enabled MQTT controller
#endif
#ifndef MAIN_PAGE_SHOW_BASIC_INFO_NOT_LOGGED_IN
#define MAIN_PAGE_SHOW_BASIC_INFO_NOT_LOGGED_IN false
#endif
// Define main page elements present
#ifndef MAIN_PAGE_SHOW_SYSINFO_BUTTON
#define MAIN_PAGE_SHOW_SYSINFO_BUTTON true
#endif
#ifndef MAIN_PAGE_SHOW_WiFi_SETUP_BUTTON
#define MAIN_PAGE_SHOW_WiFi_SETUP_BUTTON false
#endif
// ********************************************************************************
// Web Interface root page
// ********************************************************************************
@@ -33,6 +53,10 @@ void handle_root() {
checkRAM(F("handle_root"));
#endif
if (captivePortal()) { // If captive portal redirect instead of displaying the page.
return;
}
// if Wifi setup, launch setup wizard if AP_DONT_FORCE_SETUP is not set.
if (WiFiEventData.wifiSetup && !Settings.ApDontForceSetup())
{
@@ -40,7 +64,12 @@ void handle_root() {
return;
}
if (!isLoggedIn()) { return; }
if (!MAIN_PAGE_SHOW_BASIC_INFO_NOT_LOGGED_IN) {
if (!isLoggedIn()) { return; }
}
const bool loggedIn = isLoggedIn(false);
navMenuIndex = 0;
// if index.htm exists on FS serve that one (first check if gziped version exists)
@@ -55,8 +84,13 @@ void handle_root() {
#endif
TXBuffer.startStream();
String sCommand = web_server.arg(F("cmd"));
boolean rebootCmd = strcasecmp_P(sCommand.c_str(), PSTR("reboot")) == 0;
String sCommand;
boolean rebootCmd = false;
if (loggedIn) {
sCommand = web_server.arg(F("cmd"));
rebootCmd = strcasecmp_P(sCommand.c_str(), PSTR("reboot")) == 0;
}
sendHeadandTail_stdtemplate(_HEAD, rebootCmd);
int freeMem = ESP.getFreeHeap();
@@ -78,17 +112,21 @@ void handle_root() {
addHtml(F("OK"));
} else if (strcasecmp_P(sCommand.c_str(), PSTR("reset")) == 0)
{
addLog(LOG_LEVEL_INFO, F(" : factory reset..."));
cmd_within_mainloop = CMD_REBOOT;
addHtml(F(
"OK. Please wait > 1 min and connect to Acces point.<BR><BR>PW=configesp<BR>URL=<a href='http://192.168.4.1'>192.168.4.1</a>"));
TXBuffer.endStream();
ExecuteCommand_internal(EventValueSource::Enum::VALUE_SOURCE_HTTP, sCommand.c_str());
return;
if (loggedIn) {
addLog(LOG_LEVEL_INFO, F(" : factory reset..."));
cmd_within_mainloop = CMD_REBOOT;
addHtml(F(
"OK. Please wait > 1 min and connect to Acces point.<BR><BR>PW=configesp<BR>URL=<a href='http://192.168.4.1'>192.168.4.1</a>"));
TXBuffer.endStream();
ExecuteCommand_internal(EventValueSource::Enum::VALUE_SOURCE_HTTP, sCommand.c_str());
return;
}
} else {
handle_command_from_web(EventValueSource::Enum::VALUE_SOURCE_HTTP, sCommand);
printToWeb = false;
printToWebJSON = false;
if (loggedIn) {
handle_command_from_web(EventValueSource::Enum::VALUE_SOURCE_HTTP, sCommand);
printToWeb = false;
printToWebJSON = false;
}
addHtml(F("<form>"));
html_table_class_normal();
@@ -190,22 +228,42 @@ void handle_root() {
addHtml(html);
}
#endif // ifdef FEATURE_MDNS
#ifdef USES_MQTT
{
if (validControllerIndex(firstEnabledMQTT_ControllerIndex())) {
addRowLabel(F("MQTT Client Connected"));
addEnabled(MQTTclient_connected);
}
}
#endif
#if MAIN_PAGE_SHOW_SYSINFO_BUTTON
html_TR_TD();
html_TD();
addButton(F("sysinfo"), F("More info"));
#endif
#if MAIN_PAGE_SHOW_WiFi_SETUP_BUTTON
html_TR_TD();
html_TD();
addButton(F("setup"), F("WiFi Setup"));
#endif
if (printWebString.length() > 0)
{
html_BR();
html_BR();
addFormHeader(F("Command Argument"));
addRowLabel(F("Command"));
addHtml(sCommand);
if (loggedIn) {
if (printWebString.length() > 0)
{
html_BR();
html_BR();
addFormHeader(F("Command Argument"));
addRowLabel(F("Command"));
addHtml(sCommand);
addHtml(F("<TR><TD colspan='2'>Command Output<BR><textarea readonly rows='10' wrap='on'>"));
addHtml(printWebString);
addHtml(F("</textarea>"));
printWebString = "";
addHtml(F("<TR><TD colspan='2'>Command Output<BR><textarea readonly rows='10' wrap='on'>"));
addHtml(printWebString);
addHtml(F("</textarea>"));
printWebString = "";
}
}
html_end_table();
+4
View File
@@ -6,11 +6,15 @@
#ifdef WEBSERVER_ROOT
// ********************************************************************************
// Web Interface root page
// ********************************************************************************
void handle_root();
#endif // ifdef WEBSERVER_ROOT
+1 -1
View File
@@ -381,7 +381,6 @@ bool handle_rules_edit(const String& originalUri)
bool handle_rules_edit(String originalUri, bool isAddNew) {
// originalUri is passed via deepcopy, since it will be converted to lower case.
if (!isLoggedIn() || !Settings.UseRules) { return false; }
originalUri.toLowerCase();
# ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("handle_rules_edit"));
@@ -395,6 +394,7 @@ bool handle_rules_edit(String originalUri, bool isAddNew) {
if (isAddNew || (originalUri.startsWith(F("/rules/"))
&& originalUri.endsWith(F(".txt")))) {
if (!isLoggedIn() || !Settings.UseRules) { return false; }
if (Settings.OldRulesEngine())
{
Goto_Rules_Root();
+251 -106
View File
@@ -14,17 +14,26 @@
# include "../ESPEasyCore/ESPEasyNetwork.h"
# include "../ESPEasyCore/ESPEasyWifi.h"
# include "../Globals/ESPEasyWiFiEvent.h"
# include "../Globals/NetworkState.h"
# include "../Globals/RTC.h"
# include "../Globals/Settings.h"
# include "../Globals/SecuritySettings.h"
# include "../Globals/WiFi_AP_Candidates.h"
# include "../Helpers/Misc.h"
# include "../Helpers/Networking.h"
# include "../Helpers/ESPEasy_Storage.h"
# include "../Helpers/StringConverter.h"
#ifndef SETUP_PAGE_SHOW_CONFIG_BUTTON
#define SETUP_PAGE_SHOW_CONFIG_BUTTON true
#endif
// ********************************************************************************
// Web Interface Setup Wizard
// ********************************************************************************
@@ -40,79 +49,187 @@ void handle_setup() {
// Do not check client IP range allowed.
TXBuffer.startStream();
if (!NetworkConnected())
{
sendHeadandTail(F("TmplAP"));
static byte status = HANDLE_SETUP_SCAN_STAGE;
static byte refreshCount = 0;
String ssid = web_server.arg(F("ssid"));
String other = web_server.arg(F("other"));
String password = web_server.arg(F("pass"));
const bool connected = NetworkConnected();
if (other.length() != 0)
{
ssid = other;
}
// if ssid config not set and params are both provided
if ((status == HANDLE_SETUP_SCAN_STAGE) && (ssid.length() != 0) /*&& strcasecmp(SecuritySettings.WifiSSID, "ssid") == 0 */)
{
safe_strncpy(SecuritySettings.WifiKey, password.c_str(), sizeof(SecuritySettings.WifiKey));
safe_strncpy(SecuritySettings.WifiSSID, ssid.c_str(), sizeof(SecuritySettings.WifiSSID));
// Hidden SSID
Settings.IncludeHiddenSSID(isFormItemChecked(F("hiddenssid")));
WiFiEventData.wifiSetupConnect = true;
WiFiEventData.wifiConnectAttemptNeeded = true;
WiFi_AP_Candidates.force_reload(); // Force reload of the credentials and found APs from the last scan
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String reconnectlog = F("WIFI : Credentials Changed, retry connection. SSID: ");
reconnectlog += ssid;
addLog(LOG_LEVEL_INFO, reconnectlog);
}
status = HANDLE_SETUP_CONNECTING_STAGE;
refreshCount = 0;
AttemptWiFiConnect();
}
html_BR();
wrap_html_tag(F("h1"), F("Wifi Setup wizard"));
html_add_form();
switch (status) {
case HANDLE_SETUP_SCAN_STAGE:
{
// first step, scan and show access points within reach...
handle_setup_scan_and_show(ssid, other, password);
break;
}
case HANDLE_SETUP_CONNECTING_STAGE:
{
if (!handle_setup_connectingStage(refreshCount)) {
status = HANDLE_SETUP_SCAN_STAGE;
}
++refreshCount;
break;
}
}
html_end_form();
sendHeadandTail(F("TmplAP"), true);
if (connected) {
navMenuIndex = MENU_INDEX_TOOLS;
sendHeadandTail_stdtemplate(_HEAD);
} else {
// Connect Success
handle_setup_finish();
sendHeadandTail(F("TmplAP"));
}
const bool clearButtonPressed = web_server.hasArg(F("performclearcredentials"));
const bool clearWiFiCredentials =
isFormItemChecked(F("clearcredentials")) && clearButtonPressed;
if (clearWiFiCredentials) {
SecuritySettings.clearWiFiCredentials();
addHtmlError(SaveSecuritySettings());
html_add_form();
html_table_class_normal();
addFormHeader(F("WiFi credentials cleared, reboot now"));
html_end_table();
} else {
// if (active_network_medium == NetworkMedium_t::WIFI)
// {
static byte status = HANDLE_SETUP_SCAN_STAGE;
static byte refreshCount = 0;
String ssid = web_server.arg(F("ssid"));
String other = web_server.arg(F("other"));
String password = web_server.arg(F("pass"));
bool emptyPass = isFormItemChecked(F("emptypass"));
const bool performRescan = web_server.hasArg(F("performrescan"));
if (performRescan) {
WiFiEventData.lastScanMoment.clear();
WifiScan(false);
}
if (other.length() != 0)
{
ssid = other;
}
if (!performRescan) {
// if ssid config not set and params are both provided
if ((status == HANDLE_SETUP_SCAN_STAGE) && (ssid.length() != 0) /*&& strcasecmp(SecuritySettings.WifiSSID, "ssid") == 0 */)
{
if (clearButtonPressed) {
addHtmlError(F("Warning: Need to confirm to clear WiFi credentials"));
} else if (password.length() == 0 && !emptyPass) {
addHtmlError(F("No password entered"));
} else {
safe_strncpy(SecuritySettings.WifiKey, password.c_str(), sizeof(SecuritySettings.WifiKey));
safe_strncpy(SecuritySettings.WifiSSID, ssid.c_str(), sizeof(SecuritySettings.WifiSSID));
// Hidden SSID
Settings.IncludeHiddenSSID(isFormItemChecked(F("hiddenssid")));
addHtmlError(SaveSettings());
WiFiEventData.wifiSetupConnect = true;
WiFiEventData.wifiConnectAttemptNeeded = true;
WiFi_AP_Candidates.force_reload(); // Force reload of the credentials and found APs from the last scan
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String reconnectlog = F("WIFI : Credentials Changed, retry connection. SSID: ");
reconnectlog += ssid;
addLog(LOG_LEVEL_INFO, reconnectlog);
}
status = HANDLE_SETUP_CONNECTING_STAGE;
refreshCount = 0;
AttemptWiFiConnect();
}
}
}
html_BR();
wrap_html_tag(F("h1"), connected ? F("Connected to a network") : F("Wifi Setup wizard"));
html_add_form();
switch (status) {
case HANDLE_SETUP_SCAN_STAGE:
{
// first step, scan and show access points within reach...
handle_setup_scan_and_show(ssid, other, password);
break;
}
case HANDLE_SETUP_CONNECTING_STAGE:
{
if (!handle_setup_connectingStage(refreshCount)) {
status = HANDLE_SETUP_SCAN_STAGE;
}
++refreshCount;
break;
}
}
/*
} else {
html_add_form();
addFormHeader(F("Ethernet Setup Complete"));
}
*/
html_table_class_normal();
html_TR();
handle_sysinfo_NetworkServices();
if (connected) {
//addFormHeader(F("Current network configuration"));
handle_sysinfo_Network();
addFormSeparator(2);
html_TR_TD();
html_TD();
#if SETUP_PAGE_SHOW_CONFIG_BUTTON
if (!clientIPinSubnet()) {
String host = formatIP(NetworkLocalIP());
String url = F("http://");
url += host;
url += F("/config");
addButton(url, host);
}
#endif
WiFiEventData.wifiSetup = false;
}
html_end_table();
html_BR();
html_BR();
html_BR();
html_BR();
html_BR();
html_BR();
html_BR();
html_table_class_normal();
addFormHeader(F("Advanced WiFi settings"));
addFormCheckBox(F("Include Hidden SSID"), F("hiddenssid"), Settings.IncludeHiddenSSID());
addFormNote(F("Must be checked to connect to a hidden SSID"));
html_BR();
html_BR();
addFormHeader(F("Clear WiFi credentials"));
addFormCheckBox(F("Confirm clear"), F("clearcredentials"), false);
html_TR_TD();
html_TD();
addSubmitButton(F("Clear and Reboot"), F("performclearcredentials"), F("red"));
html_end_table();
}
html_end_form();
if (connected) {
sendHeadandTail_stdtemplate(_TAIL);
} else {
sendHeadandTail(F("TmplAP"), true);
}
TXBuffer.endStream();
delay(10);
if (clearWiFiCredentials) {
reboot(ESPEasy_Scheduler::IntendedRebootReason_e::RestoreSettings);
}
}
void handle_setup_scan_and_show(const String& ssid, const String& other, const String& password) {
if (WiFi.scanComplete() <= WIFI_SCAN_FAILED) {
int8_t scanCompleteStatus = WiFi_AP_Candidates.scanComplete();
const bool needsRescan = scanCompleteStatus <= 0 || WiFiScanAllowed();
if (needsRescan) {
WiFiMode_t cur_wifimode = WiFi.getMode();
WifiScan(false);
scanCompleteStatus = WiFi_AP_Candidates.scanComplete();
setWifiMode(cur_wifimode);
}
const int8_t scanCompleteStatus = WiFi.scanComplete();
if (scanCompleteStatus <= 0) {
addHtml(F("No Access Points found"));
@@ -125,31 +242,64 @@ void handle_setup_scan_and_show(const String& ssid, const String& other, const S
html_table_header(F("Network info"));
html_table_header(F("RSSI"), 50);
for (int i = 0; i < scanCompleteStatus; ++i)
for (auto it = WiFi_AP_Candidates.scanned_begin(); it != WiFi_AP_Candidates.scanned_end(); ++it)
{
html_TR_TD();
addHtml(F("<label class='container2'>"));
const String id = it->toString("");
addHtml(F("<label "));
addHtmlAttribute(F("class"), F("container2"));
addHtmlAttribute(F("for"), id);
addHtml('>');
addHtml(F("<input type='radio' name='ssid' value='"));
{
String escapeBuffer = WiFi.SSID(i);
if (it->isHidden) {
addHtml(F("#Hidden#' disabled"));
} else {
String escapeBuffer = it->ssid;
htmlStrongEscape(escapeBuffer);
addHtml(escapeBuffer);
addHtml('\'');
}
addHtml("'");
if (WiFi.SSID(i) == ssid) {
addHtml(F(" checked "));
addHtmlAttribute(F("id"), id);
{
if (it->bssid_match(RTC.lastBSSID)) {
if (!WiFi_AP_Candidates.SettingsIndexMatchCustomCredentials(RTC.lastWiFiSettingsIndex)) {
addHtml(F(" checked "));
}
}
}
addHtml(F("><span class='dotmark'></span></label><TD>"));
int32_t rssi = 0;
addHtml(formatScanResult(i, "<BR>", rssi));
addHtml(F("><span class='dotmark'></span>"));
addHtml(F("</label>"));
html_TD();
getWiFi_RSSI_icon(rssi, 45);
addHtml(F("<label "));
addHtmlAttribute(F("for"), id);
addHtml('>');
addHtml(it->toString(F("<BR>")));
addHtml(F("</label>"));
html_TD();
addHtml(F("<label "));
addHtmlAttribute(F("for"), id);
addHtml('>');
getWiFi_RSSI_icon(it->rssi, 45);
addHtml(F("</label>"));
}
html_end_table();
}
html_BR();
addSubmitButton(F("Rescan"), F("performrescan"));
html_BR();
html_table_class_normal();
html_TR_TD();
addHtml(F("<label "));
addHtmlAttribute(F("class"), F("container2"));
addHtml('>');
@@ -162,12 +312,23 @@ void handle_setup_scan_and_show(const String& ssid, const String& other, const S
addHtml('>');
addHtml(F("<span class='dotmark'></span></label>"));
html_TD();
addHtml(F("<label "));
addHtmlAttribute(F("for"), F("other_ssid"));
addHtml('>');
addHtml(F("<input "));
addHtmlAttribute(F("class"), F("wide"));
addHtmlAttribute(F("type"), F("text"));
addHtmlAttribute(F("name"), F("other"));
addHtmlAttribute(F("value"), other);
addHtml('>');
addHtml(F("</label>"));
html_TR();
html_BR();
html_BR();
@@ -175,21 +336,35 @@ void handle_setup_scan_and_show(const String& ssid, const String& other, const S
html_BR();
html_TR_TD();
addHtml(F("Password:"));
html_BR();
html_TD();
addHtml(F("<input "));
addHtmlAttribute(F("class"), F("wide"));
addHtmlAttribute(F("type"), F("text"));
addHtmlAttribute(F("name"), F("pass"));
addHtmlAttribute(F("value"), password);
addHtml('>');
html_BR();
html_BR();
addFormCheckBox(F("Include Hidden SSID"), F("hiddenssid"), Settings.IncludeHiddenSSID());
addFormNote(F("Must be checked to connect to a hidden SSID"));
addFormCheckBox(F("Allow Empty Password"), F("emptypass"), false);
/*
if (SecuritySettings.hasWiFiCredentials(SecurityStruct::WiFiCredentialsSlot::first)) {
addFormCheckBox(F("Clear Stored SSID1"), F("clearssid1"), false);
addFormNote(String(F("Current: ")) + getValue(LabelType::WIFI_STORED_SSID1));
}
if (SecuritySettings.hasWiFiCredentials(SecurityStruct::WiFiCredentialsSlot::second)) {
addFormCheckBox(F("Clear Stored SSID2"), F("clearssid2"), false);
addFormNote(String(F("Current: ")) + getValue(LabelType::WIFI_STORED_SSID2));
}
*/
html_TR_TD();
html_TD();
html_BR();
addSubmitButton(F("Connect"), "");
html_end_table();
}
bool handle_setup_connectingStage(byte refreshCount) {
@@ -198,6 +373,7 @@ bool handle_setup_connectingStage(byte refreshCount) {
// safe_strncpy(SecuritySettings.WifiSSID, "ssid", sizeof(SecuritySettings.WifiSSID));
// SecuritySettings.WifiKey[0] = 0;
addButton(F("/setup"), F("Back to Setup"));
html_TR_TD();
html_BR();
WiFiEventData.wifiSetupConnect = false;
return false;
@@ -228,35 +404,4 @@ bool handle_setup_connectingStage(byte refreshCount) {
return true;
}
void handle_setup_finish() {
navMenuIndex = MENU_INDEX_TOOLS;
sendHeadandTail_stdtemplate(_HEAD);
addHtmlError(SaveSettings());
html_add_form();
html_table_class_normal();
html_TR();
addFormHeader(F("WiFi Setup Complete"));
handle_sysinfo_Network();
addFormSeparator(2);
html_TR_TD();
html_TD();
if (!clientIPinSubnet()) {
String host = formatIP(NetworkLocalIP());
String url = F("http://");
url += host;
url += F("/config");
addButton(url, host);
}
html_end_table();
html_end_form();
WiFiEventData.wifiSetup = false;
sendHeadandTail_stdtemplate(_TAIL);
}
#endif // ifdef WEBSERVER_SETUP
-2
View File
@@ -16,8 +16,6 @@ void handle_setup_scan_and_show(const String& ssid, const String& other, const S
bool handle_setup_connectingStage(byte refreshCount);
void handle_setup_finish();
#endif // ifdef WEBSERVER_SETUP
+31
View File
@@ -32,6 +32,11 @@
#include "../Static/WebStaticData.h"
#ifdef USES_MQTT
# include "../Globals/MQTT.h"
# include "../Helpers/PeriodicalActions.h" // For finding enabled MQTT controller
#endif
#ifdef ESP32
# include <esp_partition.h>
#endif // ifdef ESP32
@@ -118,6 +123,8 @@ void handle_sysinfo_json() {
json_prop(F("connected"), getValue(LabelType::CONNECTED));
json_prop(F("ldr"), getValue(LabelType::LAST_DISC_REASON_STR));
json_number(F("reconnects"), getValue(LabelType::NUMBER_RECONNECTS));
json_prop(F("ssid1"), getValue(LabelType::WIFI_STORED_SSID1));
json_prop(F("ssid2"), getValue(LabelType::WIFI_STORED_SSID2));
json_close();
# ifdef HAS_ETHERNET
@@ -259,6 +266,8 @@ void handle_sysinfo() {
handle_sysinfo_SystemStatus();
handle_sysinfo_NetworkServices();
handle_sysinfo_ESP_Board();
handle_sysinfo_Storage();
@@ -451,10 +460,13 @@ void handle_sysinfo_Network() {
{
addRowLabel(LabelType::LAST_DISCONNECT_REASON);
addHtml(getValue(LabelType::LAST_DISC_REASON_STR));
addRowLabelValue(LabelType::WIFI_STORED_SSID1);
addRowLabelValue(LabelType::WIFI_STORED_SSID2);
}
addRowLabelValue(LabelType::STA_MAC);
addRowLabelValue(LabelType::AP_MAC);
html_TR();
}
void handle_sysinfo_WiFiSettings() {
@@ -472,6 +484,8 @@ void handle_sysinfo_WiFiSettings() {
addRowLabelValue(LabelType::WIFI_CUR_TX_PWR);
addRowLabelValue(LabelType::WIFI_SENS_MARGIN);
addRowLabelValue(LabelType::WIFI_SEND_AT_MAX_TX_PWR);
addRowLabelValue(LabelType::WIFI_NR_EXTRA_SCANS);
addRowLabelValue(LabelType::WIFI_PERIODICAL_SCAN);
}
void handle_sysinfo_Firmware() {
@@ -507,6 +521,23 @@ void handle_sysinfo_SystemStatus() {
# endif // ifdef FEATURE_SD
}
void handle_sysinfo_NetworkServices() {
addTableSeparator(F("Network Services"), 2, 3);
addRowLabel(F("Network Connected"));
addEnabled(NetworkConnected());
addRowLabel(F("NTP Initialized"));
addEnabled(statusNTPInitialized);
#ifdef USES_MQTT
if (validControllerIndex(firstEnabledMQTT_ControllerIndex())) {
addRowLabel(F("MQTT Client Connected"));
addEnabled(MQTTclient_connected);
}
#endif
}
void handle_sysinfo_ESP_Board() {
addTableSeparator(F("ESP Board"), 2, 3);
+2
View File
@@ -33,6 +33,8 @@ void handle_sysinfo_Firmware();
void handle_sysinfo_SystemStatus();
void handle_sysinfo_NetworkServices();
void handle_sysinfo_ESP_Board();
void handle_sysinfo_Storage();
+124 -15
View File
@@ -39,6 +39,7 @@
#include "../../ESPEasy-Globals.h"
#include "../../_Plugin_Helper.h"
#include "../../ESPEasy_common.h"
#include "../DataStructs/TimingStats.h"
@@ -62,6 +63,47 @@
#include "../Static/WebStaticData.h"
// Determine what pages should be visible
#ifndef MENU_INDEX_MAIN_VISIBLE
# define MENU_INDEX_MAIN_VISIBLE true
#endif // ifndef MENU_INDEX_MAIN_VISIBLE
#ifndef MENU_INDEX_CONFIG_VISIBLE
# define MENU_INDEX_CONFIG_VISIBLE true
#endif // ifndef MENU_INDEX_CONFIG_VISIBLE
#ifndef MENU_INDEX_CONTROLLERS_VISIBLE
# define MENU_INDEX_CONTROLLERS_VISIBLE true
#endif // ifndef MENU_INDEX_CONTROLLERS_VISIBLE
#ifndef MENU_INDEX_HARDWARE_VISIBLE
# define MENU_INDEX_HARDWARE_VISIBLE true
#endif // ifndef MENU_INDEX_HARDWARE_VISIBLE
#ifndef MENU_INDEX_DEVICES_VISIBLE
# define MENU_INDEX_DEVICES_VISIBLE true
#endif // ifndef MENU_INDEX_DEVICES_VISIBLE
#ifndef MENU_INDEX_RULES_VISIBLE
# define MENU_INDEX_RULES_VISIBLE true
#endif // ifndef MENU_INDEX_RULES_VISIBLE
#ifndef MENU_INDEX_NOTIFICATIONS_VISIBLE
# define MENU_INDEX_NOTIFICATIONS_VISIBLE true
#endif // ifndef MENU_INDEX_NOTIFICATIONS_VISIBLE
#ifndef MENU_INDEX_TOOLS_VISIBLE
# define MENU_INDEX_TOOLS_VISIBLE true
#endif // ifndef MENU_INDEX_TOOLS_VISIBLE
#if defined(NOTIFIER_SET_NONE) && defined(MENU_INDEX_NOTIFICATIONS_VISIBLE)
#undef MENU_INDEX_NOTIFICATIONS_VISIBLE
#define MENU_INDEX_NOTIFICATIONS_VISIBLE false
#endif
void safe_strncpy_webserver_arg(char *dest, const String& arg, size_t max_size) {
if (web_server.hasArg(arg)) {
safe_strncpy(dest, web_server.arg(arg).c_str(), max_size);
@@ -209,6 +251,26 @@ size_t streamFile_htmlEscape(const String& fileName)
return size;
}
bool captivePortal() {
if (!isIP(web_server.hostHeader()) && web_server.hostHeader() != (NetworkGetHostname() + F(".local"))) {
const bool fromAP = web_server.client().localIP() == apIP;
String redirectURL = F("http://");
redirectURL += web_server.client().localIP().toString();
#ifdef WEBSERVER_SETUP
if (fromAP && !SecuritySettings.hasWiFiCredentials()) {
redirectURL += F("/setup");
}
#endif
web_server.sendHeader(F("Location"), redirectURL, true);
web_server.send(302, F("text/plain"), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
web_server.client().stop(); // Stop is needed because we sent no content length
return true;
}
return false;
}
// ********************************************************************************
// Web Interface init
// ********************************************************************************
@@ -222,7 +284,11 @@ void WebServerInit()
// Prepare webserver pages
#ifdef WEBSERVER_ROOT
web_server.on("/", handle_root);
web_server.on(F("/"), handle_root);
// Entries for several captive portal URLs.
// Maybe not needed. Might be handled by notFound handler.
web_server.on(F("/generate_204"), handle_root); //Android captive portal.
web_server.on(F("/fwlink"), handle_root); //Microsoft captive portal.
#endif // ifdef WEBSERVER_ROOT
#ifdef WEBSERVER_ADVANCED
web_server.on(F("/advanced"), handle_advanced);
@@ -386,10 +452,15 @@ void getWebPageTemplateDefault(const String& tmplName, String& tmpl)
if (tmplName == F("TmplAP"))
{
getWebPageTemplateDefaultHead(tmpl, !addMeta, !addJS);
tmpl += F("<body>"
"<header class='apheader'>"
"<h1>Welcome to ESP Easy Mega AP</h1>"
"</header>");
#ifndef WEBPAGE_TEMPLATE_AP_HEADER
tmpl += F("<body><header class='apheader'>"
"<h1>Welcome to ESP Easy Mega AP</h1>");
#else
tmpl += F(WEBPAGE_TEMPLATE_AP_HEADER);
#endif
tmpl += F("</header>");
getWebPageTemplateDefaultContentSection(tmpl);
getWebPageTemplateDefaultFooter(tmpl);
}
@@ -437,10 +508,17 @@ void getWebPageTemplateDefaultHead(String& tmpl, bool addMeta, bool addJS) {
}
void getWebPageTemplateDefaultHeader(String& tmpl, const String& title, bool addMenu) {
tmpl += F("<header class='headermenu'>"
"<h1>ESP Easy Mega: ");
tmpl += title;
tmpl += F("</h1><BR>");
{
String tmp;
#ifndef WEBPAGE_TEMPLATE_DEFAULT_HEADER
tmp = F("<header class='headermenu'><h1>ESP Easy Mega: {{title}}</h1><BR>");
#else // ifndef WEBPAGE_TEMPLATE_DEFAULT_HEADER
tmp = F(WEBPAGE_TEMPLATE_DEFAULT_HEADER);
#endif // ifndef WEBPAGE_TEMPLATE_DEFAULT_HEADER
tmp.replace(F("{{title}}"), title);
tmpl += tmp;
}
if (addMenu) { tmpl += F("{{menu}}"); }
tmpl += F("</header>");
@@ -457,12 +535,16 @@ void getWebPageTemplateDefaultContentSection(String& tmpl) {
}
void getWebPageTemplateDefaultFooter(String& tmpl) {
#ifndef WEBPAGE_TEMPLATE_DEFAULT_FOOTER
tmpl += F("<footer>"
"<br>"
"<h6>Powered by <a href='http://www.letscontrolit.com' style='font-size: 15px; text-decoration: none'>Let's Control It</a> community</h6>"
"</footer>"
"</body></html>"
);
#else // ifndef WEBPAGE_TEMPLATE_DEFAULT_FOOTER
tmpl += F(WEBPAGE_TEMPLATE_DEFAULT_FOOTER);
#endif // ifndef WEBPAGE_TEMPLATE_DEFAULT_FOOTER
}
void getErrorNotifications() {
@@ -532,6 +614,21 @@ String getGpMenuURL(byte index) {
return "";
}
bool GpMenuVisible(byte index) {
switch (index) {
case MENU_INDEX_MAIN: return MENU_INDEX_MAIN_VISIBLE;
case MENU_INDEX_CONFIG: return MENU_INDEX_CONFIG_VISIBLE;
case MENU_INDEX_CONTROLLERS: return MENU_INDEX_CONTROLLERS_VISIBLE;
case MENU_INDEX_HARDWARE: return MENU_INDEX_HARDWARE_VISIBLE;
case MENU_INDEX_DEVICES: return MENU_INDEX_DEVICES_VISIBLE;
case MENU_INDEX_RULES: return MENU_INDEX_RULES_VISIBLE;
case MENU_INDEX_NOTIFICATIONS: return MENU_INDEX_NOTIFICATIONS_VISIBLE;
case MENU_INDEX_TOOLS: return MENU_INDEX_TOOLS_VISIBLE;
}
return false;
}
void getWebPageTemplateVar(const String& varName)
{
// serialPrint(varName); serialPrint(" : free: "); serialPrint(ESP.getFreeHeap()); serialPrint("var len before: "); serialPrint
@@ -554,6 +651,10 @@ void getWebPageTemplateVar(const String& varName)
for (byte i = 0; i < 8; i++)
{
if (!GpMenuVisible(i)) {
// hide menu item
continue;
}
if ((i == MENU_INDEX_RULES) && !Settings.UseRules) { // hide rules menu item
continue;
}
@@ -829,7 +930,7 @@ void addTaskValueSelect(const String& name, int choice, taskIndex_t TaskIndex)
// ********************************************************************************
// Login state check
// ********************************************************************************
boolean isLoggedIn()
bool isLoggedIn(bool mustProvideLogin)
{
String www_username = F(DEFAULT_ADMIN_USERNAME);
@@ -837,6 +938,9 @@ boolean isLoggedIn()
if (SecuritySettings.Password[0] == 0) { return true; }
if (!mustProvideLogin) {
return false;
}
if (!web_server.authenticate(www_username.c_str(), SecuritySettings.Password))
// Basic Auth Method with Custom realm and Failure Response
@@ -865,7 +969,7 @@ boolean isLoggedIn()
String getControllerSymbol(byte index)
{
String ret = F("<p style='font-size:20px'>&#");
String ret = F("<p style='font-size:20px; background: #00000000;'>&#");
ret += 10102 + index;
ret += F(";</p>");
@@ -900,11 +1004,12 @@ void addSVG_param(const String& key, const String& value) {
addHtml(html);
}
void createSvgRect_noStroke(unsigned int fillColor, float xoffset, float yoffset, float width, float height, float rx, float ry) {
createSvgRect(fillColor, fillColor, xoffset, yoffset, width, height, 0, rx, ry);
void createSvgRect_noStroke(const String& classname, unsigned int fillColor, float xoffset, float yoffset, float width, float height, float rx, float ry) {
createSvgRect(classname, fillColor, fillColor, xoffset, yoffset, width, height, 0, rx, ry);
}
void createSvgRect(unsigned int fillColor,
void createSvgRect(const String& classname,
unsigned int fillColor,
unsigned int strokeColor,
float xoffset,
float yoffset,
@@ -914,6 +1019,9 @@ void createSvgRect(unsigned int fillColor,
float rx,
float ry) {
addHtml(F("<rect"));
if (classname.length() != 0) {
addSVG_param(F("class"), classname);
}
addSVG_param(F("fill"), formatToHex(fillColor, F("#")));
if (!approximatelyEqual(strokeWidth, 0)) {
@@ -1017,8 +1125,9 @@ void getWiFi_RSSI_icon(int rssi, int width_pixels)
for (int i = 0; i < nbars; ++i) {
unsigned int color = i < nbars_filled ? 0x0 : 0xa1a1a1; // Black/Grey
String classname = i < nbars_filled ? F("bar_highlight") : F("bar_dimmed");
int barHeight = (i + 1) * bar_height_step;
createSvgRect_noStroke(color, i * (barWidth + white_between_bar) * scale, 100 - barHeight, barWidth, barHeight, 0, 0);
createSvgRect_noStroke(classname, color, i * (barWidth + white_between_bar) * scale, 100 - barHeight, barWidth, barHeight, 0, 0);
}
addHtml(F("</svg>\n"));
}
+13 -3
View File
@@ -44,6 +44,12 @@ size_t streamFile_htmlEscape(const String& fileName);
void WebServerInit();
// ********************************************************************************
// Redirect to captive portal if we got a request for another domain.
// Return true in that case so the page handler does not try to handle the request again.
// ********************************************************************************
bool captivePortal();
void setWebserverRunning(bool state);
void getWebPageTemplateDefault(const String& tmplName,
@@ -69,6 +75,8 @@ String getGpMenuLabel(byte index);
String getGpMenuURL(byte index);
bool GpMenuVisible(byte index);
void getWebPageTemplateVar(const String& varName);
void writeDefaultCSS(void);
@@ -126,7 +134,7 @@ void addTaskValueSelect(const String& name,
// ********************************************************************************
// Login state check
// ********************************************************************************
boolean isLoggedIn();
bool isLoggedIn(bool mustProvideLogin = true);
String getControllerSymbol(byte index);
@@ -139,7 +147,8 @@ void addSVG_param(const String& key,
void addSVG_param(const String& key,
const String& value);
void createSvgRect_noStroke(unsigned int fillColor,
void createSvgRect_noStroke(const String& classname,
unsigned int fillColor,
float xoffset,
float yoffset,
float width,
@@ -147,7 +156,8 @@ void createSvgRect_noStroke(unsigned int fillColor,
float rx,
float ry);
void createSvgRect(unsigned int fillColor,
void createSvgRect(const String& classname,
unsigned int fillColor,
unsigned int strokeColor,
float xoffset,
float yoffset,
+5 -6
View File
@@ -5,6 +5,7 @@
#include "../WebServer/HTML_wrappers.h"
#include "../ESPEasyCore/ESPEasyWifi.h"
#include "../Globals/WiFi_AP_Candidates.h"
#include "../Helpers/StringGenerator_WiFi.h"
@@ -58,6 +59,7 @@ void handle_wifiscanner() {
WiFiMode_t cur_wifimode = WiFi.getMode();
WifiScan(false);
int8_t scanCompleteStatus = WiFi_AP_Candidates.scanComplete();
setWifiMode(cur_wifimode);
navMenuIndex = MENU_INDEX_TOOLS;
@@ -70,20 +72,17 @@ void handle_wifiscanner() {
html_table_header(F("Network info"));
html_table_header(F("RSSI"), 50);
const int8_t scanCompleteStatus = WiFi.scanComplete();
if (scanCompleteStatus <= 0) {
addHtml(F("No Access Points found"));
}
else
{
for (int i = 0; i < scanCompleteStatus; ++i)
for (auto it = WiFi_AP_Candidates.scanned_begin(); it != WiFi_AP_Candidates.scanned_end(); ++it)
{
html_TR_TD();
int32_t rssi = 0;
addHtml(formatScanResult(i, "<TD>", rssi));
addHtml(it->toString(F("<TD>")));
html_TD();
getWiFi_RSSI_icon(rssi, 45);
getWiFi_RSSI_icon(it->rssi, 45);
}
}