mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Merge remote-tracking branch 'upstream/mega' into UweKaditz
This commit is contained in:
@@ -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
|
||||
===================
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
// --- 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_WIFI_INCLUDE_HIDDEN_SSID false // Allow to connect to hidden SSID APs
|
||||
#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 +143,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 +172,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
@@ -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();
|
||||
|
||||
@@ -15,6 +15,7 @@ void flushAndDisconnectAllClients();
|
||||
|
||||
float getCPUload();
|
||||
int getLoopCountPerSec();
|
||||
int getUptimeMinutes();
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
@@ -211,7 +211,7 @@ float P026_get_value(int type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
value = (wdcounter / 2);
|
||||
value = getUptimeMinutes();
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -42,6 +42,15 @@
|
||||
#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_WIFI_INCLUDE_HIDDEN_SSID
|
||||
#define DEFAULT_WIFI_INCLUDE_HIDDEN_SSID false // Allow to connect to hidden SSID APs
|
||||
#endif
|
||||
#ifndef DEFAULT_USE_STATIC_IP
|
||||
#define DEFAULT_USE_STATIC_IP false // (true|false) enabled or disabled static IP
|
||||
#endif
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1539,5 +1539,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -457,6 +457,9 @@ bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, cons
|
||||
}
|
||||
|
||||
if (cmd_s_lower.startsWith(F("bit"))) {
|
||||
#define bitSetULL(value, bit) ((value) |= (1ULL << (bit)))
|
||||
#define bitClearULL(value, bit) ((value) &= ~(1ULL << (bit)))
|
||||
#define bitWriteULL(value, bit, bitvalue) (bitvalue ? bitSetULL(value, bit) : bitClearULL(value, bit))
|
||||
uint32_t bitnr = 0;
|
||||
uint64_t iarg2 = 0;
|
||||
|
||||
@@ -470,11 +473,11 @@ bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, cons
|
||||
} else if (cmd_s_lower.equals(F("bitset"))) {
|
||||
// Syntax like {bitset:0:122} to set least significant bit of the given nr '122' to '1' => '123'
|
||||
result = iarg2;
|
||||
bitSet(result, bitnr);
|
||||
bitSetULL(result, bitnr);
|
||||
} else if (cmd_s_lower.equals(F("bitclear"))) {
|
||||
// Syntax like {bitclear:0:123} to set least significant bit of the given nr '123' to '0' => '122'
|
||||
result = iarg2;
|
||||
bitClear(result, bitnr);
|
||||
bitClearULL(result, bitnr);
|
||||
} else if (cmd_s_lower.equals(F("bitwrite"))) {
|
||||
uint32_t iarg3 = 0;
|
||||
|
||||
@@ -482,7 +485,7 @@ bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, cons
|
||||
if (validUIntFromString(arg3, iarg3)) {
|
||||
const int bitvalue = (iarg3 & 1); // Only use the last bit of the given parameter
|
||||
result = iarg2;
|
||||
bitWrite(result, bitnr, bitvalue);
|
||||
bitWriteULL(result, bitnr, bitvalue);
|
||||
} else {
|
||||
// Need 3 parameters, but 3rd one is not a valid uint
|
||||
return false;
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
#include "../Globals/Settings.h"
|
||||
#include "../Globals/WiFi_AP_Candidates.h"
|
||||
#include "../Helpers/ESPEasy_time_calc.h"
|
||||
#include "../Helpers/Misc.h"
|
||||
#include "../Helpers/Networking.h"
|
||||
#include "../Helpers/StringConverter.h"
|
||||
#include "../Helpers/StringGenerator_WiFi.h"
|
||||
|
||||
#include "../../ESPEasy_fdwdecl.h"
|
||||
|
||||
|
||||
// ********************************************************************************
|
||||
// WiFi state
|
||||
@@ -134,6 +137,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 +162,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 +175,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 +229,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 +316,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 +334,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;
|
||||
}
|
||||
|
||||
@@ -358,6 +367,7 @@ void resetWiFi() {
|
||||
// Don't reset WiFi too often
|
||||
return;
|
||||
}
|
||||
FeedSW_watchdog();
|
||||
WiFiEventData.clearAll();
|
||||
WifiDisconnect();
|
||||
|
||||
@@ -505,10 +515,12 @@ void SetWiFiTXpower(float dBm, float rssi) {
|
||||
delay(0);
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
if (WiFiEventData.wifi_TX_pwr != maxTXpwr) {
|
||||
static float last_log = -1;
|
||||
if (WiFiEventData.wifi_TX_pwr != last_log) {
|
||||
last_log = WiFiEventData.wifi_TX_pwr;
|
||||
const int TX_pwr_int = WiFiEventData.wifi_TX_pwr * 4;
|
||||
const int maxTXpwr_int = maxTXpwr * 4;
|
||||
if (TX_pwr_int != maxTXpwr_int) {
|
||||
static int last_log = -1;
|
||||
if (TX_pwr_int != last_log) {
|
||||
last_log = TX_pwr_int;
|
||||
String log = F("WiFi : Set TX power to ");
|
||||
log += String(dBm, 0);
|
||||
log += F("dBm");
|
||||
@@ -598,23 +610,87 @@ 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();
|
||||
FeedSW_watchdog();
|
||||
}
|
||||
--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) {
|
||||
FeedSW_watchdog();
|
||||
processScanDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ********************************************************************************
|
||||
@@ -624,8 +700,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 +716,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 +764,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 +828,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) {
|
||||
|
||||
@@ -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,36 @@ void processConnect() {
|
||||
WiFiEventData.setWiFiConnected();
|
||||
++WiFiEventData.wifi_reconnects;
|
||||
|
||||
if (WiFi_AP_Candidates.getCurrent().isEmergencyFallback) {
|
||||
#ifdef CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS
|
||||
const bool mustResetCredentials = CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS;
|
||||
#else
|
||||
const bool mustResetCredentials = false;
|
||||
#endif
|
||||
#ifdef CUSTOM_EMERGENCY_FALLBACK_START_AP
|
||||
const bool mustStartAP = CUSTOM_EMERGENCY_FALLBACK_START_AP;
|
||||
#else
|
||||
const bool mustStartAP = false;
|
||||
#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 +420,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 +447,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 +486,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,4 +1,4 @@
|
||||
#include "NetworkState.h"
|
||||
#include "../Globals/NetworkState.h"
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ void ResetFactory()
|
||||
str2ip((char *)DEFAULT_GW, Settings.Gateway);
|
||||
str2ip((char *)DEFAULT_SUBNET, Settings.Subnet);
|
||||
#endif // if DEFAULT_USE_STATIC_IP
|
||||
Settings.IncludeHiddenSSID(DEFAULT_WIFI_INCLUDE_HIDDEN_SSID);
|
||||
}
|
||||
|
||||
Settings.clearNotifications();
|
||||
@@ -137,6 +138,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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ESPEasy_Storage.h"
|
||||
#include "../Helpers/ESPEasy_Storage.h"
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "../Helpers/ESPEasyRTC.h"
|
||||
#include "../Helpers/ESPEasy_FactoryDefault.h"
|
||||
#include "../Helpers/ESPEasy_Storage.h"
|
||||
#include "../Helpers/ESPEasy_time_calc.h"
|
||||
#include "../Helpers/FS_Helper.h"
|
||||
#include "../Helpers/Hardware.h"
|
||||
@@ -277,6 +276,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 +361,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 +398,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();
|
||||
|
||||
@@ -53,7 +53,9 @@ bool GarbageCollection();
|
||||
/********************************************************************************************\
|
||||
Save settings to file system
|
||||
\*********************************************************************************************/
|
||||
String SaveSettings(void);
|
||||
String SaveSettings();
|
||||
|
||||
String SaveSecuritySettings();
|
||||
|
||||
void afterloadSettings();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -258,6 +258,13 @@ void reboot(ESPEasy_Scheduler::IntendedRebootReason_e reason) {
|
||||
#endif // if defined(ESP32)
|
||||
}
|
||||
|
||||
void FeedSW_watchdog()
|
||||
{
|
||||
#ifdef ESP8266
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SendValueLogger(taskIndex_t TaskIndex)
|
||||
{
|
||||
#if !defined(BUILD_NO_DEBUG) || defined(FEATURE_SD)
|
||||
|
||||
@@ -122,6 +122,7 @@ void delayedReboot(int rebootDelay, ESPEasy_Scheduler::IntendedRebootReason_e re
|
||||
|
||||
void reboot(ESPEasy_Scheduler::IntendedRebootReason_e reason);
|
||||
|
||||
void FeedSW_watchdog();
|
||||
|
||||
void SendValueLogger(taskIndex_t TaskIndex);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
#include "../Helpers/StringConverter.h"
|
||||
|
||||
|
||||
RulesCalculate_t::RulesCalculate_t() {
|
||||
for (int i = 0; i < STACK_SIZE; ++i) {
|
||||
globalstack[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Instance of the RulesCalculate to perform calculations
|
||||
These functions are wrapped in a class to
|
||||
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
|
||||
double globalstack[STACK_SIZE];
|
||||
double *sp = globalstack - 1;
|
||||
double *sp_max = &globalstack[STACK_SIZE - 1];
|
||||
const double *sp_max = &globalstack[STACK_SIZE - 1];
|
||||
|
||||
// Check if it matches part of a number (identifier)
|
||||
// @param oc Previous character
|
||||
@@ -99,6 +99,8 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
RulesCalculate_t();
|
||||
|
||||
CalculateReturnCode doCalculate(const char *input,
|
||||
double *result);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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("ℹ") : F("❔"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void addRTDPluginButton(pluginID_t taskDeviceNumber) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -6,11 +6,15 @@
|
||||
|
||||
#ifdef WEBSERVER_ROOT
|
||||
|
||||
|
||||
|
||||
// ********************************************************************************
|
||||
// Web Interface root page
|
||||
// ********************************************************************************
|
||||
void handle_root();
|
||||
|
||||
|
||||
|
||||
#endif // ifdef WEBSERVER_ROOT
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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 +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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user