diff --git a/lib/HanConfigAp/src/HanConfigAp.cpp b/lib/HanConfigAp/src/HanConfigAp.cpp
index 06a6c0fc..1c901e49 100644
--- a/lib/HanConfigAp/src/HanConfigAp.cpp
+++ b/lib/HanConfigAp/src/HanConfigAp.cpp
@@ -1,4 +1,8 @@
#include "HanConfigAp.h"
+#include "config_html.h"
+#include "style_css.h"
+
+#include "Base64.h"
#if defined(ESP8266)
ESP8266WebServer HanConfigAp::server(80);
@@ -66,44 +70,142 @@ void HanConfigAp::setup(int accessPointButtonPin, Stream* debugger)
/* Setup the DNS server redirecting all the domains to this IP */
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
-
- server.on("/", handleRoot);
- server.on("/save", handleSave);
- server.begin(); // Web server start
-
- print("Web server is ready for config at http://");
- print(WiFi.softAPIP());
- println("/");
}
}
+void HanConfigAp::enableWeb() {
+ server.on("/", handleRoot);
+ server.on("/style.css", handleStyle);
+ server.on("/save", handleSave);
+ server.begin(); // Web server start
+
+ print("Web server is ready for config at http://");
+ if(isActivated) {
+ print(WiFi.softAPIP());
+ } else {
+ print(WiFi.localIP());
+ }
+ println("/");
+}
+
bool HanConfigAp::loop() {
- if (isActivated)
- {
+ if(isActivated) {
//DNS
dnsServer.processNextRequest();
- //HTTP
- server.handleClient();
- return true;
- }
- else
- {
- return false;
}
+
+ //HTTP
+ server.handleClient();
+
+ return isActivated;
}
/** Handle root or redirect to captive portal */
void HanConfigAp::handleRoot() {
println("Serving / over http...");
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
- server.setContentLength(CONTENT_LENGTH_UNKNOWN);
- String html = String("\r\n\r\n
\r\n\t\r\n\r\n\r\n\r\n\t\r\n\r\n\t\r\n\r\n");
+ configuration *config = new configuration();
+ config->load();
+
+ String html = CONFIG_HTML;
+
+ if(config->hasConfig()) {
+ bool access = !config->isAuth();
+ if(config->isAuth() && server.hasHeader("Authorization")) {
+ String expectedAuth = String(config->authUser) + ":" + String(config->authPass);
+
+ String providedPwd = server.header("Authorization");
+ providedPwd.replace("Basic ", "");
+ char inputString[providedPwd.length()];
+ providedPwd.toCharArray(inputString, providedPwd.length()+1);
+
+ int inputStringLength = sizeof(inputString);
+ int decodedLength = Base64.decodedLength(inputString, inputStringLength);
+ char decodedString[decodedLength];
+ Base64.decode(decodedString, inputString, inputStringLength);
+ print("Received auth: ");
+ println(decodedString);
+ access = String(decodedString).equals(expectedAuth);
+ }
+
+ if(!access) {
+ server.sendHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"");
+ server.send(401, "text/html", "");
+ } else {
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+
+ html.replace("${config.ssid}", config->ssid);
+ html.replace("${config.ssidPassword}", config->ssidPassword);
+ switch (config->meterType) {
+ case 1:
+ html.replace("${config.meterType0}", "");
+ html.replace("${config.meterType1}", "selected");
+ html.replace("${config.meterType2}", "");
+ html.replace("${config.meterType3}", "");
+ break;
+ case 2:
+ html.replace("${config.meterType0}", "");
+ html.replace("${config.meterType1}", "");
+ html.replace("${config.meterType2}", "selected");
+ html.replace("${config.meterType3}", "");
+ break;
+ case 3:
+ html.replace("${config.meterType0}", "");
+ html.replace("${config.meterType1}", "");
+ html.replace("${config.meterType2}", "");
+ html.replace("${config.meterType3}", "selected");
+ break;
+ default:
+ html.replace("${config.meterType0}", "selected");
+ html.replace("${config.meterType1}", "");
+ html.replace("${config.meterType2}", "");
+ html.replace("${config.meterType3}", "");
+ }
+ html.replace("${config.mqtt}", config->mqtt);
+ html.replace("${config.mqttPort}", String(config->mqttPort));
+ html.replace("${config.mqttClientID}", config->mqttClientID);
+ html.replace("${config.mqttPublishTopic}", config->mqttPublishTopic);
+ html.replace("${config.mqttSubscribeTopic}", config->mqttSubscribeTopic);
+ html.replace("${config.mqttUser}", config->mqttUser);
+ html.replace("${config.mqttPass}", config->mqttPass);
+ html.replace("${config.authUser}", config->authUser);
+ html.replace("${config.authPass}", config->authPass);
+ }
+
+ } else {
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+
+ html.replace("${config.ssid}", "");
+ html.replace("${config.ssidPassword}", "");
+ html.replace("${config.meterType0}", "selected");
+ html.replace("${config.meterType1}", "");
+ html.replace("${config.meterType2}", "");
+ html.replace("${config.meterType3}", "");
+ html.replace("${config.mqtt}", "");
+ html.replace("${config.mqttPort}", "1883");
+ html.replace("${config.mqttClientID}", "");
+ html.replace("${config.mqttPublishTopic}", "");
+ html.replace("${config.mqttSubscribeTopic}", "");
+ html.replace("${config.mqttUser}", "");
+ html.replace("${config.mqttPass}", "");
+ html.replace("${config.authUser}", "");
+ html.replace("${config.authPass}", "");
+ }
server.send(200, "text/html", html);
}
+void HanConfigAp::handleStyle() {
+ println("Serving /style.css over http...");
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "text/html", STYLE_CSS, sizeof(STYLE_CSS));
+}
void HanConfigAp::handleSave() {
configuration *config = new configuration();
@@ -146,12 +248,20 @@ void HanConfigAp::handleSave() {
config->mqttPass = new char[temp.length() + 1];
temp.toCharArray(config->mqttPass, temp.length() + 1, 0);
+ temp = server.arg("authUser");
+ config->authUser = new char[temp.length() + 1];
+ temp.toCharArray(config->authUser, temp.length() + 1, 0);
+
+ temp = server.arg("authPass");
+ config->authPass = new char[temp.length() + 1];
+ temp.toCharArray(config->authPass, temp.length() + 1, 0);
+
println("Saving configuration now...");
if (HanConfigAp::debugger) config->print(HanConfigAp::debugger);
if (config->save())
{
- println("Successfully saved. Will roboot now.");
+ println("Successfully saved. Will reboot now.");
String html = "Successfully Saved!
Device is restarting now...
";
server.send(200, "text/html", html);
#if defined(ESP8266)
diff --git a/lib/HanConfigAp/src/HanConfigAp.h b/lib/HanConfigAp/src/HanConfigAp.h
index 42eab7c6..d1e01d85 100644
--- a/lib/HanConfigAp/src/HanConfigAp.h
+++ b/lib/HanConfigAp/src/HanConfigAp.h
@@ -27,6 +27,7 @@
class HanConfigAp {
public:
void setup(int accessPointButtonPin, Stream* debugger);
+ void enableWeb();
bool loop();
bool hasConfig();
configuration config;
@@ -46,6 +47,7 @@ private:
// Web server
static void handleRoot();
+ static void handleStyle();
static void handleSave();
#if defined(ESP8266)
static ESP8266WebServer server;
diff --git a/lib/HanConfigAp/src/config_html.h b/lib/HanConfigAp/src/config_html.h
new file mode 100644
index 00000000..310b0bce
--- /dev/null
+++ b/lib/HanConfigAp/src/config_html.h
@@ -0,0 +1,85 @@
+const char CONFIG_HTML[] PROGMEM = R"=="==(
+
+
+
+
+
+
+
+
+ AMS2MQTT - configuration
+
+
+
+
+
+)=="==";
diff --git a/lib/HanConfigAp/src/configuration.cpp b/lib/HanConfigAp/src/configuration.cpp
index 94031f07..3fd2e8ce 100644
--- a/lib/HanConfigAp/src/configuration.cpp
+++ b/lib/HanConfigAp/src/configuration.cpp
@@ -38,6 +38,13 @@ bool configuration::save()
else
address += saveBool(address, false);
+
+ address += saveBool(address, isAuth());
+ if (isAuth()) {
+ address += saveString(address, authUser);
+ address += saveString(address, authPass);
+ }
+
bool success = EEPROM.commit();
EEPROM.end();
@@ -50,8 +57,22 @@ bool configuration::load()
int address = EEPROM_CONFIG_ADDRESS;
bool success = false;
+ ssid = (char*)String("").c_str();
+ ssidPassword = (char*)String("").c_str();
+ meterType = (byte)0;
+ mqtt = (char*)String("").c_str();
+ mqttClientID = (char*)String("").c_str();
+ mqttPublishTopic = (char*)String("").c_str();
+ mqttSubscribeTopic = (char*)String("").c_str();
+ mqttUser = 0;
+ mqttPass = 0;
+ mqttPort = 1883;
+ authUser = 0;
+ authPass = 0;
+
EEPROM.begin(EEPROM_SIZE);
- if (EEPROM.read(address) == EEPROM_CHECK_SUM)
+ int cs = EEPROM.read(address);
+ if (cs >= 71)
{
address++;
@@ -80,18 +101,18 @@ bool configuration::load()
success = true;
}
- else
- {
- ssid = (char*)String("").c_str();
- ssidPassword = (char*)String("").c_str();
- meterType = (byte)0;
- mqtt = (char*)String("").c_str();
- mqttClientID = (char*)String("").c_str();
- mqttPublishTopic = (char*)String("").c_str();
- mqttSubscribeTopic = (char*)String("").c_str();
- mqttUser = 0;
- mqttPass = 0;
- mqttPort = 1883;
+ if(cs >= 72) {
+ bool auth = false;
+ address += readBool(address, &auth);
+ if (auth) {
+ address += readString(address, &authUser);
+ address += readString(address, &authPass);
+ } else {
+ authUser = 0;
+ authPass = 0;
+ }
+
+ success = true;
}
EEPROM.end();
return success;
@@ -102,6 +123,10 @@ bool configuration::isSecure()
return (mqttUser != 0) && (String(mqttUser).length() > 0);
}
+bool configuration::isAuth() {
+ return (authUser != 0) && (String(authUser).length() > 0);
+}
+
int configuration::readInt(int address, int *value)
{
int lower = EEPROM.read(address);
@@ -147,20 +172,6 @@ int configuration::saveByte(int address, byte value)
}
void configuration::print(Stream* debugger)
{
- /*
- char* ssid;
- char* ssidPassword;
- byte meterType;
- char* mqtt;
- int mqttPort;
- char* mqttClientID;
- char* mqttPublishTopic;
- char* mqttSubscribeTopic;
- bool secure;
- char* mqttUser;
- char* mqttPass;
- */
-
debugger->println("Configuration:");
debugger->println("-----------------------------------------------");
debugger->printf("ssid: %s\r\n", this->ssid);
@@ -178,6 +189,13 @@ void configuration::print(Stream* debugger)
debugger->printf("mqttUser: %s\r\n", this->mqttUser);
debugger->printf("mqttPass: %s\r\n", this->mqttPass);
}
+
+ if (this->isAuth()) {
+ debugger->printf("WEB AUTH:\r\n");
+ debugger->printf("authUser: %s\r\n", this->authUser);
+ debugger->printf("authPass: %s\r\n", this->authPass);
+ }
+
debugger->println("-----------------------------------------------");
}
diff --git a/lib/HanConfigAp/src/configuration.h b/lib/HanConfigAp/src/configuration.h
index 0dfc86ad..d785a094 100644
--- a/lib/HanConfigAp/src/configuration.h
+++ b/lib/HanConfigAp/src/configuration.h
@@ -25,8 +25,12 @@ public:
char* mqttPass;
byte meterType;
+ char* authUser;
+ char* authPass;
+
bool hasConfig();
bool isSecure();
+ bool isAuth();
bool save();
bool load();
@@ -35,7 +39,7 @@ protected:
private:
const int EEPROM_SIZE = 512;
- const byte EEPROM_CHECK_SUM = 71; // Used to check if config is stored. Change if structure changes
+ const byte EEPROM_CHECK_SUM = 72; // Used to check if config is stored. Change if structure changes
const int EEPROM_CONFIG_ADDRESS = 0;
int saveString(int pAddress, char* pString);
diff --git a/lib/HanConfigAp/src/style_css.h b/lib/HanConfigAp/src/style_css.h
new file mode 100644
index 00000000..9b28b09c
--- /dev/null
+++ b/lib/HanConfigAp/src/style_css.h
@@ -0,0 +1,135 @@
+const char STYLE_CSS[] PROGMEM = R"=="==(
+body,div,input {
+ font-family: "Roboto", Arial, Lucida Grande;
+}
+.wrapper {
+ width: 500px;
+ position: absolute;
+ padding: 30px;
+ background-color: #FFF;
+ border-radius: 1px;
+ color: #333;
+ border-color: rgba(0, 0, 0, 0.03);
+ box-shadow: 0 2px 2px rgba(0, 0, 0, .24), 0 0 2px rgba(0, 0, 0, .12);
+ margin-left: 20px;
+ margin-top: 20px;
+}
+div {
+ padding-bottom: 5px;
+}
+label {
+ font-family: "Roboto", "Helvetica Neue", sans-serif;
+ font-size: 14px;
+ line-height: 16px;
+ width: 100px;
+ display: inline-block;
+}
+input {
+ font-family: "Roboto", "Helvetica Neue", sans-serif;
+ font-size: 14px;
+ line-height: 16px;
+ bottom: 30px;
+ border: none;
+ border-bottom: 1px solid #d4d4d4;
+ padding: 10px;
+ background: transparent;
+ transition: all .25s ease;
+}
+input[type=number] {
+ width: 70px;
+ margin-left: 5px;
+}
+input:focus {
+ outline: none;
+ border-bottom: 1px solid #3f51b5;
+}
+h2 {
+ text-align: left;
+ font-size: 20px;
+ font-weight: bold;
+ letter-spacing: 3px;
+ line-height: 28px;
+}
+.submit-button {
+ position: absolute;
+ text-align: right;
+ border-radius: 20px;
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ background-color: #3f51b5;
+ color: #FFF;
+ padding: 12px 25px;
+ display: inline-block;
+ font-size: 12px;
+ font-weight: bold;
+ letter-spacing: 2px;
+ right: 0px;
+ bottom: 10px;
+ cursor: pointer;
+ transition: all .25s ease;
+ box-shadow: 0 2px 2px rgba(0, 0, 0, .24), 0 0 2px rgba(0, 0, 0, .12);
+ width: 100px;
+}
+.select-style {
+ border-top: 10px solid white;
+ border-bottom: 1px solid #d4d4d4;
+ color: #ffffff;
+ cursor: pointer;
+ display: block;
+ font-family: Roboto, "Helvetica Neue", sans-serif;
+ font-size: 14px;
+ font-weight: 400;
+ height: 16px;
+ line-height: 14px;
+ min-width: 200px;
+ padding-bottom: 7px;
+ padding-left: 0px;
+ padding-right: 0px;
+ position: relative;
+ text-align: left;
+ width: 80%;
+ -webkit-box-direction: normal;
+ overflow: hidden;
+ background: #ffffff url("data:image/png;base64,R0lGODlhDwAUAIABAAAAAP///yH5BAEAAAEALAAAAAAPABQAAAIXjI+py+0Po5wH2HsXzmw//lHiSJZmUAAAOw==") no-repeat 98% 50%;
+}
+.disabled-option {
+ color: #d4d4d4;
+}
+.select-style select {
+ padding: 5px 8px;
+ width: 100%;
+ border: none;
+ box-shadow: none;
+ background: transparent;
+ background-image: none;
+ -webkit-appearance: none;
+}
+.select-style select:focus {
+ outline: none;
+ border: none;
+}
+@media only screen and (max-width: 1000px) {
+ .wrapper {
+ width: 80%;
+ }
+}
+@media only screen and (max-width: 300px) {
+ .wrapper {
+ width: 75%;
+ }
+}
+@media only screen and (max-width: 600px) {
+ .wrapper {
+ width: 80%;
+ margin-left: 0px;
+ margin-top: 0px;
+ }
+ .submit-button {
+ bottom: 0px;
+ width: 70px;
+ }
+ input {
+ width: 100%;
+ }
+}
+)=="==";
diff --git a/platformio.ini b/platformio.ini
index 091c3d93..84250766 100755
--- a/platformio.ini
+++ b/platformio.ini
@@ -3,7 +3,7 @@ extra_configs = platformio-user.ini
[common]
framework = arduino
-lib_deps = HanConfigAp@1.0.0, HanReader@1.0.0, HanToJson@1.0.0, ArduinoJson@^6.0.0, MQTT@^2.4.0, DallasTemperature@^3.8.0
+lib_deps = HanConfigAp@1.0.0, HanReader@1.0.0, HanToJson@1.0.0, ArduinoJson@^6.0.0, MQTT@^2.4.0, DallasTemperature@^3.8.0, Base64@0.0.1
[env:esp12e]
platform = espressif8266
diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino
index 79cec03c..b3279749 100644
--- a/src/AmsToMqttBridge.ino
+++ b/src/AmsToMqttBridge.ino
@@ -59,10 +59,11 @@ HardwareSerial* debugger = NULL;
HanReader hanReader;
// the setup function runs once when you press reset or power the board
-void setup()
-{
- // Uncomment to debug over the same port as used for HAN communication
- //debugger = &Serial;
+void setup() {
+
+#if DEBUG_MODE
+ debugger = &Serial;
+#endif
if (debugger) {
// Setup serial port for debugging
@@ -100,6 +101,8 @@ void setup()
// Compensate for the known Kaifa bug
hanReader.compensateFor09HeaderBug = (ap.config.meterType == 1);
}
+
+ ap.enableWeb();
}
// the loop function runs over and over again until power down or reset