mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	[http_request] Pass trigger variables into on_response/on_error (#11464)
This commit is contained in:
		| @@ -12,7 +12,6 @@ from esphome.const import ( | |||||||
|     CONF_ON_ERROR, |     CONF_ON_ERROR, | ||||||
|     CONF_ON_RESPONSE, |     CONF_ON_RESPONSE, | ||||||
|     CONF_TIMEOUT, |     CONF_TIMEOUT, | ||||||
|     CONF_TRIGGER_ID, |  | ||||||
|     CONF_URL, |     CONF_URL, | ||||||
|     CONF_WATCHDOG_TIMEOUT, |     CONF_WATCHDOG_TIMEOUT, | ||||||
|     PLATFORM_HOST, |     PLATFORM_HOST, | ||||||
| @@ -216,16 +215,8 @@ HTTP_REQUEST_ACTION_SCHEMA = cv.Schema( | |||||||
|             f"{CONF_VERIFY_SSL} has moved to the base component configuration." |             f"{CONF_VERIFY_SSL} has moved to the base component configuration." | ||||||
|         ), |         ), | ||||||
|         cv.Optional(CONF_CAPTURE_RESPONSE, default=False): cv.boolean, |         cv.Optional(CONF_CAPTURE_RESPONSE, default=False): cv.boolean, | ||||||
|         cv.Optional(CONF_ON_RESPONSE): automation.validate_automation( |         cv.Optional(CONF_ON_RESPONSE): automation.validate_automation(single=True), | ||||||
|             {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(HttpRequestResponseTrigger)} |         cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True), | ||||||
|         ), |  | ||||||
|         cv.Optional(CONF_ON_ERROR): automation.validate_automation( |  | ||||||
|             { |  | ||||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( |  | ||||||
|                     automation.Trigger.template() |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|         ), |  | ||||||
|         cv.Optional(CONF_MAX_RESPONSE_BUFFER_SIZE, default="1kB"): cv.validate_bytes, |         cv.Optional(CONF_MAX_RESPONSE_BUFFER_SIZE, default="1kB"): cv.validate_bytes, | ||||||
|     } |     } | ||||||
| ) | ) | ||||||
| @@ -280,7 +271,12 @@ async def http_request_action_to_code(config, action_id, template_arg, args): | |||||||
|     template_ = await cg.templatable(config[CONF_URL], args, cg.std_string) |     template_ = await cg.templatable(config[CONF_URL], args, cg.std_string) | ||||||
|     cg.add(var.set_url(template_)) |     cg.add(var.set_url(template_)) | ||||||
|     cg.add(var.set_method(config[CONF_METHOD])) |     cg.add(var.set_method(config[CONF_METHOD])) | ||||||
|     cg.add(var.set_capture_response(config[CONF_CAPTURE_RESPONSE])) |  | ||||||
|  |     capture_response = config[CONF_CAPTURE_RESPONSE] | ||||||
|  |     if capture_response: | ||||||
|  |         cg.add(var.set_capture_response(capture_response)) | ||||||
|  |         cg.add_define("USE_HTTP_REQUEST_RESPONSE") | ||||||
|  |  | ||||||
|     cg.add(var.set_max_response_buffer_size(config[CONF_MAX_RESPONSE_BUFFER_SIZE])) |     cg.add(var.set_max_response_buffer_size(config[CONF_MAX_RESPONSE_BUFFER_SIZE])) | ||||||
|  |  | ||||||
|     if CONF_BODY in config: |     if CONF_BODY in config: | ||||||
| @@ -303,21 +299,26 @@ async def http_request_action_to_code(config, action_id, template_arg, args): | |||||||
|     for value in config.get(CONF_COLLECT_HEADERS, []): |     for value in config.get(CONF_COLLECT_HEADERS, []): | ||||||
|         cg.add(var.add_collect_header(value)) |         cg.add(var.add_collect_header(value)) | ||||||
|  |  | ||||||
|     for conf in config.get(CONF_ON_RESPONSE, []): |     if response_conf := config.get(CONF_ON_RESPONSE): | ||||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) |         if capture_response: | ||||||
|         cg.add(var.register_response_trigger(trigger)) |             await automation.build_automation( | ||||||
|         await automation.build_automation( |                 var.get_success_trigger_with_response(), | ||||||
|             trigger, |                 [ | ||||||
|             [ |                     (cg.std_shared_ptr.template(HttpContainer), "response"), | ||||||
|                 (cg.std_shared_ptr.template(HttpContainer), "response"), |                     (cg.std_string_ref, "body"), | ||||||
|                 (cg.std_string_ref, "body"), |                     *args, | ||||||
|             ], |                 ], | ||||||
|             conf, |                 response_conf, | ||||||
|         ) |             ) | ||||||
|     for conf in config.get(CONF_ON_ERROR, []): |         else: | ||||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) |             await automation.build_automation( | ||||||
|         cg.add(var.register_error_trigger(trigger)) |                 var.get_success_trigger(), | ||||||
|         await automation.build_automation(trigger, [], conf) |                 [(cg.std_shared_ptr.template(HttpContainer), "response"), *args], | ||||||
|  |                 response_conf, | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |     if error_conf := config.get(CONF_ON_ERROR): | ||||||
|  |         await automation.build_automation(var.get_error_trigger(), args, error_conf) | ||||||
|  |  | ||||||
|     return var |     return var | ||||||
|  |  | ||||||
|   | |||||||
| @@ -183,7 +183,9 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> { | |||||||
|   TEMPLATABLE_VALUE(std::string, url) |   TEMPLATABLE_VALUE(std::string, url) | ||||||
|   TEMPLATABLE_VALUE(const char *, method) |   TEMPLATABLE_VALUE(const char *, method) | ||||||
|   TEMPLATABLE_VALUE(std::string, body) |   TEMPLATABLE_VALUE(std::string, body) | ||||||
|  | #ifdef USE_HTTP_REQUEST_RESPONSE | ||||||
|   TEMPLATABLE_VALUE(bool, capture_response) |   TEMPLATABLE_VALUE(bool, capture_response) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   void add_request_header(const char *key, TemplatableValue<const char *, Ts...> value) { |   void add_request_header(const char *key, TemplatableValue<const char *, Ts...> value) { | ||||||
|     this->request_headers_.insert({key, value}); |     this->request_headers_.insert({key, value}); | ||||||
| @@ -195,9 +197,14 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> { | |||||||
|  |  | ||||||
|   void set_json(std::function<void(Ts..., JsonObject)> json_func) { this->json_func_ = json_func; } |   void set_json(std::function<void(Ts..., JsonObject)> json_func) { this->json_func_ = json_func; } | ||||||
|  |  | ||||||
|   void register_response_trigger(HttpRequestResponseTrigger *trigger) { this->response_triggers_.push_back(trigger); } | #ifdef USE_HTTP_REQUEST_RESPONSE | ||||||
|  |   Trigger<std::shared_ptr<HttpContainer>, std::string &, Ts...> *get_success_trigger_with_response() const { | ||||||
|  |     return this->success_trigger_with_response_; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   Trigger<std::shared_ptr<HttpContainer>, Ts...> *get_success_trigger() const { return this->success_trigger_; } | ||||||
|  |  | ||||||
|   void register_error_trigger(Trigger<> *trigger) { this->error_triggers_.push_back(trigger); } |   Trigger<Ts...> *get_error_trigger() const { return this->error_trigger_; } | ||||||
|  |  | ||||||
|   void set_max_response_buffer_size(size_t max_response_buffer_size) { |   void set_max_response_buffer_size(size_t max_response_buffer_size) { | ||||||
|     this->max_response_buffer_size_ = max_response_buffer_size; |     this->max_response_buffer_size_ = max_response_buffer_size; | ||||||
| @@ -228,17 +235,20 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> { | |||||||
|     auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers, |     auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers, | ||||||
|                                           this->collect_headers_); |                                           this->collect_headers_); | ||||||
|  |  | ||||||
|  |     auto captured_args = std::make_tuple(x...); | ||||||
|  |  | ||||||
|     if (container == nullptr) { |     if (container == nullptr) { | ||||||
|       for (auto *trigger : this->error_triggers_) |       std::apply([this](Ts... captured_args_inner) { this->error_trigger_->trigger(captured_args_inner...); }, | ||||||
|         trigger->trigger(); |                  captured_args); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     size_t content_length = container->content_length; |     size_t content_length = container->content_length; | ||||||
|     size_t max_length = std::min(content_length, this->max_response_buffer_size_); |     size_t max_length = std::min(content_length, this->max_response_buffer_size_); | ||||||
|  |  | ||||||
|     std::string response_body; | #ifdef USE_HTTP_REQUEST_RESPONSE | ||||||
|     if (this->capture_response_.value(x...)) { |     if (this->capture_response_.value(x...)) { | ||||||
|  |       std::string response_body; | ||||||
|       RAMAllocator<uint8_t> allocator; |       RAMAllocator<uint8_t> allocator; | ||||||
|       uint8_t *buf = allocator.allocate(max_length); |       uint8_t *buf = allocator.allocate(max_length); | ||||||
|       if (buf != nullptr) { |       if (buf != nullptr) { | ||||||
| @@ -253,19 +263,17 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> { | |||||||
|         response_body.assign((char *) buf, read_index); |         response_body.assign((char *) buf, read_index); | ||||||
|         allocator.deallocate(buf, max_length); |         allocator.deallocate(buf, max_length); | ||||||
|       } |       } | ||||||
|     } |       std::apply( | ||||||
|  |           [this, &container, &response_body](Ts... captured_args_inner) { | ||||||
|     if (this->response_triggers_.size() == 1) { |             this->success_trigger_with_response_->trigger(container, response_body, captured_args_inner...); | ||||||
|       // if there is only one trigger, no need to copy the response body |           }, | ||||||
|       this->response_triggers_[0]->process(container, response_body); |           captured_args); | ||||||
|     } else { |     } else | ||||||
|       for (auto *trigger : this->response_triggers_) { | #endif | ||||||
|         // with multiple triggers, pass a copy of the response body to each |     { | ||||||
|         // one so that modifications made in one trigger are not visible to |       std::apply([this, &container]( | ||||||
|         // the others |                      Ts... captured_args_inner) { this->success_trigger_->trigger(container, captured_args_inner...); }, | ||||||
|         auto response_body_copy = std::string(response_body); |                  captured_args); | ||||||
|         trigger->process(container, response_body_copy); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|     container->end(); |     container->end(); | ||||||
|   } |   } | ||||||
| @@ -283,8 +291,13 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> { | |||||||
|   std::set<std::string> collect_headers_{"content-type", "content-length"}; |   std::set<std::string> collect_headers_{"content-type", "content-length"}; | ||||||
|   std::map<const char *, TemplatableValue<std::string, Ts...>> json_{}; |   std::map<const char *, TemplatableValue<std::string, Ts...>> json_{}; | ||||||
|   std::function<void(Ts..., JsonObject)> json_func_{nullptr}; |   std::function<void(Ts..., JsonObject)> json_func_{nullptr}; | ||||||
|   std::vector<HttpRequestResponseTrigger *> response_triggers_{}; | #ifdef USE_HTTP_REQUEST_RESPONSE | ||||||
|   std::vector<Trigger<> *> error_triggers_{}; |   Trigger<std::shared_ptr<HttpContainer>, std::string &, Ts...> *success_trigger_with_response_ = | ||||||
|  |       new Trigger<std::shared_ptr<HttpContainer>, std::string &, Ts...>(); | ||||||
|  | #endif | ||||||
|  |   Trigger<std::shared_ptr<HttpContainer>, Ts...> *success_trigger_ = | ||||||
|  |       new Trigger<std::shared_ptr<HttpContainer>, Ts...>(); | ||||||
|  |   Trigger<Ts...> *error_trigger_ = new Trigger<Ts...>(); | ||||||
|  |  | ||||||
|   size_t max_response_buffer_size_{SIZE_MAX}; |   size_t max_response_buffer_size_{SIZE_MAX}; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -187,6 +187,7 @@ | |||||||
| #define ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT 1 | #define ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT 1 | ||||||
| #define ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT 2 | #define ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT 2 | ||||||
| #define USE_ESP32_CAMERA_JPEG_ENCODER | #define USE_ESP32_CAMERA_JPEG_ENCODER | ||||||
|  | #define USE_HTTP_REQUEST_RESPONSE | ||||||
| #define USE_I2C | #define USE_I2C | ||||||
| #define USE_IMPROV | #define USE_IMPROV | ||||||
| #define USE_ESP32_IMPROV_NEXT_URL | #define USE_ESP32_IMPROV_NEXT_URL | ||||||
| @@ -237,6 +238,7 @@ | |||||||
| #define USE_CAPTIVE_PORTAL | #define USE_CAPTIVE_PORTAL | ||||||
| #define USE_ESP8266_PREFERENCES_FLASH | #define USE_ESP8266_PREFERENCES_FLASH | ||||||
| #define USE_HTTP_REQUEST_ESP8266_HTTPS | #define USE_HTTP_REQUEST_ESP8266_HTTPS | ||||||
|  | #define USE_HTTP_REQUEST_RESPONSE | ||||||
| #define USE_I2C | #define USE_I2C | ||||||
| #define USE_SOCKET_IMPL_LWIP_TCP | #define USE_SOCKET_IMPL_LWIP_TCP | ||||||
|  |  | ||||||
| @@ -257,6 +259,7 @@ | |||||||
|  |  | ||||||
| #ifdef USE_RP2040 | #ifdef USE_RP2040 | ||||||
| #define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 0) | #define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 3, 0) | ||||||
|  | #define USE_HTTP_REQUEST_RESPONSE | ||||||
| #define USE_I2C | #define USE_I2C | ||||||
| #define USE_LOGGER_USB_CDC | #define USE_LOGGER_USB_CDC | ||||||
| #define USE_SOCKET_IMPL_LWIP_TCP | #define USE_SOCKET_IMPL_LWIP_TCP | ||||||
| @@ -273,6 +276,7 @@ | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_HOST | #ifdef USE_HOST | ||||||
|  | #define USE_HTTP_REQUEST_RESPONSE | ||||||
| #define USE_SOCKET_IMPL_BSD_SOCKETS | #define USE_SOCKET_IMPL_BSD_SOCKETS | ||||||
| #define USE_SOCKET_SELECT_SUPPORT | #define USE_SOCKET_SELECT_SUPPORT | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -4,51 +4,6 @@ wifi: | |||||||
|   ssid: MySSID |   ssid: MySSID | ||||||
|   password: password1 |   password: password1 | ||||||
|  |  | ||||||
| esphome: |  | ||||||
|   on_boot: |  | ||||||
|     then: |  | ||||||
|       - http_request.get: |  | ||||||
|           url: https://esphome.io |  | ||||||
|           request_headers: |  | ||||||
|             Content-Type: application/json |  | ||||||
|           collect_headers: |  | ||||||
|             - age |  | ||||||
|           on_error: |  | ||||||
|             logger.log: "Request failed" |  | ||||||
|           on_response: |  | ||||||
|             then: |  | ||||||
|               - logger.log: |  | ||||||
|                   format: "Response status: %d, Duration: %lu ms, age: %s" |  | ||||||
|                   args: |  | ||||||
|                     - response->status_code |  | ||||||
|                     - (long) response->duration_ms |  | ||||||
|                     - response->get_response_header("age").c_str() |  | ||||||
|       - http_request.post: |  | ||||||
|           url: https://esphome.io |  | ||||||
|           request_headers: |  | ||||||
|             Content-Type: application/json |  | ||||||
|           json: |  | ||||||
|             key: value |  | ||||||
|       - http_request.send: |  | ||||||
|           method: PUT |  | ||||||
|           url: https://esphome.io |  | ||||||
|           request_headers: |  | ||||||
|             Content-Type: application/json |  | ||||||
|           body: "Some data" |  | ||||||
|  |  | ||||||
| http_request: |  | ||||||
|   useragent: esphome/tagreader |  | ||||||
|   timeout: 10s |  | ||||||
|   verify_ssl: ${verify_ssl} |  | ||||||
|  |  | ||||||
| script: |  | ||||||
|   - id: does_not_compile |  | ||||||
|     parameters: |  | ||||||
|       api_url: string |  | ||||||
|     then: |  | ||||||
|       - http_request.get: |  | ||||||
|           url: "http://google.com" |  | ||||||
|  |  | ||||||
| ota: | ota: | ||||||
|   - platform: http_request |   - platform: http_request | ||||||
|     id: http_request_ota |     id: http_request_ota | ||||||
|   | |||||||
| @@ -31,6 +31,20 @@ esphome: | |||||||
|           request_headers: |           request_headers: | ||||||
|             Content-Type: application/json |             Content-Type: application/json | ||||||
|           body: "Some data" |           body: "Some data" | ||||||
|  |       - http_request.post: | ||||||
|  |           url: https://esphome.io | ||||||
|  |           request_headers: | ||||||
|  |             Content-Type: application/json | ||||||
|  |           json: | ||||||
|  |             key: value | ||||||
|  |           capture_response: true | ||||||
|  |           on_response: | ||||||
|  |             then: | ||||||
|  |               - logger.log: | ||||||
|  |                   format: "Captured response status: %d, Body: %s" | ||||||
|  |                   args: | ||||||
|  |                     - response->status_code | ||||||
|  |                     - body.c_str() | ||||||
|  |  | ||||||
| http_request: | http_request: | ||||||
|   useragent: esphome/tagreader |   useragent: esphome/tagreader | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user