summaryrefslogtreecommitdiff
path: root/portal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'portal.cpp')
-rw-r--r--portal.cpp275
1 files changed, 275 insertions, 0 deletions
diff --git a/portal.cpp b/portal.cpp
new file mode 100644
index 0000000..5013249
--- /dev/null
+++ b/portal.cpp
@@ -0,0 +1,275 @@
+#include "portal.h"
+
+//#define DEBUG
+
+#define NONE -1
+#define BLINK -2
+#define FADEIN -3
+#define FADEOUT -4
+
+const IPAddress apIP(192, 168, 1, 1);
+const char* apSSID;
+
+boolean isAPactive;
+String scanSSIDList;
+
+int apiOverrides[4] = {NONE, NONE, NONE, NONE};
+boolean requestLedUpdate = false;
+
+DNSServer dnsServer;
+ESP8266WebServer webServer(80);
+
+#define ICONPATH "http://openweathermap.org/img/w/"
+String weatherIcon = "";
+
+void portalSetup(String ssid) {
+ apSSID = ssid.c_str();
+
+ WiFi.hostname(ssid);
+ // connect to configured network or fallback to setup AP
+ if (!enterClientMode(NULL, NULL)) {
+ enterAPMode(); // let the user enter the credentials
+ }
+ startWebServer();
+}
+
+void portalLoop() {
+ if (isAPactive) {
+ dnsServer.processNextRequest();
+ }
+ webServer.handleClient();
+}
+
+boolean isConnected() {
+ return !isAPactive;
+}
+
+void startWebServer() {
+#ifdef DEBUG
+ Serial.print(F("Starting Web Server at "));
+ Serial.print(WiFi.softAPIP()); Serial.print(" "); Serial.println(WiFi.localIP());
+#endif
+
+ webServer.on("/settings", []() {
+ String s = "<h1>WLAN-Einstellungen</h1><p>Bitte wähle deinen Accesspoint und gib das Passwort ein.</p>";
+ s += "<form method=\"get\" action=\"setap\"><label>SSID: </label><select name=\"ssid\">";
+ s += scanSSIDList;
+ s += "</select><br>Passwort: <input name=\"pass\" length=64 type=\"password\"><input type=\"submit\"></form>";
+ webServer.send(200, "text/html", makePage("WLAN-Einstellungen", s));
+ });
+
+ webServer.on("/setap", []() {
+ String ssid = urlDecode(webServer.arg("ssid"));
+#ifdef DEBUG
+ Serial.print(F("Connecting to: ")); Serial.println(ssid);
+#endif
+ String pass = urlDecode(webServer.arg("pass"));
+
+ String s = "<h1>Einrichtung abgeschlossen.</h1><p>Das Gerät wird zu \"";
+ s += ssid;
+ s += "\" verbunden.";
+ webServer.send(200, "text/html", makePage("WLAN-Einstellungen", s));
+
+ if (!enterClientMode(ssid.c_str(), pass.c_str())) {
+ enterAPMode(); // wrong passphrase
+ }
+ });
+
+ webServer.on("/reset", []() {
+ String s = "<h1>Zurückgesetzt</h1><p>Zum Einstellen bitte mit dem Accesspoint \"Snowman\" verbinden.</p>";
+ webServer.send(200, "text/html", makePage("Zurücksetzen", s));
+ enterAPMode();
+ });
+
+ webServer.on("/api", []() {
+ String response = "";
+
+ for (int n = 0; n < webServer.args(); n++) {
+ String sArgName = webServer.argName(n);
+
+ if (sArgName.startsWith("led")) {
+ String sPinNumber = sArgName.substring(3);
+ if (sPinNumber == "") {
+ String msg = "API: Invalid argument '" + sArgName + "' - pin number invalid\n";
+ response += msg;
+#ifdef DEBUG
+ Serial.print(msg);
+#endif
+ continue;
+ }
+ int pinNumber = sPinNumber.toInt();
+ if (pinNumber < 0 || pinNumber > 3) {
+ String msg = "API: Invalid argument '" + sArgName + "' - pin number out of range\n";
+ response += msg;
+#ifdef DEBUG
+ Serial.print(msg);
+#endif
+ continue;
+ }
+
+ String sArg = webServer.arg(n);
+ int val = -1;
+ if (sArg == "fade" || sArg == "fadein") {
+ val = FADEIN;
+ } else if (sArg == "fadeout") {
+ val = FADEOUT;
+ } else if (sArg == "blink") {
+ val = BLINK;
+ } else if (sArg == "clear") {
+ val = NONE;
+ } else {
+ val = sArg.toInt();
+ }
+
+ if ((val != FADEIN && val != FADEOUT && val != BLINK && val != NONE && val < 0) || val > PWMRANGE) {
+ String msg = "API: Value out of range for argument '" + sArgName + "': '" + String(val) + "'\n";
+ response += msg;
+#ifdef DEBUG
+ Serial.print(msg);
+#endif
+ continue;
+ }
+ String msg = "API: Overriding pin " + String(pinNumber) + " to " + String(val) + "\n";
+ response += msg;
+#ifdef DEBUG
+ Serial.print(msg);
+#endif
+ apiOverrides[pinNumber] = val;
+ requestLedUpdate = true;
+ }
+ if (webServer.argName(n) == "clear") {
+ if (webServer.arg(n) == "true") {
+ for (int j = 0; j < 4; j++) {
+ apiOverrides[j] = NONE;
+ }
+ }
+ String msg = "API: Cleared pin overrides\n";
+ response += msg;
+#ifdef DEBUG
+ Serial.print(msg);
+#endif
+ requestLedUpdate = true;
+ }
+ }
+
+ webServer.send(200, "text/html", makePage("API", "<pre>" + response + "</pre>"));
+ });
+
+ webServer.on("/", []() {
+ String s = "<h1>Snowman</h1><p><a href=\"/reset\">Einstellungen zurücksetzen</a></p>";
+ s += "<p><a href=\"/settings\">Einstellungen ändern</a></p>";
+ s += "<p><img src=\"" ICONPATH + weatherIcon + ".png\" /></p>";
+ webServer.send(200, "text/html", makePage("Snowman", s));
+ });
+
+ webServer.onNotFound([]() {
+ String redirect = "http://" + ipToStr(apIP) + "/settings";
+ webServer.send(200, "text/html", makeRedirectPage(redirect, 2));
+ });
+
+ webServer.begin();
+}
+
+int getAPIOverride(int pin) {
+ return apiOverrides[pin];
+}
+
+boolean apiNeedLedUpdate() {
+ return requestLedUpdate;
+}
+
+void apiUpdatedLeds() {
+ requestLedUpdate = false;
+}
+
+void setWeatherIcon(String id) {
+ weatherIcon = id;
+}
+
+// must enter AP mode after scanning (SSID & PW get lost)
+void scanSSIDs() {
+#ifdef DEBUG
+ Serial.println(F("Scanning networks"));
+#endif
+
+ WiFi.disconnect();
+ WiFi.mode(WIFI_STA);
+ delay(100);
+
+ int n = WiFi.scanNetworks();
+ delay(100);
+ scanSSIDList = "";
+ for (int i = 0; i < n; ++i) {
+ scanSSIDList += "<option value=\"";
+ scanSSIDList += WiFi.SSID(i); // FIXME XSS attack possible?
+ scanSSIDList += "\">";
+ scanSSIDList += WiFi.SSID(i);
+ scanSSIDList += "</option>";
+ }
+
+#ifdef DEBUG
+ Serial.println(F("Finished scanning"));
+#endif
+}
+
+
+// also read https://github.com/esp8266/Arduino/issues/529
+
+// sets up an AP for the user to enter his/her WiFi credentials
+void enterAPMode() {
+#ifdef DEBUG
+ Serial.println(F("Entering AP mode"));
+#endif
+
+ scanSSIDs();
+
+ // create ad hoc setup-network
+ WiFi.disconnect();
+ WiFi.mode(WIFI_AP);
+ isAPactive = true;
+ delay(100);
+ WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
+ WiFi.softAP(apSSID);
+#ifdef DEBUG
+ Serial.println(F("Started Access Point"));
+#endif
+
+ dnsServer.start(apIP);
+}
+
+// returns true if successfully connected to the last AP
+boolean enterClientMode(const char* ssid, const char *passphrase) {
+ dnsServer.stop();
+
+ if (ssid && strlen(ssid) > 0) { // if ssid is not given, try to connect to the memorized AP
+ WiFi.disconnect();
+ WiFi.begin(ssid, passphrase); // register credentials
+ }
+ WiFi.mode(WIFI_STA);
+ isAPactive = false;
+ delay(100);
+
+ // SSID and password are memorized until WiFi.begin()
+ // https://github.com/esp8266/Arduino/issues/244
+ int count = 0;
+#ifdef DEBUG
+ Serial.print(F("Waiting for Wi-Fi connection to ")); Serial.println(WiFi.SSID());
+#endif
+ while (count++ < 30) { // attempt for about 30 * 1/2 = 15 seconds
+ if (WiFi.status() == WL_CONNECTED) {
+#ifdef DEBUG
+ Serial.println(); Serial.println(F("Connected!"));
+#endif
+ return true;
+ }
+ delay(500);
+#ifdef DEBUG
+ Serial.print(".");
+#endif
+ }
+#ifdef DEBUG
+ Serial.println(); Serial.println(F("Not connected"));
+#endif
+ return false;
+}
+