#include "webserver.h" // system includes #include #include // esp-idf includes #include // 3rdparty lib includes #include // local includes #include "myconfig.h" #include "feature_lamp.h" #include "feature_switch.h" #include "feature_dht.h" #include "feature_tsl.h" #include "feature_bmp.h" #include "mymqtt.h" #include "espwifistack.h" namespace deckenlampe { httpd_handle_t httpdHandle; namespace { constexpr const char * const TAG = "WEBSERVER"; template T htmlentities(const T &val) { return val; } // TODO template T htmlentities(T &&val) { return val; } // TODO esp_err_t webserver_root_handler(httpd_req_t *req); esp_err_t webserver_on_handler(httpd_req_t *req); esp_err_t webserver_off_handler(httpd_req_t *req); esp_err_t webserver_toggle_handler(httpd_req_t *req); esp_err_t webserver_reboot_handler(httpd_req_t *req); } // namespace void init_webserver() { if (!config::enable_webserver) return; { httpd_config_t httpConfig HTTPD_DEFAULT_CONFIG(); const auto result = httpd_start(&httpdHandle, &httpConfig); ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_start(): %s", esp_err_to_name(result)); if (result != ESP_OK) return; } for (const httpd_uri_t &uri : { httpd_uri_t { .uri = "/", .method = HTTP_GET, .handler = webserver_root_handler, .user_ctx = NULL }, httpd_uri_t { .uri = "/on", .method = HTTP_GET, .handler = webserver_on_handler, .user_ctx = NULL }, httpd_uri_t { .uri = "/off", .method = HTTP_GET, .handler = webserver_off_handler, .user_ctx = NULL }, httpd_uri_t { .uri = "/toggle", .method = HTTP_GET, .handler = webserver_toggle_handler, .user_ctx = NULL }, httpd_uri_t { .uri = "/reboot", .method = HTTP_GET, .handler = webserver_reboot_handler, .user_ctx = NULL } }) { const auto result = httpd_register_uri_handler(httpdHandle, &uri); ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "httpd_register_uri_handler() for %s: %s", uri.uri, esp_err_to_name(result)); //if (result != ESP_OK) // return result; } } void update_webserver() { if (!config::enable_webserver) return; } namespace { #define CALL_AND_EXIT_ON_ERROR(METHOD, ...) \ if (const auto result = METHOD(__VA_ARGS__); result != ESP_OK) \ { \ ESP_LOGE(TAG, "%s() failed with %s", #METHOD, esp_err_to_name(result)); \ return result; \ } #define CALL_AND_EXIT(METHOD, ...) \ if (const auto result = METHOD(__VA_ARGS__); result != ESP_OK) \ { \ ESP_LOGE(TAG, "%s() failed with %s", #METHOD, esp_err_to_name(result)); \ return result; \ } \ else \ return result; esp_err_t webserver_root_handler(httpd_req_t *req) { std::string body = "this is work in progress...
\n"; if (config::enable_lamp) { body += "on
\n" "off
\n" "toggle
\n"; } body += "reboot
\n" "
\n"; if (config::enable_lamp) body += fmt::format("Lamp: {}
\n", lampState ? "ON" : "OFF"); if (config::enable_switch) body += fmt::format("Switch: {}
\n", switchState ? "ON" : "OFF"); if (config::enable_dht) { if (lastDhtValue) { body += fmt::format("DHT11 Temperature: {:.1f} C
\n", lastDhtValue->temperature); body += fmt::format("DHT11 Humidity: {:.1f} %
\n", lastDhtValue->humidity); } else body += "DHT11 not available at the moment
\n"; } if (config::enable_i2c && config::enable_tsl) { if (lastTslValue) { body += fmt::format("TSL2561 Brightness: {:.1f} lux
\n", lastTslValue->lux); } else body += "TSL2561 not available at the moment
\n"; } if (config::enable_i2c && config::enable_bmp) { if (lastBmpValue) { body += fmt::format("BMP085 Pressure: {:.1f} lux
\n", lastBmpValue->pressure); body += fmt::format("BMP085 Temperature: {:.1f} C
\n", lastBmpValue->temperature); body += fmt::format("BMP085 Altitude: {:.1f} m
\n", lastBmpValue->altitude); } else body += "BMP085 not available at the moment
\n"; } body += "
\n" "

wifi

