"));
+ stream->print(title.c_str());
+
+ // KAUF edit
+ // print links to kaufha and esphome, list version numbers
+ stream->print(F(" "));
+
+ stream->print(F("KAUF Plug by Kaufman Home Automation
"));
+ stream->print(F("Firmware made using ESPHome
"));
+ stream->print(F("KAUF Plug firmware version 1.5, ESPHome version 2021.08.0
"));
+
+
+
+ stream->print(F("States Name State Actions "));
+ // KAUF edit end
+
+ // All content is controlled and created by user - so allowing all origins is fine here.
+ stream->addHeader("Access-Control-Allow-Origin", "*");
+
+#ifdef USE_SENSOR
+ for (auto *obj : App.get_sensors())
+ write_row(stream, obj, "sensor", "");
+#endif
+
+#ifdef USE_SWITCH
+ for (auto *obj : App.get_switches())
+ write_row(stream, obj, "switch", "Toggle ");
+#endif
+
+#ifdef USE_BINARY_SENSOR
+ for (auto *obj : App.get_binary_sensors())
+ write_row(stream, obj, "binary_sensor", "");
+#endif
+
+#ifdef USE_FAN
+ for (auto *obj : App.get_fans())
+ write_row(stream, obj, "fan", "Toggle ");
+#endif
+
+#ifdef USE_LIGHT
+ for (auto *obj : App.get_lights())
+ write_row(stream, obj, "light", "Toggle ");
+#endif
+
+#ifdef USE_TEXT_SENSOR
+ for (auto *obj : App.get_text_sensors())
+ write_row(stream, obj, "text_sensor", "");
+#endif
+
+#ifdef USE_COVER
+ for (auto *obj : App.get_covers())
+ write_row(stream, obj, "cover", "Open Close ");
+#endif
+
+#ifdef USE_NUMBER
+ for (auto *obj : App.get_numbers())
+ write_row(stream, obj, "number", "");
+#endif
+
+#ifdef USE_SELECT
+ for (auto *obj : App.get_selects())
+ write_row(stream, obj, "select", "");
+#endif
+
+ // KAUF edit
+ // warn not to load tasmota-minimal
+ stream->print(F("
See ESPHome Web API for "
+ "REST API documentation.
"
+ "OTA Update "
+ " **** DO NOT USE TASMOTA-MINIMAL .BIN or .BIN.GZ. **** Use tasmota.bin.gz."
+ ""
+ "Debug Log "));
+ // KAUF edit end
+
+#ifdef WEBSERVER_JS_INCLUDE
+ if (this->js_include_ != nullptr) {
+ stream->print(F(""));
+ }
+#endif
+ if (strlen(this->js_url_) > 0) {
+ stream->print(F(""));
+ }
+ stream->print(F(" "));
+
+ request->send(stream);
+}
+
+#ifdef WEBSERVER_CSS_INCLUDE
+void WebServer::handle_css_request(AsyncWebServerRequest *request) {
+ AsyncResponseStream *stream = request->beginResponseStream("text/css");
+ if (this->css_include_ != nullptr) {
+ stream->print(this->css_include_);
+ }
+
+ request->send(stream);
+}
+#endif
+
+#ifdef WEBSERVER_JS_INCLUDE
+void WebServer::handle_js_request(AsyncWebServerRequest *request) {
+ AsyncResponseStream *stream = request->beginResponseStream("text/javascript");
+ if (this->js_include_ != nullptr) {
+ stream->print(this->js_include_);
+ }
+
+ request->send(stream);
+}
+#endif
+
+#ifdef USE_SENSOR
+void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
+ this->events_.send(this->sensor_json(obj, state).c_str(), "state");
+}
+void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
+ for (sensor::Sensor *obj : App.get_sensors()) {
+ if (obj->is_internal())
+ continue;
+ if (obj->get_object_id() != match.id)
+ continue;
+ std::string data = this->sensor_json(obj, obj->state);
+ request->send(200, "text/json", data.c_str());
+ return;
+ }
+ request->send(404);
+}
+std::string WebServer::sensor_json(sensor::Sensor *obj, float value) {
+ return json::build_json([obj, value](JsonObject &root) {
+ root["id"] = "sensor-" + obj->get_object_id();
+ std::string state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
+ if (!obj->get_unit_of_measurement().empty())
+ state += " " + obj->get_unit_of_measurement();
+ root["state"] = state;
+ root["value"] = value;
+ });
+}
+#endif
+
+#ifdef USE_TEXT_SENSOR
+void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
+ this->events_.send(this->text_sensor_json(obj, state).c_str(), "state");
+}
+void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
+ for (text_sensor::TextSensor *obj : App.get_text_sensors()) {
+ if (obj->is_internal())
+ continue;
+ if (obj->get_object_id() != match.id)
+ continue;
+ std::string data = this->text_sensor_json(obj, obj->state);
+ request->send(200, "text/json", data.c_str());
+ return;
+ }
+ request->send(404);
+}
+std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value) {
+ return json::build_json([obj, value](JsonObject &root) {
+ root["id"] = "text_sensor-" + obj->get_object_id();
+ root["state"] = value;
+ root["value"] = value;
+ });
+}
+#endif
+
+#ifdef USE_SWITCH
+void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
+ this->events_.send(this->switch_json(obj, state).c_str(), "state");
+}
+std::string WebServer::switch_json(switch_::Switch *obj, bool value) {
+ return json::build_json([obj, value](JsonObject &root) {
+ root["id"] = "switch-" + obj->get_object_id();
+ root["state"] = value ? "ON" : "OFF";
+ root["value"] = value;
+ });
+}
+void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
+ for (switch_::Switch *obj : App.get_switches()) {
+ if (obj->is_internal())
+ continue;
+ if (obj->get_object_id() != match.id)
+ continue;
+
+ if (request->method() == HTTP_GET) {
+ std::string data = this->switch_json(obj, obj->state);
+ request->send(200, "text/json", data.c_str());
+ } else if (match.method == "toggle") {
+ this->defer([obj]() { obj->toggle(); });
+ request->send(200);
+ } else if (match.method == "turn_on") {
+ this->defer([obj]() { obj->turn_on(); });
+ request->send(200);
+ } else if (match.method == "turn_off") {
+ this->defer([obj]() { obj->turn_off(); });
+ request->send(200);
+ } else {
+ request->send(404);
+ }
+ return;
+ }
+ request->send(404);
+}
+#endif
+
+#ifdef USE_BINARY_SENSOR
+void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
+ if (obj->is_internal())
+ return;
+ this->events_.send(this->binary_sensor_json(obj, state).c_str(), "state");
+}
+std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value) {
+ return json::build_json([obj, value](JsonObject &root) {
+ root["id"] = "binary_sensor-" + obj->get_object_id();
+ root["state"] = value ? "ON" : "OFF";
+ root["value"] = value;
+ });
+}
+void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
+ for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
+ if (obj->is_internal())
+ continue;
+ if (obj->get_object_id() != match.id)
+ continue;
+ std::string data = this->binary_sensor_json(obj, obj->state);
+ request->send(200, "text/json", data.c_str());
+ return;
+ }
+ request->send(404);
+}
+#endif
+
+#ifdef USE_FAN
+void WebServer::on_fan_update(fan::FanState *obj) {
+ if (obj->is_internal())
+ return;
+ this->events_.send(this->fan_json(obj).c_str(), "state");
+}
+std::string WebServer::fan_json(fan::FanState *obj) {
+ return json::build_json([obj](JsonObject &root) {
+ root["id"] = "fan-" + obj->get_object_id();
+ root["state"] = obj->state ? "ON" : "OFF";
+ root["value"] = obj->state;
+ const auto traits = obj->get_traits();
+ if (traits.supports_speed()) {
+ root["speed_level"] = obj->speed;
+ switch (fan::speed_level_to_enum(obj->speed, traits.supported_speed_count())) {
+ case fan::FAN_SPEED_LOW:
+ root["speed"] = "low";
+ break;
+ case fan::FAN_SPEED_MEDIUM:
+ root["speed"] = "medium";
+ break;
+ case fan::FAN_SPEED_HIGH:
+ root["speed"] = "high";
+ break;
+ }
+ }
+ if (obj->get_traits().supports_oscillation())
+ root["oscillation"] = obj->oscillating;
+ });
+}
+void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
+ for (fan::FanState *obj : App.get_fans()) {
+ if (obj->is_internal())
+ continue;
+ if (obj->get_object_id() != match.id)
+ continue;
+
+ if (request->method() == HTTP_GET) {
+ std::string data = this->fan_json(obj);
+ request->send(200, "text/json", data.c_str());
+ } else if (match.method == "toggle") {
+ this->defer([obj]() { obj->toggle().perform(); });
+ request->send(200);
+ } else if (match.method == "turn_on") {
+ auto call = obj->turn_on();
+ if (request->hasParam("speed")) {
+ String speed = request->getParam("speed")->value();
+ call.set_speed(speed.c_str());
+ }
+ if (request->hasParam("speed_level")) {
+ String speed_level = request->getParam("speed_level")->value();
+ auto val = parse_int(speed_level.c_str());
+ if (!val.has_value()) {
+ ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
+ return;
+ }
+ call.set_speed(*val);
+ }
+ if (request->hasParam("oscillation")) {
+ String speed = request->getParam("oscillation")->value();
+ auto val = parse_on_off(speed.c_str());
+ switch (val) {
+ case PARSE_ON:
+ call.set_oscillating(true);
+ break;
+ case PARSE_OFF:
+ call.set_oscillating(false);
+ break;
+ case PARSE_TOGGLE:
+ call.set_oscillating(!obj->oscillating);
+ break;
+ case PARSE_NONE:
+ request->send(404);
+ return;
+ }
+ }
+ this->defer([call]() { call.perform(); });
+ request->send(200);
+ } else if (match.method == "turn_off") {
+ this->defer([obj]() { obj->turn_off().perform(); });
+ request->send(200);
+ } else {
+ request->send(404);
+ }
+ return;
+ }
+ request->send(404);
+}
+#endif
+
+#ifdef USE_LIGHT
+void WebServer::on_light_update(light::LightState *obj) {
+ if (obj->is_internal())
+ return;
+ this->events_.send(this->light_json(obj).c_str(), "state");
+}
+void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
+ for (light::LightState *obj : App.get_lights()) {
+ if (obj->is_internal())
+ continue;
+ if (obj->get_object_id() != match.id)
+ continue;
+
+ if (request->method() == HTTP_GET) {
+ std::string data = this->light_json(obj);
+ request->send(200, "text/json", data.c_str());
+ } else if (match.method == "toggle") {
+ this->defer([obj]() { obj->toggle().perform(); });
+ request->send(200);
+ } else if (match.method == "turn_on") {
+ auto call = obj->turn_on();
+ if (request->hasParam("brightness"))
+ call.set_brightness(request->getParam("brightness")->value().toFloat() / 255.0f);
+ if (request->hasParam("r"))
+ call.set_red(request->getParam("r")->value().toFloat() / 255.0f);
+ if (request->hasParam("g"))
+ call.set_green(request->getParam("g")->value().toFloat() / 255.0f);
+ if (request->hasParam("b"))
+ call.set_blue(request->getParam("b")->value().toFloat() / 255.0f);
+ if (request->hasParam("white_value"))
+ call.set_white(request->getParam("white_value")->value().toFloat() / 255.0f);
+ if (request->hasParam("color_temp"))
+ call.set_color_temperature(request->getParam("color_temp")->value().toFloat());
+
+ if (request->hasParam("flash")) {
+ float length_s = request->getParam("flash")->value().toFloat();
+ call.set_flash_length(static_cast