//********************************// void handleNotFound() { String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(404, "text/plain", message); } //********************************// void init_webserver() { // -------------------- // server.on("/state", HTTP_PUT, []() { // HTTP PUT request used to set a new light state DynamicJsonDocument root(512); DeserializationError error = deserializeJson(root, server.arg("plain")); if (error) { server.send(404, "text/plain", "FAIL. " + server.arg("plain")); } else { for (JsonPair state : root.as()) { const char* key = state.key().c_str(); int light = atoi(key) - 1; if (light < 0) { light = 0; } else if (light > (LIGHTS_COUNT-1)) { light = (LIGHTS_COUNT-1); } JsonObject values = state.value(); uint8_t tmp = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); if (values.containsKey("on")) { if (values["on"]) { light_state[light] = true; if (tmp == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_OFF) { EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON); } } else { light_state[light] = false; if (tmp == LAST_STATE_STARTUP_LIGHT_LAST_STATE && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == LIGHT_STATE_ON) { EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF); } } } if (values.containsKey("bri")) { bri[light] = values["bri"]; } if (values.containsKey("bri_inc")) { bri[light] += (int)values["bri_inc"]; if (bri[light] > 255) bri[light] = 255; else if (bri[light] < 1) bri[light] = 1; } if (values.containsKey("transitiontime")) { default_transitiontime = values["transitiontime"]; if (default_transitiontime <= 0) { default_transitiontime = 1; } if (tc_enabled == TIMING_CONTROL_DISABLED) { for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) { // set the default transition time for all lights process_lightdata(i, default_transitiontime); } } } } String output; serializeJson(root, output); server.send(200, "text/plain", output); } }); // -------------------- // server.on("/state", HTTP_GET, []() { // HTTP GET request used to fetch current light state uint8_t light = server.arg("light").toInt() - 1; if (light < 0) { light = 0; } else if (light > (LIGHTS_COUNT-1)) { light = (LIGHTS_COUNT-1); } DynamicJsonDocument root(512); root["on"] = light_state[light]; root["bri"] = bri[light]; root["curbri"] = (int)current_bri[light]; root["curpwm"] = current_pwm[light]; String output; serializeJson(root, output); server.send(200, "text/plain", output); }); // -------------------- // server.on("/detect", []() { // HTTP GET request used to discover the light type char macString[32] = { 0 }; sprintf(macString, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); DynamicJsonDocument root(1024); root["name"] = LIGHT_NAME; root["lights"] = LIGHTS_COUNT; root["protocol"] = "native_multi"; root["modelid"] = "LWB010"; root["type"] = "dimmable_light"; root["mac"] = String(macString); root["version"] = LIGHT_VERSION; String output; serializeJson(root, output); server.send(200, "text/plain", output); }); // -------------------- // server.on("/test_pwm", []() { test_pwm = true; tc_enabled_old = tc_enabled; tc_enabled = TIMING_CONTROL_DISABLED; server.send(200, "text/html", "OK"); }); // -------------------- // server.on("/tc_data_blocks_read", []() { String output = tc_getJsonData(); server.send(200, "application/json", output); }); // -------------------- // server.on("/tc_data_blocks_store", []() { if (server.hasArg("data")) { String jsonData = server.arg("data"); Serial.println("Received: " + jsonData); tc_jsonDataBlocksToEEPROM(jsonData); server.send(200, "text/html", "tcdata saved"); } }); // -------------------- // server.on("/js_top", []() { server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/top.js"))); }); // -------------------- // server.on("/js_bottom", []() { server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/bottom.js"))); }); // -------------------- // server.on("/css", []() { server.send(200, "text/css", loadSPIFFSFile("/style.css")); }); // -------------------- // server.on("/", []() { if (server.hasArg("transition")) { default_transitiontime = server.arg("transition").toFloat(); if (default_transitiontime <= 0) { default_transitiontime = 1; } if (tc_enabled == TIMING_CONTROL_DISABLED) { for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) { // set the default transition time for all lights process_lightdata(i, default_transitiontime); Serial.println("transition for light " + (String)i + " set to " + (String)default_transitiontime); } } } // startup behavior switch handling if (server.hasArg("startup")) { int startup = server.arg("startup").toInt(); if (EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS) != startup) { EEPROM.write(EEPROM_LAST_STATE_STARTUP_ADDRESS, startup); for (uint8_t i = 0; i < LIGHTS_COUNT; i++) { uint8_t tmp = (light_state[i] == true ? LIGHT_STATE_ON : LIGHT_STATE_OFF); if (EEPROM.read(EEPROM_LAST_STATE_ADDRESS + i) != tmp) { EEPROM.write(EEPROM_LAST_STATE_ADDRESS + i, tmp); } } EEPROM.commit(); Serial.print("Startup behavior set to "); Serial.println(EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS)); } } // timing controller switch handling if (server.hasArg("tc")) { if (server.arg("tc") == "true") { if (tc_enabled == TIMING_CONTROL_DISABLED) { if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) != TIMING_CONTROL_ENABLED) { tc_enabled = TIMING_CONTROL_ENABLED; EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_ENABLED); EEPROM.commit(); Serial.print("Timing control = "); Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS)); tc_reset(); tc_update_main(); // call the main update function to read data and set the light states } } } else { // tc is set to false or something else if (tc_enabled == TIMING_CONTROL_ENABLED) { tc_enabled = TIMING_CONTROL_DISABLED; if (EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS) != TIMING_CONTROL_DISABLED) { EEPROM.write(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS, TIMING_CONTROL_DISABLED); EEPROM.commit(); Serial.print("Timing control = "); Serial.println(EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS)); for (uint8_t i = 0 ; i < LIGHTS_COUNT; i++) { // set the default transition time for all lights process_lightdata(i, default_transitiontime); } } } } } // scene switch handling if (server.hasArg("scene")) { scene = server.arg("scene").toInt(); if (EEPROM.read(EEPROM_SCENE_ADDRESS) != scene) { EEPROM.write(EEPROM_SCENE_ADDRESS, scene); EEPROM.commit(); Serial.print("Scene set to "); Serial.println(EEPROM.read(EEPROM_SCENE_ADDRESS)); } } if (server.hasArg("dip")) { uint8_t tmp = EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS); uint8_t tmp2 = (server.arg("dip") == "true" ? 1 : 0); if (tmp != tmp2) { EEPROM.write(EEPROM_DYNAMIC_IP_ADDRESS, tmp2); EEPROM.commit(); Serial.print("Set dynamic IP to "); Serial.println(EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS)); } } if (server.hasArg("ip")) { strip_ip.fromString(server.arg("ip")); // make it persistent to eeprom EEPROM.write(EEPROM_IP_ADDRESS, strip_ip[0]); EEPROM.write(EEPROM_IP_ADDRESS+1, strip_ip[1]); EEPROM.write(EEPROM_IP_ADDRESS+2, strip_ip[2]); EEPROM.write(EEPROM_IP_ADDRESS+3, strip_ip[3]); EEPROM.commit(); Serial.println("IP set to: " + (String)EEPROM.read(EEPROM_IP_ADDRESS) + "." + (String)EEPROM.read(EEPROM_IP_ADDRESS+1) + "." + (String)EEPROM.read(EEPROM_IP_ADDRESS+2) + "." + (String)EEPROM.read(EEPROM_IP_ADDRESS+3)); } if (server.hasArg("gwip")) { gateway_ip.fromString(server.arg("gwip")); // make it persistent to eeprom EEPROM.write(EEPROM_GW_ADDRESS, gateway_ip[0]); EEPROM.write(EEPROM_GW_ADDRESS+1, gateway_ip[1]); EEPROM.write(EEPROM_GW_ADDRESS+2, gateway_ip[2]); EEPROM.write(EEPROM_GW_ADDRESS+3, gateway_ip[3]); EEPROM.commit(); Serial.println("GW set to: " + (String)EEPROM.read(EEPROM_GW_ADDRESS) + "." + (String)EEPROM.read(EEPROM_GW_ADDRESS+1) + "." + (String)EEPROM.read(EEPROM_GW_ADDRESS+2) + "." + (String)EEPROM.read(EEPROM_GW_ADDRESS+3)); } if (server.hasArg("netmas")) { subnet_mask.fromString(server.arg("netmas")); // make it persistent to eeprom EEPROM.write(EEPROM_NM_ADDRESS, subnet_mask[0]); EEPROM.write(EEPROM_NM_ADDRESS+1, subnet_mask[1]); EEPROM.write(EEPROM_NM_ADDRESS+2, subnet_mask[2]); EEPROM.write(EEPROM_NM_ADDRESS+3, subnet_mask[3]); EEPROM.commit(); Serial.println("NetMask set to: " + (String)EEPROM.read(EEPROM_NM_ADDRESS) + "." + (String)EEPROM.read(EEPROM_NM_ADDRESS+1) + "." + (String)EEPROM.read(EEPROM_NM_ADDRESS+2) + "." + (String)EEPROM.read(EEPROM_NM_ADDRESS+3)); } if (server.hasArg("dns")) { dns.fromString(server.arg("dns")); // make it persistent to eeprom EEPROM.write(EEPROM_DNS_ADDRESS, dns[0]); EEPROM.write(EEPROM_DNS_ADDRESS+1, dns[1]); EEPROM.write(EEPROM_DNS_ADDRESS+2, dns[2]); EEPROM.write(EEPROM_DNS_ADDRESS+3, dns[3]); EEPROM.commit(); Serial.println("DNS set to: " + (String)EEPROM.read(EEPROM_DNS_ADDRESS) + "." + (String)EEPROM.read(EEPROM_DNS_ADDRESS+1) + "." + (String)EEPROM.read(EEPROM_DNS_ADDRESS+2) + "." + (String)EEPROM.read(EEPROM_DNS_ADDRESS+3)); } // process the received data for every light for (int light = 0; light < LIGHTS_COUNT; light++) { if (server.hasArg("bri" + (String)light)) { int tmp = (int)server.arg("bri" + (String)light).toInt(); if (tmp > 255) { tmp = 255; } else if (tmp < 0) { tmp = 0; } bri[light] = tmp; Serial.print("Brightness "); Serial.print(light); Serial.print(" set to "); Serial.println(bri[light]); } if (server.hasArg("on" + (String)light)) { uint8_t tmp = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); if (server.arg("on" + (String)light) == "true" && light_state[light] == false) { light_state[light] = true; if (tmp == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 0) { EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_ON); EEPROM.commit(); } Serial.print("Light "); Serial.print(light); Serial.print(" state set to "); Serial.println(light_state[light]); } else if (server.arg("on" + (String)light) == "false" && light_state[light] == true) { light_state[light] = false; bri[light] = 0; if (tmp == 0 && EEPROM.read(EEPROM_LAST_STATE_ADDRESS + light) == 1) { EEPROM.write(EEPROM_LAST_STATE_ADDRESS + light, LIGHT_STATE_OFF); EEPROM.commit(); } Serial.print("Light "); Serial.print(light); Serial.print(" state set to "); Serial.println(light_state[light]); } if (tc_enabled == TIMING_CONTROL_DISABLED) { process_lightdata(light, default_transitiontime); } } else { // light is off if (tc_enabled == TIMING_CONTROL_DISABLED) { process_lightdata(light, default_transitiontime); } } // start alerting for every light if (server.hasArg("alert")) { if (light_state[light]) { current_bri[light] = 0; } else { current_bri[light] = 255; } } } // process all lights if (server.hasArg("resettc")) { // reqrite the tc config and reboot tc_write_default(); ESP.reset(); } if (server.hasArg("reset")) { ESP.reset(); } // ***** Generate static HTML page ***** // String tmp1 = genHMTLTop(); String tmp2 = genLightControlHTML(); String tmp3 = getIndexHTMLMiddle(); String tmp4 = genConfigHTML(); String tmp5 = genHMTLBottom(); server.send(200, "text/html", tmp1 + tmp2 + tmp3 + tmp4 + tmp5); }); // -------------------- // server.on("/reset", []() { // trigger manual reset server.send(200, "text/html", "reset"); delay(1000); ESP.restart(); }); server.onNotFound(handleNotFound); } //********************************// String genHMTLTop() { return replacePlaceholder(getIndexHTMLTop()); } String genHMTLBottom() { return replacePlaceholder(getIndexHTMLBottom()); } String genConfigHTML() { // +++++ Generate config part of the page +++++ return replacePlaceholder(getConfigHTML()); } String genTCEditHTML() { return replacePlaceholder(getTCDataEditHTML()); } String genLightControlHTML() { String http_content = ""; // +++++ Generate lights part of the HTML page +++++ // Light control for (uint8 light_num = 0; light_num < LIGHTS_COUNT; light_num++) { // Generate lights part of the HTML page String tmp_light_content = getLightControlHTML(); // on/off buttons and slider tmp_light_content.replace("{{LIGHT_NUMBER}}", (String)(light_num + 1)); tmp_light_content.replace("{{LIGHT_NUMBER_DEC}}", (String)light_num); // add the lights code to the html output string http_content += tmp_light_content; } return http_content; } //********************************// String replacePlaceholder(String http_content) { http_content.replace("{{VERSION_STR}}", (String)VERSION_STR); http_content.replace("{{LIGHT_NAME}}", (String)LIGHT_NAME); #ifdef DEVELOPMENT http_content.replace("{{LIGHT_NAME_DEV_POSTFIX}}", (String)LIGHT_NAME_DEV_POSTFIX); #else http_content.replace("{{LIGHT_NAME_DEV_POSTFIX}}", (String)" "); #endif http_content.replace("{{LIGHT_COUNT}}", (String)LIGHTS_COUNT); int tc_val = EEPROM.read(EEPROM_TIMING_CONTROL_ENABLED_ADDRESS); if (tc_val == TIMING_CONTROL_ENABLED) { http_content.replace("{{TC_LINK_PRIMARY_ON}}", "pure-button-primary"); } else { http_content.replace("{{TC_LINK_PRIMARY_ON}}", ""); } if (tc_val == TIMING_CONTROL_DISABLED) { http_content.replace("{{TC_LINK_PRIMARY_OFF}}", "pure-button-primary"); } else { http_content.replace("{{TC_LINK_PRIMARY_OFF}}", ""); } http_content.replace("{{TRANSITION_TIME}}", (String)default_transitiontime); int ls_val = EEPROM.read(EEPROM_LAST_STATE_STARTUP_ADDRESS); if (ls_val == LAST_STATE_STARTUP_LIGHT_LAST_STATE) { http_content.replace("{{STARTUP_SELECTED_LS_0}}", "selected=\"selected\""); } else { http_content.replace("{{STARTUP_SELECTED_LS_0}}", ""); } if (ls_val == LAST_STATE_STARTUP_LIGHT_ON_STATE) { http_content.replace("{{STARTUP_SELECTED_ON_1}}", "selected=\"selected\""); } else { http_content.replace("{{STARTUP_SELECTED_ON_1}}", ""); } if (ls_val == LAST_STATE_STARTUP_LIGHT_OFF_STATE) { http_content.replace("{{STARTUP_SELECTED_OFF_2}}", "selected=\"selected\""); } else { http_content.replace("{{STARTUP_SELECTED_OFF_2}}", ""); } // scene int sc_val = EEPROM.read(EEPROM_SCENE_ADDRESS); if (sc_val == SCENE_RELEAX) { http_content.replace("{{SCENE_SELECTED_RELAX_0}}", "selected=\"selected\""); } else { http_content.replace("{{SCENE_SELECTED_RELAX_0}}", ""); } if (sc_val == SCENE_BRIGHT) { http_content.replace("{{SCENE_SELECTED_BRIGHT_1}}", "selected=\"selected\""); } else { http_content.replace("{{SCENE_SELECTED_BRIGHT_1}}", ""); } if (sc_val == SCENE_NIGHTLY) { http_content.replace("{{SCENE_SELECTED_NIGHT_2}}", "selected=\"selected\""); } else { http_content.replace("{{SCENE_SELECTED_NIGHT_2}}", ""); } // Wifi settings http_content.replace("{{WIFI_SSID}}", WiFi.SSID()); // Network settings uint8_t dip = EEPROM.read(EEPROM_DYNAMIC_IP_ADDRESS); if (dip) { http_content.replace("{{DIP_LINK_ON_PRIMARY}}", "pure-button-primary"); http_content.replace("{{DIP_LINK_OFF_PRIMARY}}", ""); } else { http_content.replace("{{DIP_LINK_OFF_PRIMARY}}", "pure-button-primary"); http_content.replace("{{DIP_LINK_ON_PRIMARY}}", ""); } // network config http_content.replace("{{WIFI_CFG_IP}}", WiFi.localIP().toString()); http_content.replace("{{WIFI_CFG_GW}}", WiFi.gatewayIP().toString()); http_content.replace("{{WIFI_CFG_NM}}", WiFi.subnetMask().toString()); http_content.replace("{{WIFI_CFG_DNS}}", WiFi.dnsIP().toString()); // add the current ip address to the page http_content.replace("{{IP_ADDRESS}}", WiFi.localIP().toString()); // set the pwm values http_content.replace("{{PWM_MIN}}", (String)PWM_MIN); http_content.replace("{{PWM_MAX}}", (String)PWM_MAX); return http_content; } //********************************// String loadSPIFFSFile(String fname) { File file = SPIFFS.open(fname, "r"); if (!file) { Serial.println("Failed to open file " + fname); return ""; } String contents = file.readString(); file.close(); return contents; } //********************************// String getIndexHTMLTop() { // load file return loadSPIFFSFile("/index_template_top.html"); } String getIndexHTMLMiddle() { // load file return loadSPIFFSFile("/index_template_middle.html"); } String getIndexHTMLBottom() { // load file return loadSPIFFSFile("/index_template_bottom.html"); } //********************************// String getConfigHTML() { // load file return loadSPIFFSFile("/config_template.html"); } //********************************// String getTCDataEditHTML() { // load file return loadSPIFFSFile("/tc_data_edit.html"); } //********************************// String getLightControlHTML() { // load file return loadSPIFFSFile("/light_control_template.html"); } //********************************//