\n" "\n"; body += fmt::format("\n", wifi_stack::toString(wifi_stack::wifiStateMachineState)); { const auto staStatus = wifi_stack::get_sta_status(); body += fmt::format("\n", wifi_stack::toString(staStatus)); if (staStatus == wifi_stack::WiFiStaStatus::WL_CONNECTED) { if (const auto result = wifi_stack::get_sta_ap_info(); result) { body += fmt::format("\n", result->rssi); body += fmt::format("\n", htmlentities(result->ssid)); body += fmt::format("\n", result->primary); body += fmt::format("\n", wifi_stack::toString(wifi_stack::mac_t{result->bssid})); } else { body += fmt::format("\n", htmlentities(result.error())); } if (const auto result = wifi_stack::get_ip_info(TCPIP_ADAPTER_IF_STA)) { body += fmt::format("\n", wifi_stack::toString(result->ip)); body += fmt::format("\n", wifi_stack::toString(result->netmask)); body += fmt::format("\n", wifi_stack::toString(result->gw)); } else { body += fmt::format("\n", htmlentities(result.error())); } } } body += fmt::format("\n", wifi_stack::toString(wifi_stack::get_scan_status())); body += "
state machine state{}
STA status{}
STA rssi{}dB
STA SSID{}
STA channel{}
STA BSSID{}
get_sta_ap_info() failed: {}
STA ip{}
STA netmask{}
STA gw{}
get_ip_info() failed: {}
scan status{}
\n" "

scan result

\n"; if (const auto &scanResult = wifi_stack::get_scan_result()) { body += fmt::format("scan age: {}s
\n", std::chrono::round(espchrono::ago(scanResult->finished)).count()); body += "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n"; for (const auto &entry : scanResult->entries) { body += fmt::format( "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n", htmlentities(entry.ssid), wifi_stack::toString(entry.authmode), wifi_stack::toString(entry.pairwise_cipher), wifi_stack::toString(entry.group_cipher), entry.rssi, entry.primary, wifi_stack::toString(wifi_stack::mac_t{entry.bssid})); } body += "\n" "
ssidauthmodepairwise_ciphergroup_cipherrssichannelbssid
{}{}{}{}{}{}{}
\n"; } else { body += "no wifi scan result at the moment!
\n"; } body += "
\n" "

MQTT

\n" "\n"; body += fmt::format("\n", htmlentities(config::broker_url)); body += fmt::format("\n", mqttClient ? "true" : "false"); if (mqttClient) { body += fmt::format("\n", mqttStarted ? "true" : "false"); body += fmt::format("\n", mqttConnected ? "true" : "false"); } body += "
client url{}
client constructed{}
client started{}
client connected{}
\n"; CALL_AND_EXIT_ON_ERROR(httpd_resp_set_type, req, "text/html") CALL_AND_EXIT_ON_ERROR(httpd_resp_send, req, body.data(), body.size()) return ESP_OK; } esp_err_t webserver_on_handler(httpd_req_t *req) { if (!config::enable_lamp) { ESP_LOGW(TAG, "lamp support not enabled!"); CALL_AND_EXIT_ON_ERROR(httpd_resp_send_err, req, HTTPD_400_BAD_REQUEST, "lamp support not enabled!") } const bool state = (lampState = true); writeLamp(state); if (mqttConnected) mqttVerbosePub(config::topic_lamp_status, state ? "ON" : "OFF", 0, 1); std::string_view body{"ON called..."}; CALL_AND_EXIT_ON_ERROR(httpd_resp_set_type, req, "text/html") CALL_AND_EXIT_ON_ERROR(httpd_resp_send, req, body.data(), body.size()) return ESP_OK; } esp_err_t webserver_off_handler(httpd_req_t *req) { if (!config::enable_lamp) { ESP_LOGW(TAG, "lamp support not enabled!"); CALL_AND_EXIT_ON_ERROR(httpd_resp_send_err, req, HTTPD_400_BAD_REQUEST, "lamp support not enabled!") } const bool state = (lampState = false); writeLamp(state); if (mqttConnected) mqttVerbosePub(config::topic_lamp_status, state ? "ON" : "OFF", 0, 1); std::string_view body{"OFF called..."}; CALL_AND_EXIT_ON_ERROR(httpd_resp_set_type, req, "text/html") CALL_AND_EXIT_ON_ERROR(httpd_resp_send, req, body.data(), body.size()) return ESP_OK; } esp_err_t webserver_toggle_handler(httpd_req_t *req) { if (!config::enable_lamp) { ESP_LOGW(TAG, "lamp support not enabled!"); CALL_AND_EXIT_ON_ERROR(httpd_resp_send_err, req, HTTPD_400_BAD_REQUEST, "lamp support not enabled!") } const bool state = (lampState = !lampState); writeLamp(state); if (mqttConnected) mqttVerbosePub(config::topic_lamp_status, state ? "ON" : "OFF", 0, 1); std::string_view body{"TOGGLE called..."}; CALL_AND_EXIT_ON_ERROR(httpd_resp_set_type, req, "text/html") CALL_AND_EXIT_ON_ERROR(httpd_resp_send, req, body.data(), body.size()) return ESP_OK; } esp_err_t webserver_reboot_handler(httpd_req_t *req) { std::string_view body{"REBOOT called..."}; CALL_AND_EXIT_ON_ERROR(httpd_resp_set_type, req, "text/html") CALL_AND_EXIT_ON_ERROR(httpd_resp_send, req, body.data(), body.size()) esp_restart(); return ESP_OK; } } // namespace } // namespace deckenlampe