|
|
@ -6,17 +6,17 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com> |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
#if WEB_SUPPORT
|
|
|
|
|
|
|
|
#include <ESPAsyncTCP.h>
|
|
|
|
#include <ESPAsyncWebServer.h>
|
|
|
|
#include <ESP8266mDNS.h>
|
|
|
|
#include <FS.h>
|
|
|
|
#include <Hash.h>
|
|
|
|
#include <AsyncJson.h>
|
|
|
|
#include <ArduinoJson.h>
|
|
|
|
#include <Ticker.h>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#if EMBEDDED_WEB
|
|
|
|
#if WEB_EMBEDDED
|
|
|
|
#include "static/index.html.gz.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
@ -25,16 +25,23 @@ Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com> |
|
|
|
#include "static/server.key.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
AsyncWebServer * _server; |
|
|
|
AsyncWebSocket ws("/ws"); |
|
|
|
Ticker deferred; |
|
|
|
char _last_modified[50]; |
|
|
|
Ticker _web_defer; |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
AsyncWebSocket _ws("/ws"); |
|
|
|
typedef struct { |
|
|
|
IPAddress ip; |
|
|
|
unsigned long timestamp = 0; |
|
|
|
} ws_ticket_t; |
|
|
|
ws_ticket_t _ticket[WS_BUFFER_SIZE]; |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
char * url; |
|
|
|
char * key; |
|
|
@ -42,34 +49,19 @@ typedef struct { |
|
|
|
apiPutCallbackFunction putFn = NULL; |
|
|
|
} web_api_t; |
|
|
|
std::vector<web_api_t> _apis; |
|
|
|
char _last_modified[50]; |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// WEBSOCKETS
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool wsConnected() { |
|
|
|
return (ws.count() > 0); |
|
|
|
} |
|
|
|
|
|
|
|
bool wsSend(const char * payload) { |
|
|
|
if (ws.count() > 0) { |
|
|
|
ws.textAll(payload); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool wsSend(uint32_t client_id, const char * payload) { |
|
|
|
ws.text(client_id, payload); |
|
|
|
} |
|
|
|
|
|
|
|
void wsMQTTCallback(unsigned int type, const char * topic, const char * payload) { |
|
|
|
void _wsMQTTCallback(unsigned int type, const char * topic, const char * payload) { |
|
|
|
|
|
|
|
if (type == MQTT_CONNECT_EVENT) { |
|
|
|
wsSend("{\"mqttStatus\": true}"); |
|
|
|
wsSend_P(PSTR("{\"mqttStatus\": true}")); |
|
|
|
} |
|
|
|
|
|
|
|
if (type == MQTT_DISCONNECT_EVENT) { |
|
|
|
wsSend("{\"mqttStatus\": false}"); |
|
|
|
wsSend_P(PSTR("{\"mqttStatus\": false}")); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
@ -81,7 +73,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
JsonObject& root = jsonBuffer.parseObject((char *) payload); |
|
|
|
if (!root.success()) { |
|
|
|
DEBUG_MSG_P(PSTR("[WEBSOCKET] Error parsing data\n")); |
|
|
|
ws.text(client_id, "{\"message\": \"Error parsing data!\"}"); |
|
|
|
wsSend_P(client_id, PSTR("{\"message\": \"Error parsing data!\"}")); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -116,7 +108,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
|
|
|
|
JsonObject& data = root["data"]; |
|
|
|
if (!data.containsKey("app") || (data["app"] != APP_NAME)) { |
|
|
|
ws.text(client_id, "{\"message\": \"The file does not look like a valid configuration backup.\"}"); |
|
|
|
wsSend_P(client_id, PSTR("{\"message\": \"The file does not look like a valid configuration backup.\"}")); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -132,14 +124,14 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
|
|
|
|
saveSettings(); |
|
|
|
|
|
|
|
ws.text(client_id, "{\"message\": \"Changes saved. You should reboot your board now.\"}"); |
|
|
|
wsSend_P(client_id, PSTR("{\"message\": \"Changes saved. You should reboot your board now.\"}")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (action.equals("reconnect")) { |
|
|
|
|
|
|
|
// Let the HTTP request return and disconnect after 100ms
|
|
|
|
deferred.once_ms(100, wifiDisconnect); |
|
|
|
_web_defer.once_ms(100, wifiDisconnect); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
@ -203,21 +195,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
bool changed = false; |
|
|
|
bool changedMQTT = false; |
|
|
|
bool changedNTP = false; |
|
|
|
bool apiEnabled = false; |
|
|
|
bool dstEnabled = false; |
|
|
|
bool mqttUseJson = false; |
|
|
|
bool useColor = false; |
|
|
|
bool useWhite = false; |
|
|
|
bool useGamma = false; |
|
|
|
#if ASYNC_TCP_SSL_ENABLED
|
|
|
|
bool mqttUseSSL = false; |
|
|
|
#endif
|
|
|
|
#if ENABLE_FAUXMO
|
|
|
|
bool fauxmoEnabled = false; |
|
|
|
#endif
|
|
|
|
#if ENABLE_DOMOTICZ
|
|
|
|
bool dczEnabled = false; |
|
|
|
#endif
|
|
|
|
|
|
|
|
unsigned int network = 0; |
|
|
|
unsigned int dczRelayIdx = 0; |
|
|
|
String adminPass; |
|
|
@ -230,7 +208,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
// Skip firmware filename
|
|
|
|
if (key.equals("filename")) continue; |
|
|
|
|
|
|
|
#if ENABLE_HLW8012
|
|
|
|
#if HLW8012_SUPPORT
|
|
|
|
|
|
|
|
if (key == "powExpectedPower") { |
|
|
|
hlw8012SetExpectedActivePower(value.toInt()); |
|
|
@ -256,7 +234,7 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
|
|
|
|
if (key.startsWith("pow")) continue; |
|
|
|
|
|
|
|
#if ENABLE_DOMOTICZ
|
|
|
|
#if DOMOTICZ_SUPPORT
|
|
|
|
|
|
|
|
if (key == "dczRelayIdx") { |
|
|
|
if (dczRelayIdx >= relayCount()) continue; |
|
|
@ -291,31 +269,14 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
} |
|
|
|
if (key == "adminPass2") { |
|
|
|
if (!value.equals(adminPass)) { |
|
|
|
ws.text(client_id, "{\"message\": \"Passwords do not match!\"}"); |
|
|
|
wsSend_P(client_id, PSTR("{\"message\": \"Passwords do not match!\"}")); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (value.length() == 0) continue; |
|
|
|
ws.text(client_id, "{\"action\": \"reload\"}"); |
|
|
|
wsSend_P(client_id, PSTR("{\"action\": \"reload\"}")); |
|
|
|
key = String("adminPass"); |
|
|
|
} |
|
|
|
|
|
|
|
// Checkboxes
|
|
|
|
if (key == "apiEnabled") { apiEnabled = true; continue; } |
|
|
|
if (key == "ntpDST") { dstEnabled = true; continue; } |
|
|
|
if (key == "mqttUseJson") { mqttUseJson = true; continue; } |
|
|
|
if (key == "useColor") { useColor = true; continue; } |
|
|
|
if (key == "useWhite") { useWhite = true; continue; } |
|
|
|
if (key == "useGamma") { useGamma = true; continue; } |
|
|
|
#if ASYNC_TCP_SSL_ENABLED
|
|
|
|
if (key == "mqttUseSSL") { mqttUseSSL = true; continue; } |
|
|
|
#endif
|
|
|
|
#if ENABLE_FAUXMO
|
|
|
|
if (key == "fauxmoEnabled") { fauxmoEnabled = true; continue; } |
|
|
|
#endif
|
|
|
|
#if ENABLE_DOMOTICZ
|
|
|
|
if (key == "dczEnabled") { dczEnabled = true; continue; } |
|
|
|
#endif
|
|
|
|
|
|
|
|
if (key == "ssid") { |
|
|
|
key = key + String(network); |
|
|
|
} |
|
|
@ -348,23 +309,6 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
|
|
|
|
if (webMode == WEB_MODE_NORMAL) { |
|
|
|
|
|
|
|
// Checkboxes
|
|
|
|
setBoolSetting("apiEnabled", apiEnabled, ENABLE_API); |
|
|
|
setBoolSetting("ntpDST", dstEnabled, NTP_DAY_LIGHT); |
|
|
|
setBoolSetting("mqttUseJson", mqttUseJson, MQTT_USE_JSON); |
|
|
|
setBoolSetting("useColor", useColor, LIGHT_USE_COLOR); |
|
|
|
setBoolSetting("useWhite", useWhite, LIGHT_USE_WHITE); |
|
|
|
setBoolSetting("useGamma", useGamma, LIGHT_USE_GAMMA); |
|
|
|
#if ASYNC_TCP_SSL_ENABLED
|
|
|
|
setBoolSetting("mqttUseSSL", mqttUseSSL, MQTT_USE_SSL); |
|
|
|
#endif
|
|
|
|
#if ENABLE_FAUXMO
|
|
|
|
setBoolSetting("fauxmoEnabled", fauxmoEnabled, FAUXMO_ENABLED); |
|
|
|
#endif
|
|
|
|
#if ENABLE_DOMOTICZ
|
|
|
|
setBoolSetting("dczEnabled", dczEnabled, DOMOTICZ_ENABLED); |
|
|
|
#endif
|
|
|
|
|
|
|
|
// Clean wifi networks
|
|
|
|
int i = 0; |
|
|
|
while (i < network) { |
|
|
@ -400,22 +344,22 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
saveSettings(); |
|
|
|
wifiConfigure(); |
|
|
|
otaConfigure(); |
|
|
|
#if ENABLE_FAUXMO
|
|
|
|
fauxmoConfigure(); |
|
|
|
#if ALEXA_SUPPORT
|
|
|
|
alexaConfigure(); |
|
|
|
#endif
|
|
|
|
#if ENABLE_INFLUXDB
|
|
|
|
#if INFLUXDB_SUPPORT
|
|
|
|
influxDBConfigure(); |
|
|
|
#endif
|
|
|
|
#if ENABLE_DOMOTICZ
|
|
|
|
#if DOMOTICZ_SUPPORT
|
|
|
|
domoticzConfigure(); |
|
|
|
#endif
|
|
|
|
mqttConfigure(); |
|
|
|
|
|
|
|
#if ENABLE_RF
|
|
|
|
#if RF_SUPPORT
|
|
|
|
rfBuildCodes(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_EMON
|
|
|
|
#if EMON_SUPPORT
|
|
|
|
setCurrentRatio(getSetting("emonRatio").toFloat()); |
|
|
|
#endif
|
|
|
|
|
|
|
@ -432,9 +376,9 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
} |
|
|
|
|
|
|
|
if (changed) { |
|
|
|
ws.text(client_id, "{\"message\": \"Changes saved\"}"); |
|
|
|
wsSend_P(client_id, PSTR("{\"message\": \"Changes saved\"}")); |
|
|
|
} else { |
|
|
|
ws.text(client_id, "{\"message\": \"No changes detected\"}"); |
|
|
|
wsSend_P(client_id, PSTR("{\"message\": \"No changes detected\"}")); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
@ -444,13 +388,13 @@ void _wsParse(uint32_t client_id, uint8_t * payload, size_t length) { |
|
|
|
void _wsStart(uint32_t client_id) { |
|
|
|
|
|
|
|
char chipid[6]; |
|
|
|
sprintf_P(chipid, PSTR("%06X"), ESP.getChipId()); |
|
|
|
snprintf_P(chipid, sizeof(chipid), PSTR("%06X"), ESP.getChipId()); |
|
|
|
|
|
|
|
DynamicJsonBuffer jsonBuffer; |
|
|
|
JsonObject& root = jsonBuffer.createObject(); |
|
|
|
|
|
|
|
bool changePassword = false; |
|
|
|
#if FORCE_CHANGE_PASS == 1
|
|
|
|
#if WEB_FORCE_PASS_CHANGE
|
|
|
|
String adminPass = getSetting("adminPass", ADMIN_PASS); |
|
|
|
if (adminPass.equals(ADMIN_PASS)) changePassword = true; |
|
|
|
#endif
|
|
|
@ -526,14 +470,14 @@ void _wsStart(uint32_t client_id) { |
|
|
|
|
|
|
|
root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt(); |
|
|
|
|
|
|
|
root["webPort"] = getSetting("webPort", WEBSERVER_PORT).toInt(); |
|
|
|
root["webPort"] = getSetting("webPort", WEB_PORT).toInt(); |
|
|
|
|
|
|
|
root["apiEnabled"] = getSetting("apiEnabled", ENABLE_API).toInt() == 1; |
|
|
|
root["apiEnabled"] = getSetting("apiEnabled", API_ENABLED).toInt() == 1; |
|
|
|
root["apiKey"] = getSetting("apiKey"); |
|
|
|
|
|
|
|
root["tmpUnits"] = getSetting("tmpUnits", TMP_UNITS).toInt(); |
|
|
|
|
|
|
|
#if ENABLE_DOMOTICZ
|
|
|
|
#if DOMOTICZ_SUPPORT
|
|
|
|
|
|
|
|
root["dczVisible"] = 1; |
|
|
|
root["dczEnabled"] = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1; |
|
|
@ -545,26 +489,26 @@ void _wsStart(uint32_t client_id) { |
|
|
|
dczRelayIdx.add(domoticzIdx(i)); |
|
|
|
} |
|
|
|
|
|
|
|
#if ENABLE_DHT
|
|
|
|
#if DHT_SUPPORT
|
|
|
|
root["dczTmpIdx"] = getSetting("dczTmpIdx").toInt(); |
|
|
|
root["dczHumIdx"] = getSetting("dczHumIdx").toInt(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_DS18B20
|
|
|
|
#if DS18B20_SUPPORT
|
|
|
|
root["dczTmpIdx"] = getSetting("dczTmpIdx").toInt(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_EMON
|
|
|
|
#if EMON_SUPPORT
|
|
|
|
root["dczPowIdx"] = getSetting("dczPowIdx").toInt(); |
|
|
|
root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt(); |
|
|
|
root["dczCurrentIdx"] = getSetting("dczCurrentIdx").toInt(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_ANALOG
|
|
|
|
#if ANALOG_SUPPORT
|
|
|
|
root["dczAnaIdx"] = getSetting("dczAnaIdx").toInt(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_HLW8012
|
|
|
|
#if HLW8012_SUPPORT
|
|
|
|
root["dczPowIdx"] = getSetting("dczPowIdx").toInt(); |
|
|
|
root["dczEnergyIdx"] = getSetting("dczEnergyIdx").toInt(); |
|
|
|
root["dczVoltIdx"] = getSetting("dczVoltIdx").toInt(); |
|
|
@ -573,7 +517,7 @@ void _wsStart(uint32_t client_id) { |
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_INFLUXDB
|
|
|
|
#if INFLUXDB_SUPPORT
|
|
|
|
root["idbVisible"] = 1; |
|
|
|
root["idbHost"] = getSetting("idbHost"); |
|
|
|
root["idbPort"] = getSetting("idbPort", INFLUXDB_PORT).toInt(); |
|
|
@ -582,29 +526,29 @@ void _wsStart(uint32_t client_id) { |
|
|
|
root["idbPassword"] = getSetting("idbPassword"); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_FAUXMO
|
|
|
|
root["fauxmoVisible"] = 1; |
|
|
|
root["fauxmoEnabled"] = getSetting("fauxmoEnabled", FAUXMO_ENABLED).toInt() == 1; |
|
|
|
#if ALEXA_SUPPORT
|
|
|
|
root["alexaVisible"] = 1; |
|
|
|
root["alexaEnabled"] = getSetting("alexaEnabled", ALEXA_ENABLED).toInt() == 1; |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_DS18B20
|
|
|
|
#if DS18B20_SUPPORT
|
|
|
|
root["dsVisible"] = 1; |
|
|
|
root["dsTmp"] = getDSTemperatureStr(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_DHT
|
|
|
|
#if DHT_SUPPORT
|
|
|
|
root["dhtVisible"] = 1; |
|
|
|
root["dhtTmp"] = getDHTTemperature(); |
|
|
|
root["dhtHum"] = getDHTHumidity(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_RF
|
|
|
|
#if RF_SUPPORT
|
|
|
|
root["rfVisible"] = 1; |
|
|
|
root["rfChannel"] = getSetting("rfChannel", RF_CHANNEL); |
|
|
|
root["rfDevice"] = getSetting("rfDevice", RF_DEVICE); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_EMON
|
|
|
|
#if EMON_SUPPORT
|
|
|
|
root["emonVisible"] = 1; |
|
|
|
root["emonApparentPower"] = getApparentPower(); |
|
|
|
root["emonCurrent"] = getCurrent(); |
|
|
@ -612,12 +556,12 @@ void _wsStart(uint32_t client_id) { |
|
|
|
root["emonRatio"] = getSetting("emonRatio", EMON_CURRENT_RATIO); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_ANALOG
|
|
|
|
#if ANALOG_SUPPORT
|
|
|
|
root["analogVisible"] = 1; |
|
|
|
root["analogValue"] = getAnalog(); |
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_HLW8012
|
|
|
|
#if HLW8012_SUPPORT
|
|
|
|
root["powVisible"] = 1; |
|
|
|
root["powActivePower"] = getActivePower(); |
|
|
|
root["powApparentPower"] = getApparentPower(); |
|
|
@ -658,7 +602,7 @@ void _wsStart(uint32_t client_id) { |
|
|
|
|
|
|
|
String output; |
|
|
|
root.printTo(output); |
|
|
|
ws.text(client_id, (char *) output.c_str()); |
|
|
|
wsSend(client_id, (char *) output.c_str()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
@ -674,7 +618,7 @@ bool _wsAuth(AsyncWebSocketClient * client) { |
|
|
|
|
|
|
|
if (index == WS_BUFFER_SIZE) { |
|
|
|
DEBUG_MSG_P(PSTR("[WEBSOCKET] Validation check failed\n")); |
|
|
|
ws.text(client->id(), "{\"message\": \"Session expired, please reload page...\"}"); |
|
|
|
wsSend_P(client->id(), PSTR("{\"message\": \"Session expired, please reload page...\"}")); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
@ -723,24 +667,50 @@ void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTy |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// WEBSERVER
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void webLogRequest(AsyncWebServerRequest *request) { |
|
|
|
DEBUG_MSG_P(PSTR("[WEBSERVER] Request: %s %s\n"), request->methodToString(), request->url().c_str()); |
|
|
|
bool wsConnected() { |
|
|
|
return (_ws.count() > 0); |
|
|
|
} |
|
|
|
|
|
|
|
bool _authenticate(AsyncWebServerRequest *request) { |
|
|
|
String password = getSetting("adminPass", ADMIN_PASS); |
|
|
|
char httpPassword[password.length() + 1]; |
|
|
|
password.toCharArray(httpPassword, password.length() + 1); |
|
|
|
return request->authenticate(HTTP_USERNAME, httpPassword); |
|
|
|
void wsSend(const char * payload) { |
|
|
|
if (_ws.count() > 0) { |
|
|
|
_ws.textAll(payload); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void wsSend_P(PGM_P payload) { |
|
|
|
if (_ws.count() > 0) { |
|
|
|
char buffer[strlen_P(payload)]; |
|
|
|
strcpy_P(buffer, payload); |
|
|
|
_ws.textAll(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void wsSend(uint32_t client_id, const char * payload) { |
|
|
|
_ws.text(client_id, payload); |
|
|
|
} |
|
|
|
|
|
|
|
void wsSend_P(uint32_t client_id, PGM_P payload) { |
|
|
|
char buffer[strlen_P(payload)]; |
|
|
|
strcpy_P(buffer, payload); |
|
|
|
_ws.text(client_id, buffer); |
|
|
|
} |
|
|
|
|
|
|
|
void wsSetup() { |
|
|
|
_ws.onEvent(_wsEvent); |
|
|
|
mqttRegister(_wsMQTTCallback); |
|
|
|
_server->addHandler(&_ws); |
|
|
|
_server->on("/auth", HTTP_GET, _onAuth); |
|
|
|
} |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// API
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool _authAPI(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
if (getSetting("apiEnabled", ENABLE_API).toInt() == 0) { |
|
|
|
if (getSetting("apiEnabled", API_ENABLED).toInt() == 0) { |
|
|
|
DEBUG_MSG_P(PSTR("[WEBSERVER] HTTP API is not enabled\n")); |
|
|
|
request->send(403); |
|
|
|
return false; |
|
|
@ -776,7 +746,7 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) { |
|
|
|
|
|
|
|
return [apiID](AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
webLogRequest(request); |
|
|
|
_webLog(request); |
|
|
|
if (!_authAPI(request)) return; |
|
|
|
|
|
|
|
web_api_t api = _apis[apiID]; |
|
|
@ -805,7 +775,7 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) { |
|
|
|
// Format response according to the Accept header
|
|
|
|
if (_asJson(request)) { |
|
|
|
char buffer[64]; |
|
|
|
sprintf_P(buffer, PSTR("{ \"%s\": %s }"), api.key, p); |
|
|
|
snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": %s }"), api.key, p); |
|
|
|
request->send(200, "application/json", buffer); |
|
|
|
} else { |
|
|
|
request->send(200, "text/plain", p); |
|
|
@ -815,28 +785,9 @@ ArRequestHandlerFunction _bindAPI(unsigned int apiID) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn) { |
|
|
|
|
|
|
|
// Store it
|
|
|
|
web_api_t api; |
|
|
|
char buffer[40]; |
|
|
|
snprintf_P(buffer, 39, PSTR("/api/%s"), url); |
|
|
|
api.url = strdup(buffer); |
|
|
|
api.key = strdup(key); |
|
|
|
api.getFn = getFn; |
|
|
|
api.putFn = putFn; |
|
|
|
_apis.push_back(api); |
|
|
|
|
|
|
|
// Bind call
|
|
|
|
unsigned int methods = HTTP_GET; |
|
|
|
if (putFn != NULL) methods += HTTP_PUT; |
|
|
|
_server->on(buffer, methods, _bindAPI(_apis.size() - 1)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void _onAPIs(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
webLogRequest(request); |
|
|
|
_webLog(request); |
|
|
|
|
|
|
|
if (!_authAPI(request)) return; |
|
|
|
|
|
|
@ -863,7 +814,7 @@ void _onAPIs(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
void _onRPC(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
webLogRequest(request); |
|
|
|
_webLog(request); |
|
|
|
|
|
|
|
if (!_authAPI(request)) return; |
|
|
|
|
|
|
@ -878,7 +829,7 @@ void _onRPC(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
if (action.equals("reset")) { |
|
|
|
response = 200; |
|
|
|
deferred.once_ms(100, []() { |
|
|
|
_web_defer.once_ms(100, []() { |
|
|
|
customReset(CUSTOM_RESET_RPC); |
|
|
|
ESP.restart(); |
|
|
|
}); |
|
|
@ -890,9 +841,50 @@ void _onRPC(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void apiRegister(const char * url, const char * key, apiGetCallbackFunction getFn, apiPutCallbackFunction putFn) { |
|
|
|
|
|
|
|
// Store it
|
|
|
|
web_api_t api; |
|
|
|
char buffer[40]; |
|
|
|
snprintf_P(buffer, sizeof(buffer), PSTR("/api/%s"), url); |
|
|
|
api.url = strdup(buffer); |
|
|
|
api.key = strdup(key); |
|
|
|
api.getFn = getFn; |
|
|
|
api.putFn = putFn; |
|
|
|
_apis.push_back(api); |
|
|
|
|
|
|
|
// Bind call
|
|
|
|
unsigned int methods = HTTP_GET; |
|
|
|
if (putFn != NULL) methods += HTTP_PUT; |
|
|
|
_server->on(buffer, methods, _bindAPI(_apis.size() - 1)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void apiSetup() { |
|
|
|
_server->on("/apis", HTTP_GET, _onAPIs); |
|
|
|
_server->on("/rpc", HTTP_GET, _onRPC); |
|
|
|
} |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// WEBSERVER
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void _webLog(AsyncWebServerRequest *request) { |
|
|
|
DEBUG_MSG_P(PSTR("[WEBSERVER] Request: %s %s\n"), request->methodToString(), request->url().c_str()); |
|
|
|
} |
|
|
|
|
|
|
|
bool _authenticate(AsyncWebServerRequest *request) { |
|
|
|
String password = getSetting("adminPass", ADMIN_PASS); |
|
|
|
char httpPassword[password.length() + 1]; |
|
|
|
password.toCharArray(httpPassword, password.length() + 1); |
|
|
|
return request->authenticate(WEB_USERNAME, httpPassword); |
|
|
|
} |
|
|
|
|
|
|
|
void _onAuth(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
webLogRequest(request); |
|
|
|
_webLog(request); |
|
|
|
if (!_authenticate(request)) return request->requestAuthentication(); |
|
|
|
|
|
|
|
IPAddress ip = request->client()->remoteIP(); |
|
|
@ -915,7 +907,7 @@ void _onAuth(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
void _onGetConfig(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
webLogRequest(request); |
|
|
|
_webLog(request); |
|
|
|
if (!_authenticate(request)) return request->requestAuthentication(); |
|
|
|
|
|
|
|
AsyncJsonResponse * response = new AsyncJsonResponse(); |
|
|
@ -932,17 +924,17 @@ void _onGetConfig(AsyncWebServerRequest *request) { |
|
|
|
} |
|
|
|
|
|
|
|
char buffer[100]; |
|
|
|
sprintf_P(buffer, PSTR("attachment; filename=\"%s-backup.json\""), (char *) getSetting("hostname").c_str()); |
|
|
|
snprintf_P(buffer, sizeof(buffer), PSTR("attachment; filename=\"%s-backup.json\""), (char *) getSetting("hostname").c_str()); |
|
|
|
response->addHeader("Content-Disposition", buffer); |
|
|
|
response->setLength(); |
|
|
|
request->send(response); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#if EMBEDDED_WEB
|
|
|
|
#if WEB_EMBEDDED
|
|
|
|
void _onHome(AsyncWebServerRequest *request) { |
|
|
|
|
|
|
|
webLogRequest(request); |
|
|
|
_webLog(request); |
|
|
|
|
|
|
|
if (request->header("If-Modified-Since").equals(_last_modified)) { |
|
|
|
|
|
|
@ -1043,7 +1035,7 @@ void _onUpgrade(AsyncWebServerRequest *request) { |
|
|
|
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", Update.hasError() ? "FAIL" : "OK"); |
|
|
|
response->addHeader("Connection", "close"); |
|
|
|
if (!Update.hasError()) { |
|
|
|
deferred.once_ms(100, []() { |
|
|
|
_web_defer.once_ms(100, []() { |
|
|
|
customReset(CUSTOM_RESET_UPGRADE); |
|
|
|
ESP.restart(); |
|
|
|
}); |
|
|
@ -1081,8 +1073,13 @@ void _onUpgradeData(AsyncWebServerRequest *request, String filename, size_t inde |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void webSetup() { |
|
|
|
|
|
|
|
// Cache the Last-Modifier header value
|
|
|
|
snprintf_P(_last_modified, sizeof(_last_modified), PSTR("%s %s GMT"), __DATE__, __TIME__); |
|
|
|
|
|
|
|
// Create server
|
|
|
|
#if ASYNC_TCP_SSL_ENABLED & WEB_USE_SSL
|
|
|
|
unsigned int port = 443; |
|
|
@ -1092,34 +1089,27 @@ void webSetup() { |
|
|
|
_server = new AsyncWebServer(port); |
|
|
|
|
|
|
|
// Setup websocket
|
|
|
|
ws.onEvent(_wsEvent); |
|
|
|
mqttRegister(wsMQTTCallback); |
|
|
|
|
|
|
|
// Cache the Last-Modifier header value
|
|
|
|
sprintf_P(_last_modified, PSTR("%s %s GMT"), __DATE__, __TIME__); |
|
|
|
wsSetup(); |
|
|
|
|
|
|
|
// Setup webserver
|
|
|
|
_server->addHandler(&ws); |
|
|
|
// API setup
|
|
|
|
apiSetup(); |
|
|
|
|
|
|
|
// Rewrites
|
|
|
|
_server->rewrite("/", "/index.html"); |
|
|
|
|
|
|
|
// Serve home (basic authentication protection)
|
|
|
|
#if EMBEDDED_WEB
|
|
|
|
#if WEB_EMBEDDED
|
|
|
|
_server->on("/index.html", HTTP_GET, _onHome); |
|
|
|
#endif
|
|
|
|
_server->on("/config", HTTP_GET, _onGetConfig); |
|
|
|
_server->on("/auth", HTTP_GET, _onAuth); |
|
|
|
_server->on("/apis", HTTP_GET, _onAPIs); |
|
|
|
_server->on("/rpc", HTTP_GET, _onRPC); |
|
|
|
_server->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeData); |
|
|
|
|
|
|
|
// Serve static files
|
|
|
|
#if ENABLE_SPIFFS
|
|
|
|
#if SPIFFS_SUPPORT
|
|
|
|
_server->serveStatic("/", SPIFFS, "/") |
|
|
|
.setLastModified(_last_modified) |
|
|
|
.setFilter([](AsyncWebServerRequest *request) -> bool { |
|
|
|
webLogRequest(request); |
|
|
|
_webLog(request); |
|
|
|
return true; |
|
|
|
}); |
|
|
|
#endif
|
|
|
@ -1139,3 +1129,5 @@ void webSetup() { |
|
|
|
DEBUG_MSG_P(PSTR("[WEBSERVER] Webserver running on port %d\n"), port); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif // WEB_SUPPORT
|