mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-11 17:14:27 +00:00
Merge branch 'mega' into feature/builds-rename-test-to-collection
This commit is contained in:
@@ -126,7 +126,7 @@ To see what plugins are included in which collection set, you can find that on t
|
||||
|
||||
## Documentation & more info
|
||||
|
||||
Our new, in-depth documentation can be found at [ESPEasy.readthedocs.io](https://espeasy.readthedocs.io/en/latest/). Automatically built, so always up-to-date according to the contributed contents. The old Wiki documention can be found at [letscontrolit.com/wiki](https://www.letscontrolit.com/wiki/index.php?title=ESPEasy).
|
||||
Our new, in-depth documentation can be found at [ESPEasy.readthedocs.io](https://espeasy.readthedocs.io/en/latest/). Automatically built, so always up-to-date according to the contributed contents. The old Wiki documentation can be found at [letscontrolit.com/wiki](https://www.letscontrolit.com/wiki/index.php?title=ESPEasy).
|
||||
|
||||
Additional details and discussion are on the "Experimental" section of the forum: https://www.letscontrolit.com/forum/viewforum.php?f=18
|
||||
|
||||
|
||||
@@ -127,6 +127,23 @@
|
||||
GPIO,2,0
|
||||
endon
|
||||
|
||||
"
|
||||
"
|
||||
``http#hostname=404``
|
||||
Added: 2022/07/23
|
||||
Triggered as a "return value" when performing a HTTP call to some host.
|
||||
The event value is the HTTP return code.
|
||||
The ``hostname`` is replaced by the hostname used in the HTTP call.
|
||||
","
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
on http#192.168.1.2 do
|
||||
if %eventvalue1%!=200
|
||||
LogEntry,"HTTP error: %eventvalue1% to: %eventpar%:
|
||||
endif
|
||||
endon
|
||||
|
||||
"
|
||||
"
|
||||
``MQTT#Connected``
|
||||
|
||||
@@ -1813,6 +1813,14 @@ There is the following workaround:
|
||||
|
||||
SendToHTTP 192.168.0.243,8080,/json.htm?type=param=switchlight&command&idx=174&switchcmd=On
|
||||
|
||||
Added: 2022/07/23
|
||||
|
||||
* ``SendToHTTP`` can now also be called with a full URL starting with ``http://``, so no longer the host, port and uri have to be separated. (it is still possible of course)
|
||||
* HTTP return value will be made available as event to be evaluated in the rules. Example event: ``http#hostname=404``
|
||||
* Calls made to a HTTP server can now also follow redirects. (GET and HEAD calls only) This has to be enabled in Tools->Advanced page.
|
||||
* Host name can contain user credentials. For example: ``http://username:pass@hostname:portnr/foo.html``
|
||||
* HTTP user credentials now can handle Basic Auth and Digest Auth.
|
||||
|
||||
|
||||
Dew Point for temp/humidity sensors (BME280 for example)
|
||||
--------------------------------------------------------
|
||||
|
||||
@@ -230,6 +230,7 @@ Rules Settings
|
||||
* Allow Rules Event Reorder - It is best to have the rules blocks for the most frequently occuring events placed at the top of the first rules file. (also for frequently happening events, which you don't want to act on) The cached event positions can be reordered in memory based on how often an event was matched. (Enabled by default, Added 2022/04/17, disabled 2022/06/24)
|
||||
* Tolerant last parameter - When checked, the last parameter of a command will have less strict parsing.
|
||||
* SendToHTTP wait for ack - When checked, the command SendToHTTP will wait for an acknowledgement from the server.
|
||||
* SendToHTTP Follow Redirects - When checked, HTTP calls may follow redirects. Strict RFC2616, only requests using GET or HEAD methods will be redirected (using the same method), since the RFC requires end-user confirmation in other cases.
|
||||
|
||||
Time Source
|
||||
-----------
|
||||
|
||||
@@ -94,7 +94,8 @@ extern float vcc;
|
||||
extern bool shouldReboot;
|
||||
extern bool firstLoop;
|
||||
|
||||
|
||||
// This is read from the settings at boot.
|
||||
// Even if this setting is changed, you need to reboot to activate the changes.
|
||||
extern boolean UseRTOSMultitasking;
|
||||
|
||||
#endif /* ESPEASY_GLOBALS_H_ */
|
||||
|
||||
+38
-42
@@ -21,22 +21,44 @@
|
||||
const __FlashStringHelper* Command_HTTP_SendToHTTP(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
if (NetworkConnected()) {
|
||||
String user, pass;
|
||||
String host = parseStringKeepCase(Line, 2);
|
||||
const int pos_at = host.indexOf('@');
|
||||
String user, pass, host, file, path;
|
||||
uint16_t port;
|
||||
|
||||
if (pos_at != -1) {
|
||||
user = host.substring(0, pos_at);
|
||||
host = host.substring(pos_at + 1);
|
||||
const int pos_colon = user.indexOf(':');
|
||||
const String arg1 = parseStringKeepCase(Line, 2);
|
||||
|
||||
if (pos_colon != -1) {
|
||||
pass = user.substring(pos_colon + 1);
|
||||
user = user.substring(0, pos_colon);
|
||||
if (arg1.indexOf('/') != -1) {
|
||||
// Full url given
|
||||
path = splitURL(arg1, user, pass, host, port, file);
|
||||
} else {
|
||||
// Command arguments are split into: host, port, url
|
||||
if (!splitUserPass_HostPortString(
|
||||
arg1,
|
||||
user,
|
||||
pass,
|
||||
host,
|
||||
port))
|
||||
{
|
||||
return return_command_failed();
|
||||
}
|
||||
}
|
||||
|
||||
const int port = parseCommandArgumentInt(Line, 2);
|
||||
const int port_arg = event->Par2;
|
||||
|
||||
if ((port_arg > 0) && (port_arg < 65536)) {
|
||||
port = port_arg;
|
||||
} else {
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
String log = F("SendToHTTP: Invalid port argument: ");
|
||||
log += port_arg;
|
||||
log += F(" will use: ");
|
||||
log += port;
|
||||
addLogMove(LOG_LEVEL_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME TD-er: This is not using the tolerant settings option.
|
||||
// String path = tolerantParseStringKeepCase(Line, 4);
|
||||
path = parseStringToEndKeepCase(Line, 4);
|
||||
}
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
@@ -44,29 +66,16 @@ const __FlashStringHelper* Command_HTTP_SendToHTTP(struct EventStruct *event, co
|
||||
log += host;
|
||||
log += F(" port: ");
|
||||
log += port;
|
||||
addLogMove(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
if ((port < 0) || (port > 65535)) { return return_command_failed(); }
|
||||
|
||||
// FIXME TD-er: This is not using the tolerant settings option.
|
||||
// String path = tolerantParseStringKeepCase(Line, 4);
|
||||
const String path = parseStringToEndKeepCase(Line, 4);
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("SendToHTTP: Path: ");
|
||||
log += F(" path: ");
|
||||
log += path;
|
||||
addLogMove(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
|
||||
int httpCode = -1;
|
||||
WiFiClient client;
|
||||
const String res = send_via_http(
|
||||
F("Command_HTTP_SendToHTTP"),
|
||||
WiFiClient client;
|
||||
send_via_http(
|
||||
F("SendToHTTP"),
|
||||
client,
|
||||
CONTROLLER_CLIENTTIMEOUT_MAX,
|
||||
user,
|
||||
@@ -80,22 +89,9 @@ const __FlashStringHelper* Command_HTTP_SendToHTTP(struct EventStruct *event, co
|
||||
httpCode,
|
||||
Settings.SendToHttp_ack());
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String logstr;
|
||||
logstr += F("SendToHTTP: ");
|
||||
logstr += httpCode;
|
||||
|
||||
if (!res.isEmpty()) {
|
||||
logstr += F(" Received reply: ");
|
||||
logstr += res;
|
||||
}
|
||||
addLog(LOG_LEVEL_INFO, logstr);
|
||||
}
|
||||
|
||||
if ((httpCode >= 100) && (httpCode < 300)) {
|
||||
return return_command_success();
|
||||
}
|
||||
addLog(LOG_LEVEL_ERROR, String(F("SendToHTTP: HTTP code: ")) + httpCode);
|
||||
} else {
|
||||
addLog(LOG_LEVEL_ERROR, F("SendToHTTP Not connected to network"));
|
||||
}
|
||||
|
||||
@@ -391,6 +391,7 @@ bool executeInternalCommand(command_case_data & data)
|
||||
COMMAND_CASE_A( "provisionnotification", Command_Provisioning_Notification, 0); // Provisioning.h
|
||||
COMMAND_CASE_A( "provisionprovision", Command_Provisioning_Provision, 0); // Provisioning.h
|
||||
COMMAND_CASE_A( "provisionrules", Command_Provisioning_Rules, 1); // Provisioning.h
|
||||
COMMAND_CASE_A( "provisionfirmware", Command_Provisioning_Firmware, 1); // Provisioning.h
|
||||
#endif
|
||||
COMMAND_CASE_A( "pulse", Command_GPIO_Pulse, 3); // GPIO.h
|
||||
#ifdef USES_MQTT
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
# include "../DataTypes/ESPEasyFileType.h"
|
||||
# include "../DataStructs/ESPEasy_EventStruct.h"
|
||||
# include "../Helpers/ESPEasy_Storage.h"
|
||||
|
||||
# include "../Helpers/Networking.h"
|
||||
# include "../Helpers/StringConverter.h"
|
||||
|
||||
String Command_Provisioning_Config(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
@@ -39,4 +40,15 @@ String Command_Provisioning_Rules(struct EventStruct *event, const char *Line)
|
||||
return downloadFileType(FileType::RULES_TXT, event->Par1 - 1);
|
||||
}
|
||||
|
||||
String Command_Provisioning_Firmware(struct EventStruct *event, const char *Line)
|
||||
{
|
||||
const String url = parseStringToEndKeepCase(Line, 2);
|
||||
String error;
|
||||
if (downloadFirmware(url, error)) {
|
||||
// TODO TD-er: send events
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
#endif // ifdef USE_CUSTOM_PROVISIONING
|
||||
|
||||
@@ -18,6 +18,8 @@ String Command_Provisioning_Provision(struct EventStruct *event,
|
||||
String Command_Provisioning_Rules(struct EventStruct *event,
|
||||
const char *Line);
|
||||
|
||||
String Command_Provisioning_Firmware(struct EventStruct *event,
|
||||
const char *Line);
|
||||
|
||||
#endif // ifdef USE_CUSTOM_PROVISIONING
|
||||
|
||||
|
||||
@@ -136,6 +136,11 @@ class SettingsStruct_tmpl
|
||||
bool AllowOTAUnlimited() const;
|
||||
void AllowOTAUnlimited(bool value);
|
||||
|
||||
// Default behavior is to not allow following redirects
|
||||
bool SendToHTTP_follow_redirects() const;
|
||||
void SendToHTTP_follow_redirects(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);
|
||||
|
||||
@@ -295,6 +295,16 @@ void SettingsStruct_tmpl<N_TASKS>::AllowOTAUnlimited(bool value) {
|
||||
bitWrite(VariousBits1, 26, value);
|
||||
}
|
||||
|
||||
template<unsigned int N_TASKS>
|
||||
bool SettingsStruct_tmpl<N_TASKS>::SendToHTTP_follow_redirects() const {
|
||||
return bitRead(VariousBits1, 27);
|
||||
}
|
||||
|
||||
template<unsigned int N_TASKS>
|
||||
void SettingsStruct_tmpl<N_TASKS>::SendToHTTP_follow_redirects(bool value) {
|
||||
bitWrite(VariousBits1, 27, value);
|
||||
}
|
||||
|
||||
template<unsigned int N_TASKS>
|
||||
ExtTimeSource_e SettingsStruct_tmpl<N_TASKS>::ExtTimeSource() const {
|
||||
return static_cast<ExtTimeSource_e>(ExternalTimeSource >> 1);
|
||||
|
||||
@@ -31,6 +31,9 @@ const __FlashStringHelper * getFileName(FileType::Enum filetype) {
|
||||
case FileType::RULES_TXT:
|
||||
// Use getRulesFileName
|
||||
break;
|
||||
case FileType::FIRMWARE:
|
||||
// File name may differ each time.
|
||||
break;
|
||||
|
||||
case FileType::MAX_FILETYPE:
|
||||
break;
|
||||
@@ -68,6 +71,7 @@ bool getDownloadFiletypeChecked(FileType::Enum filetype, unsigned int filenr) {
|
||||
case FileType::PROVISIONING_DAT: isChecked = ResetFactoryDefaultPreference.fetchProvisioningDat(); break;
|
||||
break;
|
||||
|
||||
case FileType::FIRMWARE: // FIXME TD-er: Must decide what to do with firmware description/protection on provisioning settings
|
||||
case FileType::MAX_FILETYPE:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ struct FileType {
|
||||
RULES_TXT,
|
||||
NOTIFICATION_DAT,
|
||||
PROVISIONING_DAT,
|
||||
|
||||
FIRMWARE,
|
||||
|
||||
MAX_FILETYPE
|
||||
};
|
||||
|
||||
@@ -1085,6 +1085,7 @@ void readBootCause() {
|
||||
case TG1WDT_CPU_RESET: lastBootCause = BOOT_CAUSE_EXT_WD; break;
|
||||
case SUPER_WDT_RESET: lastBootCause = BOOT_CAUSE_EXT_WD; break;
|
||||
case GLITCH_RTC_RESET: lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; // FIXME TD-er: Does this need a different reason?
|
||||
case EFUSE_RESET: break; // FIXME TD-er: No idea what may cause this reset reason.
|
||||
# endif // ifdef ESP32S2
|
||||
}
|
||||
}
|
||||
|
||||
+551
-81
@@ -2,13 +2,16 @@
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
#include "../Commands/InternalCommands.h"
|
||||
#include "../CustomBuild/CompiletimeDefines.h"
|
||||
#include "../DataStructs/TimingStats.h"
|
||||
#include "../DataTypes/EventValueSource.h"
|
||||
#include "../ESPEasyCore/ESPEasy_Log.h"
|
||||
#include "../ESPEasyCore/ESPEasy_backgroundtasks.h"
|
||||
#include "../ESPEasyCore/ESPEasyNetwork.h"
|
||||
#include "../ESPEasyCore/ESPEasyWifi.h"
|
||||
#include "../Globals/ESPEasyWiFiEvent.h"
|
||||
#include "../Globals/ESPEasy_Scheduler.h"
|
||||
#include "../Globals/EventQueue.h"
|
||||
#include "../Globals/NetworkState.h"
|
||||
#include "../Globals/Nodes.h"
|
||||
#include "../Globals/Settings.h"
|
||||
@@ -20,7 +23,12 @@
|
||||
#include "../Helpers/StringConverter.h"
|
||||
#include "../Helpers/StringProvider.h"
|
||||
|
||||
#include "../../ESPEasy-Globals.h"
|
||||
|
||||
#include <IPAddress.h>
|
||||
#include <base64.h>
|
||||
#include <MD5Builder.h>
|
||||
|
||||
|
||||
// Generic Networking routines
|
||||
|
||||
@@ -61,8 +69,9 @@ void etharp_gratuitous_r(struct netif *netif) {
|
||||
# endif // ifdef ESP8266
|
||||
# ifdef ESP32
|
||||
# include <HTTPClient.h>
|
||||
# include <Update.h>
|
||||
# endif // ifdef ESP32
|
||||
#endif
|
||||
#endif // ifdef USE_DOWNLOAD
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -76,6 +85,7 @@ void sendSyslog(uint8_t logLevel, const String& message)
|
||||
IPAddress broadcastIP(Settings.Syslog_IP[0], Settings.Syslog_IP[1], Settings.Syslog_IP[2], Settings.Syslog_IP[3]);
|
||||
|
||||
FeedSW_watchdog();
|
||||
|
||||
if (portUDP.beginPacket(broadcastIP, Settings.SyslogPort) == 0) {
|
||||
// problem resolving the hostname or port
|
||||
return;
|
||||
@@ -107,7 +117,7 @@ void sendSyslog(uint8_t logLevel, const String& message)
|
||||
header += hostname;
|
||||
header += F(" EspEasy: ");
|
||||
#ifdef ESP8266
|
||||
portUDP.write(header.c_str(), header.length());
|
||||
portUDP.write(header.c_str(), header.length());
|
||||
#endif // ifdef ESP8266
|
||||
#ifdef ESP32
|
||||
portUDP.write(reinterpret_cast<const uint8_t *>(header.c_str()), header.length());
|
||||
@@ -115,6 +125,7 @@ void sendSyslog(uint8_t logLevel, const String& message)
|
||||
}
|
||||
|
||||
const size_t messageLength = message.length();
|
||||
|
||||
for (size_t i = 0; i < messageLength; ++i) {
|
||||
#ifdef ESP8266
|
||||
portUDP.write(message[i]);
|
||||
@@ -129,7 +140,6 @@ void sendSyslog(uint8_t logLevel, const String& message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if FEATURE_ESPEASY_P2P
|
||||
|
||||
/*********************************************************************************************\
|
||||
@@ -140,6 +150,7 @@ void SendUDPCommand(uint8_t destUnit, const char *data, uint8_t dataLength)
|
||||
if (!NetworkConnected(10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (destUnit != 0)
|
||||
{
|
||||
sendUDP(destUnit, (const uint8_t *)data, dataLength);
|
||||
@@ -170,14 +181,14 @@ void sendUDP(uint8_t unit, const uint8_t *data, uint8_t size)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
|
||||
String log = F("UDP : Send UDP message to ");
|
||||
log += unit;
|
||||
addLogMove(LOG_LEVEL_DEBUG_MORE, log);
|
||||
}
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
statusLED(true);
|
||||
FeedSW_watchdog();
|
||||
@@ -290,9 +301,9 @@ void checkUDP()
|
||||
break;
|
||||
}
|
||||
uint8_t unit = packetBuffer[12];
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
MAC_address mac;
|
||||
uint8_t ip[4];
|
||||
uint8_t ip[4];
|
||||
|
||||
for (uint8_t x = 0; x < 6; x++) {
|
||||
mac.mac[x] = packetBuffer[x + 2];
|
||||
@@ -301,12 +312,13 @@ void checkUDP()
|
||||
for (uint8_t x = 0; x < 4; x++) {
|
||||
ip[x] = packetBuffer[x + 8];
|
||||
}
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
{
|
||||
#ifdef USE_SECOND_HEAP
|
||||
# ifdef USE_SECOND_HEAP
|
||||
HeapSelectIram ephemeral;
|
||||
|
||||
// TD-er: Disabled for now as it is suspect for crashes.
|
||||
#endif
|
||||
# endif // ifdef USE_SECOND_HEAP
|
||||
|
||||
Nodes[unit].age = 0; // Create a new element when not present
|
||||
}
|
||||
@@ -323,11 +335,11 @@ void checkUDP()
|
||||
it->second.build = makeWord(packetBuffer[14], packetBuffer[13]);
|
||||
char tmpNodeName[26] = { 0 };
|
||||
memcpy(&tmpNodeName[0], reinterpret_cast<uint8_t *>(&packetBuffer[15]), 25);
|
||||
tmpNodeName[25] = 0;
|
||||
tmpNodeName[25] = 0;
|
||||
{
|
||||
#ifdef USE_SECOND_HEAP
|
||||
# ifdef USE_SECOND_HEAP
|
||||
HeapSelectIram ephemeral;
|
||||
#endif
|
||||
# endif // ifdef USE_SECOND_HEAP
|
||||
|
||||
it->second.nodeName = tmpNodeName;
|
||||
it->second.nodeName.trim();
|
||||
@@ -341,7 +353,7 @@ void checkUDP()
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
|
||||
String log;
|
||||
@@ -353,7 +365,7 @@ void checkUDP()
|
||||
log += unit;
|
||||
addLogMove(LOG_LEVEL_DEBUG_MORE, log);
|
||||
}
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -482,9 +494,9 @@ void sendSysInfoUDP(uint8_t repeats)
|
||||
// 1 uint8_t node type id
|
||||
|
||||
// send my info to the world...
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, F("UDP : Send Sysinfo message"));
|
||||
#endif // ifndef BUILD_NO_DEBUG
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
for (uint8_t counter = 0; counter < repeats; counter++)
|
||||
{
|
||||
@@ -494,6 +506,7 @@ void sendSysInfoUDP(uint8_t repeats)
|
||||
|
||||
{
|
||||
const MAC_address macread = NetworkMacAddress();
|
||||
|
||||
for (uint8_t x = 0; x < 6; x++) {
|
||||
data[x + 2] = macread.mac[x];
|
||||
}
|
||||
@@ -501,6 +514,7 @@ void sendSysInfoUDP(uint8_t repeats)
|
||||
|
||||
{
|
||||
const IPAddress ip = NetworkLocalIP();
|
||||
|
||||
for (uint8_t x = 0; x < 4; x++) {
|
||||
data[x + 8] = ip[x];
|
||||
}
|
||||
@@ -527,13 +541,15 @@ void sendSysInfoUDP(uint8_t repeats)
|
||||
}
|
||||
|
||||
{
|
||||
#ifdef USE_SECOND_HEAP
|
||||
# ifdef USE_SECOND_HEAP
|
||||
|
||||
// HeapSelectIram ephemeral;
|
||||
// TD-er: disabled for now as it is suspect for crashes.
|
||||
#endif
|
||||
# endif // ifdef USE_SECOND_HEAP
|
||||
|
||||
Nodes[Settings.Unit].age = 0; // Create new node when not already present.
|
||||
}
|
||||
|
||||
// store my own info also in the list
|
||||
NodesMap::iterator it = Nodes.find(Settings.Unit);
|
||||
|
||||
@@ -574,42 +590,42 @@ void SSDP_schema(WiFiClient& client) {
|
||||
(uint16_t)chipId & 0xff);
|
||||
|
||||
client.print(F(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"\r\n"
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
||||
"<specVersion>"
|
||||
"<major>1</major>"
|
||||
"<minor>0</minor>"
|
||||
"</specVersion>"
|
||||
"<URLBase>http://"));
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"\r\n"
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
||||
"<specVersion>"
|
||||
"<major>1</major>"
|
||||
"<minor>0</minor>"
|
||||
"</specVersion>"
|
||||
"<URLBase>http://"));
|
||||
|
||||
client.print(formatIP(ip));
|
||||
client.print(F(":80/</URLBase>"
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType>"
|
||||
"<friendlyName>"));
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType>"
|
||||
"<friendlyName>"));
|
||||
client.print(Settings.Name);
|
||||
client.print(F("</friendlyName>"
|
||||
"<presentationURL>/</presentationURL>"
|
||||
"<serialNumber>"));
|
||||
"<presentationURL>/</presentationURL>"
|
||||
"<serialNumber>"));
|
||||
client.print(String(ESP.getChipId()));
|
||||
client.print(F("</serialNumber>"
|
||||
"<modelName>ESP Easy</modelName>"
|
||||
"<modelNumber>"));
|
||||
"<modelName>ESP Easy</modelName>"
|
||||
"<modelNumber>"));
|
||||
client.print(getValue(LabelType::GIT_BUILD));
|
||||
client.print(F("</modelNumber>"
|
||||
"<modelURL>http://www.letscontrolit.com</modelURL>"
|
||||
"<manufacturer>http://www.letscontrolit.com</manufacturer>"
|
||||
"<manufacturerURL>http://www.letscontrolit.com</manufacturerURL>"
|
||||
"<UDN>uuid:"));
|
||||
"<modelURL>http://www.letscontrolit.com</modelURL>"
|
||||
"<manufacturer>http://www.letscontrolit.com</manufacturer>"
|
||||
"<manufacturerURL>http://www.letscontrolit.com</manufacturerURL>"
|
||||
"<UDN>uuid:"));
|
||||
client.print(String(uuid));
|
||||
client.print(F("</UDN></device>"
|
||||
"</root>\r\n"
|
||||
"\r\n"));
|
||||
"</root>\r\n"
|
||||
"\r\n"));
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
@@ -644,12 +660,14 @@ bool SSDP_begin() {
|
||||
|
||||
if (_server != nullptr) {
|
||||
_server->unref();
|
||||
|
||||
// FIXME TD-er: Shouldn't this also call delete _server ?
|
||||
|
||||
_server = nullptr;
|
||||
_server = nullptr;
|
||||
}
|
||||
|
||||
_server = new (std::nothrow) UdpContext;
|
||||
|
||||
if (_server == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -1086,6 +1104,7 @@ bool hostReachable(const String& hostname) {
|
||||
if (resolveHostByName(hostname.c_str(), remote_addr)) {
|
||||
return hostReachable(remote_addr);
|
||||
}
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
String log = F("Hostname cannot be resolved: ");
|
||||
|
||||
@@ -1161,9 +1180,26 @@ bool splitHostPortString(const String& hostPortString, String& host, uint16_t& p
|
||||
return true;
|
||||
}
|
||||
|
||||
bool splitUserPass_HostPortString(const String& hostPortString, String& user, String& pass, String& host, uint16_t& port)
|
||||
{
|
||||
const int pos_at = hostPortString.indexOf('@');
|
||||
|
||||
if (pos_at != -1) {
|
||||
user = hostPortString.substring(0, pos_at);
|
||||
const int pos_colon = user.indexOf(':');
|
||||
|
||||
if (pos_colon != -1) {
|
||||
pass = user.substring(pos_colon + 1);
|
||||
user = user.substring(0, pos_colon);
|
||||
}
|
||||
return splitHostPortString(hostPortString.substring(pos_at + 1), host, port);
|
||||
}
|
||||
return splitHostPortString(hostPortString, host, port);
|
||||
}
|
||||
|
||||
// Split a full URL like "http://hostname:port/path/file.htm"
|
||||
// Return value is everything after the hostname:port section (including /)
|
||||
String splitURL(const String& fullURL, String& host, uint16_t& port, String& file) {
|
||||
String splitURL(const String& fullURL, String& user, String& pass, String& host, uint16_t& port, String& file) {
|
||||
int starthost = fullURL.indexOf(F("//"));
|
||||
|
||||
if (starthost == -1) {
|
||||
@@ -1173,7 +1209,7 @@ String splitURL(const String& fullURL, String& host, uint16_t& port, String& fil
|
||||
}
|
||||
int endhost = fullURL.indexOf('/', starthost);
|
||||
|
||||
splitHostPortString(fullURL.substring(starthost, endhost), host, port);
|
||||
splitUserPass_HostPortString(fullURL.substring(starthost, endhost), user, pass, host, port);
|
||||
int startfile = fullURL.lastIndexOf('/');
|
||||
|
||||
if (startfile >= 0) {
|
||||
@@ -1182,8 +1218,315 @@ String splitURL(const String& fullURL, String& host, uint16_t& port, String& fil
|
||||
return fullURL.substring(endhost);
|
||||
}
|
||||
|
||||
String get_user_agent_string() {
|
||||
static unsigned int agent_size = 20;
|
||||
String userAgent;
|
||||
|
||||
userAgent.reserve(agent_size);
|
||||
userAgent += F("ESP Easy/");
|
||||
userAgent += BUILD;
|
||||
userAgent += '/';
|
||||
userAgent += get_build_date();
|
||||
userAgent += ' ';
|
||||
userAgent += get_build_time();
|
||||
agent_size = userAgent.length();
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
bool splitHeaders(int& strpos, const String& multiHeaders, String& name, String& value) {
|
||||
if (strpos < 0) {
|
||||
return false;
|
||||
}
|
||||
int colonPos = multiHeaders.indexOf(':', strpos);
|
||||
|
||||
if (colonPos < 0) {
|
||||
return false;
|
||||
}
|
||||
name = multiHeaders.substring(strpos, colonPos);
|
||||
int valueEndPos = multiHeaders.indexOf('\n', colonPos + 1);
|
||||
|
||||
if (valueEndPos < 0) {
|
||||
value = multiHeaders.substring(colonPos + 1);
|
||||
strpos = -1;
|
||||
} else {
|
||||
value = multiHeaders.substring(colonPos + 1, valueEndPos);
|
||||
strpos = valueEndPos + 1;
|
||||
}
|
||||
value.replace('\r', ' ');
|
||||
value.trim();
|
||||
return true;
|
||||
}
|
||||
|
||||
String extractParam(const String& authReq, const String& param, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
|
||||
if (_begin == -1) { return EMPTY_STRING; }
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
String getCNonce(const int len) {
|
||||
static const char alphanum[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
String s;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
s += alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String getDigestAuth(const String& authReq,
|
||||
const String& username,
|
||||
const String& password,
|
||||
const String& method,
|
||||
const String& uri,
|
||||
unsigned int counter) {
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
const String realm = extractParam(authReq, F("realm=\""), '"');
|
||||
const String nonce = extractParam(authReq, F("nonce=\""), '"');
|
||||
const String cNonce = getCNonce(8);
|
||||
|
||||
char nc[9];
|
||||
|
||||
snprintf(nc, sizeof(nc), "%08x", counter);
|
||||
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
MD5Builder md5;
|
||||
|
||||
md5.begin();
|
||||
md5.add(username + ':' + realm + ':' + password); // md5 of the user:realm:user
|
||||
md5.calculate();
|
||||
const String h1 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(method + ':' + uri);
|
||||
md5.calculate();
|
||||
const String h2 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(h1 + ':' + nonce + ':' + String(nc) + ':' + cNonce + F(":auth:") + h2);
|
||||
md5.calculate();
|
||||
const String response = md5.toString();
|
||||
|
||||
const String authorization =
|
||||
String(F("Digest username=\"")) + username +
|
||||
F("\", realm=\"") + realm +
|
||||
F("\", nonce=\"") + nonce +
|
||||
F("\", uri=\"") + uri +
|
||||
F("\", algorithm=\"MD5\", qop=auth, nc=") + String(nc) +
|
||||
F(", cnonce=\"") + cNonce +
|
||||
F("\", response=\"") + response +
|
||||
'"';
|
||||
|
||||
// Serial.println(authorization);
|
||||
|
||||
return authorization;
|
||||
}
|
||||
|
||||
void log_http_result(const HTTPClient& http,
|
||||
const String & logIdentifier,
|
||||
const String & host,
|
||||
const String & HttpMethod,
|
||||
int httpCode,
|
||||
const String & response)
|
||||
{
|
||||
uint8_t loglevel = LOG_LEVEL_ERROR;
|
||||
bool success = false;
|
||||
|
||||
// HTTP codes:
|
||||
// 1xx Informational response
|
||||
// 2xx Success
|
||||
if ((httpCode >= 100) && (httpCode < 300)) {
|
||||
loglevel = LOG_LEVEL_INFO;
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (loglevelActiveFor(loglevel)) {
|
||||
String log = F("HTTP : ");
|
||||
log += logIdentifier;
|
||||
log += ' ';
|
||||
log += host;
|
||||
log += ' ';
|
||||
log += HttpMethod;
|
||||
log += F("... ");
|
||||
|
||||
if (!success) {
|
||||
log += F("failed ");
|
||||
}
|
||||
log += F("HTTP code: ");
|
||||
log += String(httpCode);
|
||||
|
||||
if (!success) {
|
||||
log += ' ';
|
||||
log += http.errorToString(httpCode);
|
||||
}
|
||||
|
||||
if (response.length() > 0) {
|
||||
log += F(" Received reply: ");
|
||||
log += response.substring(0, 100); // Returned string may be huge, so only log the first part.
|
||||
}
|
||||
addLogMove(loglevel, log);
|
||||
}
|
||||
}
|
||||
|
||||
int http_authenticate(const String& logIdentifier,
|
||||
WiFiClient & client,
|
||||
HTTPClient & http,
|
||||
uint16_t timeout,
|
||||
const String& user,
|
||||
const String& pass,
|
||||
const String& host,
|
||||
uint16_t port,
|
||||
const String& uri,
|
||||
const String& HttpMethod,
|
||||
const String& header,
|
||||
const String& postStr)
|
||||
{
|
||||
int httpCode = 0;
|
||||
|
||||
http.setAuthorization(user.c_str(), pass.c_str());
|
||||
http.setTimeout(timeout);
|
||||
http.setUserAgent(get_user_agent_string());
|
||||
|
||||
if (Settings.SendToHTTP_follow_redirects()) {
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
http.setRedirectLimit(2);
|
||||
}
|
||||
|
||||
#ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
|
||||
// See: https://github.com/espressif/arduino-esp32/pull/6676
|
||||
client.setTimeout((timeout + 500) / 1000); // in seconds!!!!
|
||||
#else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
client.setTimeout(timeout); // in msec as it should be!
|
||||
#endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
|
||||
// Add request header as fall back.
|
||||
// When adding another "accept" header, it may be interpreted as:
|
||||
// "if you have XXX, send it; or failing that, just give me what you've got."
|
||||
http.addHeader(F("Accept"), F("*/*;q=0.1"));
|
||||
|
||||
delay(0);
|
||||
#if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri, false); // HTTP
|
||||
#else // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri);
|
||||
#endif // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
|
||||
const char *keys[] = { "WWW-Authenticate" };
|
||||
http.collectHeaders(keys, 1);
|
||||
|
||||
{
|
||||
int headerpos = 0;
|
||||
String name, value;
|
||||
|
||||
while (splitHeaders(headerpos, header, name, value)) {
|
||||
http.addHeader(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// start connection and send HTTP header (and body)
|
||||
if (HttpMethod.equals(F("HEAD")) || HttpMethod.equals(F("GET"))) {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str());
|
||||
} else {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str(), postStr);
|
||||
}
|
||||
|
||||
// Check to see if we need to try digest auth
|
||||
if (httpCode == 401) {
|
||||
const String authReq = http.header(String(F("WWW-Authenticate")).c_str());
|
||||
|
||||
if (authReq.indexOf(F("Digest")) != -1) {
|
||||
// Use Digest authorization
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLogMove(LOG_LEVEL_INFO, String(F("HTTP : Start Digest Authorization for ")) + host);
|
||||
}
|
||||
|
||||
http.setAuthorization(""); // Clear Basic authorization
|
||||
const String authorization = getDigestAuth(authReq, user, pass, "GET", uri, 1);
|
||||
|
||||
http.end();
|
||||
#if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri, false); // HTTP, not HTTPS
|
||||
#else // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri);
|
||||
#endif // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
|
||||
http.addHeader(F("Authorization"), authorization);
|
||||
|
||||
// start connection and send HTTP header (and body)
|
||||
if (HttpMethod.equals(F("HEAD")) || HttpMethod.equals(F("GET"))) {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str());
|
||||
} else {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str(), postStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.UseRules) {
|
||||
// Generate event with the HTTP return code
|
||||
// e.g. http#hostname=401
|
||||
String event = F("http#");
|
||||
event += host;
|
||||
event += '=';
|
||||
event += httpCode;
|
||||
eventQueue.addMove(std::move(event));
|
||||
}
|
||||
log_http_result(http, logIdentifier, host, HttpMethod, httpCode, EMPTY_STRING);
|
||||
return httpCode;
|
||||
}
|
||||
|
||||
String send_via_http(const String& logIdentifier,
|
||||
WiFiClient & client,
|
||||
uint16_t timeout,
|
||||
const String& user,
|
||||
const String& pass,
|
||||
const String& host,
|
||||
uint16_t port,
|
||||
const String& uri,
|
||||
const String& HttpMethod,
|
||||
const String& header,
|
||||
const String& postStr,
|
||||
int & httpCode,
|
||||
bool must_check_reply) {
|
||||
HTTPClient http;
|
||||
|
||||
httpCode = http_authenticate(
|
||||
logIdentifier,
|
||||
client,
|
||||
http,
|
||||
timeout,
|
||||
user,
|
||||
pass,
|
||||
host,
|
||||
port,
|
||||
uri,
|
||||
HttpMethod,
|
||||
header,
|
||||
postStr);
|
||||
|
||||
String response;
|
||||
|
||||
if ((httpCode > 0) && must_check_reply) {
|
||||
response = http.getString();
|
||||
|
||||
if (!response.isEmpty()) {
|
||||
log_http_result(http, logIdentifier, host, HttpMethod, httpCode, response);
|
||||
}
|
||||
}
|
||||
http.end();
|
||||
return response;
|
||||
}
|
||||
|
||||
#ifdef USE_DOWNLOAD
|
||||
|
||||
// FIXME TD-er: Must set the timeout somewhere
|
||||
# ifndef DOWNLOAD_FILE_TIMEOUT
|
||||
# define DOWNLOAD_FILE_TIMEOUT 2000
|
||||
# endif // ifndef DOWNLOAD_FILE_TIMEOUT
|
||||
|
||||
// Download a file from a given URL and save to a local file named "file_save"
|
||||
// If the URL ends with a /, the file part will be assumed the same as file_save.
|
||||
// If file_save is empty, the file part from the URL will be used as local file name.
|
||||
@@ -1194,10 +1537,18 @@ bool downloadFile(const String& url, String file_save) {
|
||||
return downloadFile(url, file_save, EMPTY_STRING, EMPTY_STRING, error);
|
||||
}
|
||||
|
||||
bool downloadFile(const String& url, String file_save, const String& user, const String& pass, String& error) {
|
||||
// User and Pass may be updated if they occur in the hostname part.
|
||||
// Thus have to be copied instead of const reference.
|
||||
bool start_downloadFile(WiFiClient & client,
|
||||
HTTPClient & http,
|
||||
const String& url,
|
||||
String & file_save,
|
||||
String user,
|
||||
String pass,
|
||||
String & error) {
|
||||
String host, file;
|
||||
uint16_t port;
|
||||
String uri = splitURL(url, host, port, file);
|
||||
String uri = splitURL(url, user, pass, host, port, file);
|
||||
|
||||
if (file_save.isEmpty()) {
|
||||
file_save = file;
|
||||
@@ -1205,8 +1556,9 @@ bool downloadFile(const String& url, String file_save, const String& user, const
|
||||
// file = file_save;
|
||||
uri += file_save;
|
||||
}
|
||||
# ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
String log = F("downloadFile: URL: ");
|
||||
log += url;
|
||||
log += F(" decoded: ");
|
||||
@@ -1214,8 +1566,9 @@ bool downloadFile(const String& url, String file_save, const String& user, const
|
||||
log += ':';
|
||||
log += port;
|
||||
log += uri;
|
||||
addLogMove(LOG_LEVEL_ERROR, log);
|
||||
addLogMove(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
# endif // ifndef BUILD_NO_DEBUG
|
||||
|
||||
if (file_save.isEmpty()) {
|
||||
error = F("Empty filename");
|
||||
@@ -1223,27 +1576,20 @@ bool downloadFile(const String& url, String file_save, const String& user, const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileExists(file_save)) {
|
||||
error = F("File exists: ");
|
||||
error += file_save;
|
||||
addLog(LOG_LEVEL_ERROR, error);
|
||||
return false;
|
||||
}
|
||||
unsigned long timeout = millis() + 2000;
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
|
||||
http.begin(client, host, port, uri);
|
||||
{
|
||||
if ((user.length() > 0) && (pass.length() > 0)) {
|
||||
http.setAuthorization(user.c_str(), pass.c_str());
|
||||
}
|
||||
|
||||
/*
|
||||
http.setAuthorization(user, pass);
|
||||
*/
|
||||
}
|
||||
int httpCode = http.GET();
|
||||
const int httpCode = http_authenticate(
|
||||
F("DownloadFile"),
|
||||
client,
|
||||
http,
|
||||
DOWNLOAD_FILE_TIMEOUT,
|
||||
user,
|
||||
pass,
|
||||
host,
|
||||
port,
|
||||
uri,
|
||||
F("GET"),
|
||||
EMPTY_STRING, // header
|
||||
EMPTY_STRING // postStr
|
||||
);
|
||||
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
error = F("HTTP code: ");
|
||||
@@ -1255,14 +1601,32 @@ bool downloadFile(const String& url, String file_save, const String& user, const
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
long len = http.getSize();
|
||||
fs::File f = tryOpenFile(file_save, "w");
|
||||
bool downloadFile(const String& url, String file_save, const String& user, const String& pass, String& error) {
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
|
||||
if (!start_downloadFile(client, http, url, file_save, user, pass, error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileExists(file_save)) {
|
||||
error = F("File exists: ");
|
||||
error += file_save;
|
||||
addLog(LOG_LEVEL_ERROR, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
long len = http.getSize();
|
||||
fs::File f = tryOpenFile(file_save, "w");
|
||||
|
||||
if (f) {
|
||||
const size_t downloadBuffSize = 256;
|
||||
uint8_t buff[downloadBuffSize];
|
||||
size_t bytesWritten = 0;
|
||||
size_t bytesWritten = 0;
|
||||
unsigned long timeout = millis() + DOWNLOAD_FILE_TIMEOUT;
|
||||
|
||||
// get tcp stream
|
||||
WiFiClient *stream = &client;
|
||||
@@ -1270,10 +1634,15 @@ bool downloadFile(const String& url, String file_save, const String& user, const
|
||||
// read all data from server
|
||||
while (http.connected() && (len > 0 || len == -1)) {
|
||||
// read up to downloadBuffSize at a time.
|
||||
const size_t c = stream->readBytes(buff, std::min(static_cast<size_t>(len), downloadBuffSize));
|
||||
size_t bytes_to_read = downloadBuffSize;
|
||||
|
||||
if ((len > 0) && (len < static_cast<int>(bytes_to_read))) {
|
||||
bytes_to_read = len;
|
||||
}
|
||||
const size_t c = stream->readBytes(buff, bytes_to_read);
|
||||
|
||||
if (c > 0) {
|
||||
timeout = millis() + 2000;
|
||||
timeout = millis() + DOWNLOAD_FILE_TIMEOUT;
|
||||
|
||||
if (f.write(buff, c) != c) {
|
||||
error = F("Error saving file: ");
|
||||
@@ -1291,7 +1660,7 @@ bool downloadFile(const String& url, String file_save, const String& user, const
|
||||
}
|
||||
|
||||
if (timeOutReached(timeout)) {
|
||||
error = F("Timeout: ");
|
||||
error = F("Timeout: ");
|
||||
error += file_save;
|
||||
addLog(LOG_LEVEL_ERROR, error);
|
||||
delay(0);
|
||||
@@ -1302,18 +1671,119 @@ bool downloadFile(const String& url, String file_save, const String& user, const
|
||||
}
|
||||
f.close();
|
||||
http.end();
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("downloadFile: ");
|
||||
log += file_save;
|
||||
log += F(" Success");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
addLogMove(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
error = F("Failed to open file for writing: ");
|
||||
http.end();
|
||||
error = F("Failed to open file for writing: ");
|
||||
error += file_save;
|
||||
addLog(LOG_LEVEL_ERROR, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
bool downloadFirmware(const String& url, String& error)
|
||||
{
|
||||
String file_save;
|
||||
String user;
|
||||
String pass;
|
||||
WiFiClient client;
|
||||
HTTPClient http;
|
||||
|
||||
if (!start_downloadFile(client, http, url, file_save, user, pass, error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int len = http.getSize();
|
||||
|
||||
if (Update.begin(len, U_FLASH, Settings.Pin_status_led, Settings.Pin_status_led_Inversed ? LOW : HIGH)) {
|
||||
const size_t downloadBuffSize = 256;
|
||||
uint8_t buff[downloadBuffSize];
|
||||
size_t bytesWritten = 0;
|
||||
unsigned long timeout = millis() + DOWNLOAD_FILE_TIMEOUT;
|
||||
|
||||
// get tcp stream
|
||||
WiFiClient *stream = &client;
|
||||
|
||||
while (http.connected() && (len > 0 || len == -1)) {
|
||||
// read up to downloadBuffSize at a time.
|
||||
size_t bytes_to_read = downloadBuffSize;
|
||||
|
||||
if ((len > 0) && (len < static_cast<int>(bytes_to_read))) {
|
||||
bytes_to_read = len;
|
||||
}
|
||||
const size_t c = stream->readBytes(buff, bytes_to_read);
|
||||
|
||||
if (c > 0) {
|
||||
timeout = millis() + DOWNLOAD_FILE_TIMEOUT;
|
||||
|
||||
if (Update.write(buff, c) != c) {
|
||||
error = F("Error saving firmware update: ");
|
||||
error += file_save;
|
||||
error += ' ';
|
||||
error += bytesWritten;
|
||||
error += F(" Bytes written");
|
||||
addLog(LOG_LEVEL_ERROR, error);
|
||||
Update.end();
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
bytesWritten += c;
|
||||
|
||||
if (len > 0) { len -= c; }
|
||||
}
|
||||
|
||||
if (timeOutReached(timeout)) {
|
||||
error = F("Timeout: ");
|
||||
error += file_save;
|
||||
addLog(LOG_LEVEL_ERROR, error);
|
||||
delay(0);
|
||||
Update.end();
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UseRTOSMultitasking) {
|
||||
// On ESP32 the schedule is executed on the 2nd core.
|
||||
Scheduler.handle_schedule();
|
||||
}
|
||||
backgroundtasks();
|
||||
}
|
||||
http.end();
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("downloadFile: ");
|
||||
log += file_save;
|
||||
log += F(" Success");
|
||||
addLogMove(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
|
||||
if (Update.end()) {
|
||||
if (Settings.UseRules) {
|
||||
String event = F("ProvisionFirmware#success=");
|
||||
event += file_save;
|
||||
eventQueue.addMove(std::move(event));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
http.end();
|
||||
Update.end();
|
||||
error = F("Failed update firmware: ");
|
||||
error += file_save;
|
||||
addLog(LOG_LEVEL_ERROR, error);
|
||||
|
||||
if (Settings.UseRules) {
|
||||
String event = F("ProvisionFirmware#failed=");
|
||||
event += file_save;
|
||||
eventQueue.addMove(std::move(event));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // ifdef USE_DOWNLOAD
|
||||
|
||||
@@ -3,10 +3,18 @@
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
# include <ESP8266HTTPClient.h>
|
||||
#endif // ifdef ESP8266
|
||||
#ifdef ESP32
|
||||
# include <HTTPClient.h>
|
||||
#endif // ifdef ESP32
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
Syslog client
|
||||
\*********************************************************************************************/
|
||||
@@ -150,11 +158,54 @@ bool beginWiFiUDP_randomPort(WiFiUDP& udp);
|
||||
|
||||
void sendGratuitousARP();
|
||||
|
||||
|
||||
bool splitHostPortString(const String& hostPortString, String& host, uint16_t& port);
|
||||
|
||||
// Split the username and password from a string like this:
|
||||
// username:password@hostname:portnr
|
||||
// @param hostPortString The string to parse
|
||||
// @param user The found username (if any)
|
||||
// @param pass The found password (if any)
|
||||
// @param hostname The hostname stripped from any of the other possible parameters
|
||||
// @param port The found portname (defaults to 80 when not specified)
|
||||
// @retval Whether supplied hostPortString was valid.
|
||||
bool splitUserPass_HostPortString(const String& hostPortString, String& user, String& pass, String& host, uint16_t& port);
|
||||
|
||||
// Split a full URL like "http://hostname:port/path/file.htm"
|
||||
// Return value is everything after the hostname:port section (including /)
|
||||
String splitURL(const String& fullURL, String& host, uint16_t& port, String& file);
|
||||
String splitURL(const String& fullURL, String& user, String& pass, String& host, uint16_t& port, String& file);
|
||||
|
||||
|
||||
// Initiate the HTTP connection.
|
||||
// Also try to authenticate using either Basic auth or Digest.
|
||||
// @retval HTTP return code.
|
||||
int http_authenticate(const String& logIdentifier,
|
||||
WiFiClient & client,
|
||||
HTTPClient & http,
|
||||
uint16_t timeout,
|
||||
const String& user,
|
||||
const String& pass,
|
||||
const String& host,
|
||||
uint16_t port,
|
||||
const String& uri,
|
||||
const String& HttpMethod,
|
||||
const String& header,
|
||||
const String& postStr);
|
||||
|
||||
|
||||
String send_via_http(const String& logIdentifier,
|
||||
WiFiClient & client,
|
||||
uint16_t timeout,
|
||||
const String& user,
|
||||
const String& pass,
|
||||
const String& host,
|
||||
uint16_t port,
|
||||
const String& uri,
|
||||
const String& HttpMethod,
|
||||
const String& header,
|
||||
const String& postStr,
|
||||
int & httpCode,
|
||||
bool must_check_reply);
|
||||
|
||||
#ifdef USE_DOWNLOAD
|
||||
|
||||
@@ -166,6 +217,8 @@ bool downloadFile(const String& url, String file_save);
|
||||
|
||||
bool downloadFile(const String& url, String file_save, const String& user, const String& pass, String& error);
|
||||
|
||||
bool downloadFirmware(const String& url, String& error);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ const __FlashStringHelper * getResetReasonString_f(uint8_t icore, bool& isDEEPSL
|
||||
case TG1WDT_CPU_RESET: return F("Time Group1 reset CPU"); // 17
|
||||
case SUPER_WDT_RESET: return F("Super watchdog reset digital core and rtc module"); // 18
|
||||
case GLITCH_RTC_RESET: return F("Glitch reset digital core and rtc module"); // 19
|
||||
case EFUSE_RESET: return F("EFUSE_RESET"); // FIXME TD-er: No idea what may cause this
|
||||
case NO_MEAN: break; // Undefined, "No Meaning"
|
||||
}
|
||||
|
||||
|
||||
@@ -29,17 +29,8 @@
|
||||
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <base64.h>
|
||||
#include <MD5Builder.h>
|
||||
|
||||
|
||||
#ifdef ESP8266
|
||||
# include <ESP8266HTTPClient.h>
|
||||
#endif // ifdef ESP8266
|
||||
#ifdef ESP32
|
||||
# include <HTTPClient.h>
|
||||
#endif // ifdef ESP32
|
||||
|
||||
bool safeReadStringUntil(Stream & input,
|
||||
String & str,
|
||||
char terminator,
|
||||
@@ -90,22 +81,6 @@ bool safeReadStringUntil(Stream & input,
|
||||
return false;
|
||||
}
|
||||
|
||||
String get_user_agent_string() {
|
||||
static unsigned int agent_size = 20;
|
||||
String userAgent;
|
||||
|
||||
userAgent.reserve(agent_size);
|
||||
userAgent += F("ESP Easy/");
|
||||
userAgent += BUILD;
|
||||
userAgent += '/';
|
||||
userAgent += get_build_date();
|
||||
userAgent += ' ';
|
||||
userAgent += get_build_time();
|
||||
agent_size = userAgent.length();
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
void log_connecting_to(const __FlashStringHelper *prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) {
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
||||
@@ -152,11 +127,12 @@ bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettings
|
||||
|
||||
if (!NetworkConnected()) { return false; }
|
||||
#ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
|
||||
// See: https://github.com/espressif/arduino-esp32/pull/6676
|
||||
client.setTimeout((ControllerSettings.ClientTimeout + 500) / 1000); // in seconds!!!!
|
||||
#else
|
||||
client.setTimeout(ControllerSettings.ClientTimeout); // in msec as it should be!
|
||||
#endif
|
||||
#else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
client.setTimeout(ControllerSettings.ClientTimeout); // in msec as it should be!
|
||||
#endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
delay(0);
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
log_connecting_to(F("UDP : "), controller_number, ControllerSettings);
|
||||
@@ -184,11 +160,12 @@ bool try_connect_host(int controller_number,
|
||||
// Use WiFiClient class to create TCP connections
|
||||
delay(0);
|
||||
#ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
|
||||
// See: https://github.com/espressif/arduino-esp32/pull/6676
|
||||
client.setTimeout((ControllerSettings.ClientTimeout + 500) / 1000); // in seconds!!!!
|
||||
#else
|
||||
client.setTimeout(ControllerSettings.ClientTimeout); // in msec as it should be!
|
||||
#endif
|
||||
#else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
client.setTimeout(ControllerSettings.ClientTimeout); // in msec as it should be!
|
||||
#endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
log_connecting_to(loglabel, controller_number, ControllerSettings);
|
||||
@@ -209,7 +186,6 @@ bool client_available(WiFiClient& client) {
|
||||
return (client.available() != 0) || (client.connected() != 0);
|
||||
}
|
||||
|
||||
|
||||
String send_via_http(int controller_number,
|
||||
const ControllerSettingsStruct& ControllerSettings,
|
||||
controllerIndex_t controller_idx,
|
||||
@@ -246,246 +222,9 @@ String send_via_http(int controller_number,
|
||||
return result;
|
||||
}
|
||||
|
||||
bool splitHeaders(int& strpos, const String& multiHeaders, String& name, String& value) {
|
||||
if (strpos < 0) {
|
||||
return false;
|
||||
}
|
||||
int colonPos = multiHeaders.indexOf(':', strpos);
|
||||
|
||||
if (colonPos < 0) {
|
||||
return false;
|
||||
}
|
||||
name = multiHeaders.substring(strpos, colonPos);
|
||||
int valueEndPos = multiHeaders.indexOf('\n', colonPos + 1);
|
||||
|
||||
if (valueEndPos < 0) {
|
||||
value = multiHeaders.substring(colonPos + 1);
|
||||
strpos = -1;
|
||||
} else {
|
||||
value = multiHeaders.substring(colonPos + 1, valueEndPos);
|
||||
strpos = valueEndPos + 1;
|
||||
}
|
||||
value.replace('\r', ' ');
|
||||
value.trim();
|
||||
return true;
|
||||
}
|
||||
|
||||
String extractParam(const String& authReq, const String& param, const char delimit) {
|
||||
int _begin = authReq.indexOf(param);
|
||||
|
||||
if (_begin == -1) { return EMPTY_STRING; }
|
||||
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length()));
|
||||
}
|
||||
|
||||
String getCNonce(const int len) {
|
||||
static const char alphanum[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
String s;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
s += alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String getDigestAuth(const String& authReq,
|
||||
const String& username,
|
||||
const String& password,
|
||||
const String& method,
|
||||
const String& uri,
|
||||
unsigned int counter) {
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
const String realm = extractParam(authReq, F("realm=\""), '"');
|
||||
const String nonce = extractParam(authReq, F("nonce=\""), '"');
|
||||
const String cNonce = getCNonce(8);
|
||||
|
||||
char nc[9];
|
||||
|
||||
snprintf(nc, sizeof(nc), "%08x", counter);
|
||||
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
MD5Builder md5;
|
||||
|
||||
md5.begin();
|
||||
md5.add(username + ':' + realm + ':' + password); // md5 of the user:realm:user
|
||||
md5.calculate();
|
||||
const String h1 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(method + ':' + uri);
|
||||
md5.calculate();
|
||||
const String h2 = md5.toString();
|
||||
|
||||
md5.begin();
|
||||
md5.add(h1 + ':' + nonce + ':' + String(nc) + ':' + cNonce + F(":auth:") + h2);
|
||||
md5.calculate();
|
||||
const String response = md5.toString();
|
||||
|
||||
const String authorization =
|
||||
String(F("Digest username=\"")) + username +
|
||||
F("\", realm=\"") + realm +
|
||||
F("\", nonce=\"") + nonce +
|
||||
F("\", uri=\"") + uri +
|
||||
F("\", algorithm=\"MD5\", qop=auth, nc=") + String(nc) +
|
||||
F(", cnonce=\"") + cNonce +
|
||||
F("\", response=\"") + response +
|
||||
'"';
|
||||
|
||||
// Serial.println(authorization);
|
||||
|
||||
return authorization;
|
||||
}
|
||||
|
||||
void log_http_result(const HTTPClient& http,
|
||||
const String & logIdentifier,
|
||||
const String & HttpMethod,
|
||||
int httpCode,
|
||||
const String & response)
|
||||
{
|
||||
uint8_t loglevel = LOG_LEVEL_ERROR;
|
||||
bool success = false;
|
||||
|
||||
// HTTP codes:
|
||||
// 1xx Informational response
|
||||
// 2xx Success
|
||||
if ((httpCode >= 100) && (httpCode < 300)) {
|
||||
loglevel = LOG_LEVEL_INFO;
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (loglevelActiveFor(loglevel)) {
|
||||
String log = F("HTTP : ");
|
||||
log += logIdentifier;
|
||||
log += ' ';
|
||||
log += HttpMethod;
|
||||
log += F("... ");
|
||||
|
||||
if (!success) {
|
||||
log += F("failed ");
|
||||
}
|
||||
log += F("HTTP code: ");
|
||||
log += String(httpCode);
|
||||
|
||||
if (!success) {
|
||||
log += ' ';
|
||||
log += http.errorToString(httpCode);
|
||||
}
|
||||
|
||||
if (response.length() > 0) {
|
||||
log += ' ';
|
||||
log += response.substring(0, 100); // Returned string may be huge, so only log the first part.
|
||||
}
|
||||
addLogMove(loglevel, log);
|
||||
}
|
||||
}
|
||||
|
||||
String send_via_http(const String& logIdentifier,
|
||||
WiFiClient & client,
|
||||
uint16_t timeout,
|
||||
const String& user,
|
||||
const String& pass,
|
||||
const String& host,
|
||||
uint16_t port,
|
||||
const String& uri,
|
||||
const String& HttpMethod,
|
||||
const String& header,
|
||||
const String& postStr,
|
||||
int & httpCode,
|
||||
bool must_check_reply) {
|
||||
HTTPClient http;
|
||||
http.setAuthorization(user.c_str(), pass.c_str());
|
||||
http.setTimeout(timeout);
|
||||
http.setUserAgent(get_user_agent_string());
|
||||
|
||||
#ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
// See: https://github.com/espressif/arduino-esp32/pull/6676
|
||||
client.setTimeout((timeout + 500) / 1000); // in seconds!!!!
|
||||
#else
|
||||
client.setTimeout(timeout); // in msec as it should be!
|
||||
#endif
|
||||
|
||||
// Add request header as fall back.
|
||||
// When adding another "accept" header, it may be interpreted as:
|
||||
// "if you have XXX, send it; or failing that, just give me what you've got."
|
||||
http.addHeader(F("Accept"), F("*/*;q=0.1"));
|
||||
|
||||
delay(0);
|
||||
#if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri, false); // HTTP
|
||||
#else // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri);
|
||||
#endif // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
|
||||
const char *keys[] = { "WWW-Authenticate" };
|
||||
http.collectHeaders(keys, 1);
|
||||
|
||||
{
|
||||
int headerpos = 0;
|
||||
String name, value;
|
||||
|
||||
while (splitHeaders(headerpos, header, name, value)) {
|
||||
http.addHeader(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// start connection and send HTTP header (and body)
|
||||
if (HttpMethod.equals(F("HEAD")) || HttpMethod.equals(F("GET"))) {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str());
|
||||
} else {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str(), postStr);
|
||||
}
|
||||
|
||||
String response;
|
||||
|
||||
// httpCode will be negative on error
|
||||
if (httpCode > 0) {
|
||||
const String authReq = http.header(String(F("WWW-Authenticate")).c_str());
|
||||
|
||||
if ((httpCode == 401) && (authReq.indexOf(F("Digest")) != -1)) {
|
||||
// Use Digest authorization
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLogMove(LOG_LEVEL_INFO, String(F("HTTP : Start Digest Authorization for ")) + host);
|
||||
}
|
||||
|
||||
http.setAuthorization(""); // Clear Basic authorization
|
||||
const String authorization = getDigestAuth(authReq, user, pass, "GET", uri, 1);
|
||||
|
||||
http.end();
|
||||
#if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri, false); // HTTP
|
||||
#else // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
http.begin(client, host, port, uri);
|
||||
#endif // if defined(CORE_POST_2_6_0) || defined(ESP32)
|
||||
|
||||
http.addHeader(F("Authorization"), authorization);
|
||||
|
||||
// start connection and send HTTP header (and body)
|
||||
if (HttpMethod.equals(F("HEAD")) || HttpMethod.equals(F("GET"))) {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str());
|
||||
} else {
|
||||
httpCode = http.sendRequest(HttpMethod.c_str(), postStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (httpCode > 0 && must_check_reply) {
|
||||
response = http.getString();
|
||||
}
|
||||
}
|
||||
log_http_result(http, logIdentifier, HttpMethod, httpCode, response);
|
||||
http.end();
|
||||
if (Settings.UseRules) {
|
||||
// Generate event with the HTTP return code
|
||||
// e.g. http#hostname=401
|
||||
String event = F("http#");
|
||||
event += host;
|
||||
event += '=';
|
||||
event += httpCode;
|
||||
eventQueue.addMove(std::move(event));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
String getControllerUser(controllerIndex_t controller_idx, const ControllerSettingsStruct& ControllerSettings)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#ifndef CPLUGIN_HELPER_H
|
||||
#define CPLUGIN_HELPER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
#include "../../_Plugin_Helper.h"
|
||||
|
||||
@@ -19,6 +15,7 @@
|
||||
#include "../Helpers/_CPlugin_init.h"
|
||||
#include "../Helpers/Misc.h"
|
||||
#include "../Helpers/Network.h"
|
||||
#include "../Helpers/Networking.h"
|
||||
#include "../Helpers/Numerical.h"
|
||||
#include "../Helpers/StringConverter.h"
|
||||
#include "../Helpers/_CPlugin_Helper_webform.h"
|
||||
@@ -55,19 +52,6 @@ bool try_connect_host(int controller_number, WiFiClient& client, ControllerSetti
|
||||
bool client_available(WiFiClient& client);
|
||||
|
||||
|
||||
String send_via_http(const String& logIdentifier,
|
||||
WiFiClient & client,
|
||||
uint16_t timeout,
|
||||
const String& user,
|
||||
const String& pass,
|
||||
const String& host,
|
||||
uint16_t port,
|
||||
const String& uri,
|
||||
const String& HttpMethod,
|
||||
const String& header,
|
||||
const String& postStr,
|
||||
int & httpCode,
|
||||
bool must_check_reply);
|
||||
|
||||
String send_via_http(int controller_number,
|
||||
const ControllerSettingsStruct& ControllerSettings,
|
||||
|
||||
@@ -88,6 +88,7 @@ void handle_advanced() {
|
||||
#endif // WEBSERVER_NEW_RULES
|
||||
Settings.TolerantLastArgParse(isFormItemChecked(F("tolerantargparse")));
|
||||
Settings.SendToHttp_ack(isFormItemChecked(F("sendtohttp_ack")));
|
||||
Settings.SendToHTTP_follow_redirects(isFormItemChecked(F("sendtohttp_redir")));
|
||||
Settings.ForceWiFi_bg_mode(isFormItemChecked(LabelType::FORCE_WIFI_BG));
|
||||
Settings.WiFiRestart_connection_lost(isFormItemChecked(LabelType::RESTART_WIFI_LOST_CONN));
|
||||
Settings.EcoPowerMode(isFormItemChecked(LabelType::CPU_ECO_MODE));
|
||||
@@ -144,6 +145,7 @@ void handle_advanced() {
|
||||
addFormCheckBox(F("Tolerant last parameter"), F("tolerantargparse"), Settings.TolerantLastArgParse());
|
||||
addFormNote(F("Perform less strict parsing on last argument of some commands (e.g. publish and sendToHttp)"));
|
||||
addFormCheckBox(F("SendToHTTP wait for ack"), F("sendtohttp_ack"), Settings.SendToHttp_ack());
|
||||
addFormCheckBox(F("SendToHTTP Follow Redirects"), F("sendtohttp_redir"), Settings.SendToHTTP_follow_redirects());
|
||||
|
||||
/*
|
||||
// MQTT settings now moved to the controller settings.
|
||||
|
||||
@@ -65,8 +65,8 @@ void handle_notifications() {
|
||||
NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_SAVE, 0, dummyString);
|
||||
}
|
||||
NotificationSettings.Port = getFormItemInt(F("port"), 0);
|
||||
NotificationSettings.Pin1 = getFormItemInt(F("pin1"), 0);
|
||||
NotificationSettings.Pin2 = getFormItemInt(F("pin2"), 0);
|
||||
NotificationSettings.Pin1 = getFormItemInt(F("pin1"), -1);
|
||||
NotificationSettings.Pin2 = getFormItemInt(F("pin2"), -1);
|
||||
Settings.NotificationEnabled[notificationindex] = isFormItemChecked(F("notificationenabled"));
|
||||
strncpy_webserver_arg(NotificationSettings.Domain, F("domain"));
|
||||
strncpy_webserver_arg(NotificationSettings.Server, F("server"));
|
||||
@@ -140,10 +140,9 @@ void handle_notifications() {
|
||||
html_TD();
|
||||
addHtml(NotificationSettings.Server);
|
||||
html_TD();
|
||||
addHtmlInt(NotificationSettings.Port);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NotificationSettings.Port){
|
||||
addHtmlInt(NotificationSettings.Port);
|
||||
} else {
|
||||
//MFD: we display the GPIO
|
||||
addGpioHtml(NotificationSettings.Pin1);
|
||||
|
||||
@@ -152,6 +151,9 @@ void handle_notifications() {
|
||||
html_BR();
|
||||
addGpioHtml(NotificationSettings.Pin2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
html_TD(3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,6 +222,7 @@ void storeDownloadFiletypeCheckbox(FileType::Enum filetype, unsigned int filenr)
|
||||
case FileType::NOTIFICATION_DAT: ResetFactoryDefaultPreference.fetchNotificationDat(isChecked); break;
|
||||
case FileType::RULES_TXT: { ResetFactoryDefaultPreference.fetchRulesTXT(filenr, isChecked); break; }
|
||||
case FileType::PROVISIONING_DAT: { ResetFactoryDefaultPreference.fetchProvisioningDat(isChecked); break; }
|
||||
case FileType::FIRMWARE: // FIXME TD-er: Still have to decide what to do with protecting firmware downloads
|
||||
case FileType::MAX_FILETYPE:
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user