ESPHome  2024.7.2
http_request.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <list>
4 #include <map>
5 #include <memory>
6 #include <utility>
7 #include <vector>
8 
12 #include "esphome/core/component.h"
13 #include "esphome/core/defines.h"
14 #include "esphome/core/helpers.h"
15 #include "esphome/core/log.h"
16 
17 namespace esphome {
18 namespace http_request {
19 
20 struct Header {
21  const char *name;
22  const char *value;
23 };
24 
26 
27 class HttpContainer : public Parented<HttpRequestComponent> {
28  public:
29  virtual ~HttpContainer() = default;
32  uint32_t duration_ms;
33 
34  virtual int read(uint8_t *buf, size_t max_len) = 0;
35  virtual void end() = 0;
36 
37  void set_secure(bool secure) { this->secure_ = secure; }
38 
39  size_t get_bytes_read() const { return this->bytes_read_; }
40 
41  protected:
42  size_t bytes_read_{0};
43  bool secure_{false};
44 };
45 
46 class HttpRequestResponseTrigger : public Trigger<std::shared_ptr<HttpContainer>, std::string &> {
47  public:
48  void process(std::shared_ptr<HttpContainer> container, std::string &response_body) {
49  this->trigger(std::move(container), response_body);
50  }
51 };
52 
54  public:
55  void dump_config() override;
56  float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
57 
58  void set_useragent(const char *useragent) { this->useragent_ = useragent; }
59  void set_timeout(uint16_t timeout) { this->timeout_ = timeout; }
60  void set_watchdog_timeout(uint32_t watchdog_timeout) { this->watchdog_timeout_ = watchdog_timeout; }
61  uint32_t get_watchdog_timeout() const { return this->watchdog_timeout_; }
62  void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; }
63  void set_redirect_limit(uint16_t limit) { this->redirect_limit_ = limit; }
64 
65  std::shared_ptr<HttpContainer> get(std::string url) { return this->start(std::move(url), "GET", "", {}); }
66  std::shared_ptr<HttpContainer> get(std::string url, std::list<Header> headers) {
67  return this->start(std::move(url), "GET", "", std::move(headers));
68  }
69  std::shared_ptr<HttpContainer> post(std::string url, std::string body) {
70  return this->start(std::move(url), "POST", std::move(body), {});
71  }
72  std::shared_ptr<HttpContainer> post(std::string url, std::string body, std::list<Header> headers) {
73  return this->start(std::move(url), "POST", std::move(body), std::move(headers));
74  }
75 
76  virtual std::shared_ptr<HttpContainer> start(std::string url, std::string method, std::string body,
77  std::list<Header> headers) = 0;
78 
79  protected:
80  const char *useragent_{nullptr};
82  uint16_t redirect_limit_;
83  uint16_t timeout_{4500};
84  uint32_t watchdog_timeout_{0};
85 };
86 
87 template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
88  public:
89  HttpRequestSendAction(HttpRequestComponent *parent) : parent_(parent) {}
90  TEMPLATABLE_VALUE(std::string, url)
91  TEMPLATABLE_VALUE(const char *, method)
92  TEMPLATABLE_VALUE(std::string, body)
93  TEMPLATABLE_VALUE(bool, capture_response)
94 
95  void add_header(const char *key, TemplatableValue<const char *, Ts...> value) { this->headers_.insert({key, value}); }
96 
97  void add_json(const char *key, TemplatableValue<std::string, Ts...> value) { this->json_.insert({key, value}); }
98 
99  void set_json(std::function<void(Ts..., JsonObject)> json_func) { this->json_func_ = json_func; }
100 
101  void register_response_trigger(HttpRequestResponseTrigger *trigger) { this->response_triggers_.push_back(trigger); }
102 
103  void set_max_response_buffer_size(size_t max_response_buffer_size) {
104  this->max_response_buffer_size_ = max_response_buffer_size;
105  }
106 
107  void play(Ts... x) override {
108  std::string body;
109  if (this->body_.has_value()) {
110  body = this->body_.value(x...);
111  }
112  if (!this->json_.empty()) {
113  auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_, this, x..., std::placeholders::_1);
114  body = json::build_json(f);
115  }
116  if (this->json_func_ != nullptr) {
117  auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1);
118  body = json::build_json(f);
119  }
120  std::list<Header> headers;
121  for (const auto &item : this->headers_) {
122  auto val = item.second;
123  Header header;
124  header.name = item.first;
125  header.value = val.value(x...);
126  headers.push_back(header);
127  }
128 
129  auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, headers);
130 
131  if (container == nullptr) {
132  return;
133  }
134 
135  size_t content_length = container->content_length;
136  size_t max_length = std::min(content_length, this->max_response_buffer_size_);
137 
138  std::string response_body;
139  if (this->capture_response_.value(x...)) {
141  uint8_t *buf = allocator.allocate(max_length);
142  if (buf != nullptr) {
143  size_t read_index = 0;
144  while (container->get_bytes_read() < max_length) {
145  int read = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
146  App.feed_wdt();
147  yield();
148  read_index += read;
149  }
150  response_body.reserve(read_index);
151  response_body.assign((char *) buf, read_index);
152  allocator.deallocate(buf, max_length);
153  }
154  }
155 
156  if (this->response_triggers_.size() == 1) {
157  // if there is only one trigger, no need to copy the response body
158  this->response_triggers_[0]->process(container, response_body);
159  } else {
160  for (auto *trigger : this->response_triggers_) {
161  // with multiple triggers, pass a copy of the response body to each
162  // one so that modifications made in one trigger are not visible to
163  // the others
164  auto response_body_copy = std::string(response_body);
165  trigger->process(container, response_body_copy);
166  }
167  }
168  container->end();
169  }
170 
171  protected:
172  void encode_json_(Ts... x, JsonObject root) {
173  for (const auto &item : this->json_) {
174  auto val = item.second;
175  root[item.first] = val.value(x...);
176  }
177  }
178  void encode_json_func_(Ts... x, JsonObject root) { this->json_func_(x..., root); }
180  std::map<const char *, TemplatableValue<const char *, Ts...>> headers_{};
181  std::map<const char *, TemplatableValue<std::string, Ts...>> json_{};
182  std::function<void(Ts..., JsonObject)> json_func_{nullptr};
183  std::vector<HttpRequestResponseTrigger *> response_triggers_;
184 
185  size_t max_response_buffer_size_{SIZE_MAX};
186 };
187 
188 } // namespace http_request
189 } // namespace esphome
void set_json(std::function< void(Ts..., JsonObject)> json_func)
Definition: http_request.h:99
std::shared_ptr< HttpContainer > post(std::string url, std::string body)
Definition: http_request.h:69
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition: component.cpp:26
void set_follow_redirects(bool follow_redirects)
Definition: http_request.h:62
void set_useragent(const char *useragent)
Definition: http_request.h:58
uint16_t x
Definition: tt21100.cpp:17
An STL allocator that uses SPI RAM.
Definition: helpers.h:651
void deallocate(T *p, size_t n)
Definition: helpers.h:678
void add_json(const char *key, TemplatableValue< std::string, Ts... > value)
Definition: http_request.h:97
void set_max_response_buffer_size(size_t max_response_buffer_size)
Definition: http_request.h:103
mopeka_std_values val[4]
std::shared_ptr< HttpContainer > post(std::string url, std::string body, std::list< Header > headers)
Definition: http_request.h:72
void encode_json_func_(Ts... x, JsonObject root)
Definition: http_request.h:178
Application App
Global storage of Application pointer - only one Application can exist.
std::vector< HttpRequestResponseTrigger * > response_triggers_
Definition: http_request.h:183
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition: json_util.cpp:21
void set_watchdog_timeout(uint32_t watchdog_timeout)
Definition: http_request.h:60
void IRAM_ATTR HOT yield()
Definition: core.cpp:24
void process(std::shared_ptr< HttpContainer > container, std::string &response_body)
Definition: http_request.h:48
HttpRequestSendAction(HttpRequestComponent *parent)
Definition: http_request.h:89
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint8_t end[39]
Definition: sun_gtil2.cpp:31
void register_response_trigger(HttpRequestResponseTrigger *trigger)
Definition: http_request.h:101
void encode_json_(Ts... x, JsonObject root)
Definition: http_request.h:172
Helper class to easily give an object a parent of type T.
Definition: helpers.h:521