esphome 2025.2.1__py3-none-any.whl → 2025.3.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +9 -1
- esphome/components/api/api_connection.cpp +426 -70
- esphome/components/api/api_connection.h +117 -25
- esphome/components/api/api_pb2.cpp +9 -0
- esphome/components/api/api_pb2.h +1 -0
- esphome/components/api/api_server.cpp +2 -2
- esphome/components/api/list_entities.cpp +76 -22
- esphome/components/api/list_entities.h +1 -0
- esphome/components/api/subscribe_state.h +2 -0
- esphome/components/audio/audio_reader.cpp +7 -7
- esphome/components/audio/audio_reader.h +1 -1
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -0
- esphome/components/bmp085/bmp085.cpp +1 -1
- esphome/components/chsc6x/__init__.py +2 -0
- esphome/components/chsc6x/chsc6x_touchscreen.cpp +47 -0
- esphome/components/chsc6x/chsc6x_touchscreen.h +34 -0
- esphome/components/chsc6x/touchscreen.py +33 -0
- esphome/components/climate/__init__.py +0 -1
- esphome/components/cst816/binary_sensor/__init__.py +2 -25
- esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +3 -14
- esphome/components/cst816/touchscreen/cst816_touchscreen.h +0 -4
- esphome/components/esp32_ble_beacon/__init__.py +3 -1
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -2
- esphome/components/esp8266/gpio.py +1 -2
- esphome/components/font/__init__.py +185 -185
- esphome/components/font/font.cpp +4 -4
- esphome/components/font/font.h +1 -0
- esphome/components/haier/climate.py +11 -10
- esphome/components/hbridge/switch/hbridge_switch.cpp +2 -2
- esphome/components/heatpumpir/climate.py +2 -1
- esphome/components/heatpumpir/heatpumpir.cpp +1 -0
- esphome/components/heatpumpir/heatpumpir.h +1 -0
- esphome/components/i2c/__init__.py +6 -6
- esphome/components/i2c/i2c_bus_esp_idf.cpp +6 -2
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
- esphome/components/ili9xxx/display.py +1 -0
- esphome/components/ili9xxx/ili9xxx_display.h +5 -0
- esphome/components/ili9xxx/ili9xxx_init.h +59 -0
- esphome/components/ld2450/__init__.py +51 -0
- esphome/components/ld2450/binary_sensor.py +47 -0
- esphome/components/ld2450/button/__init__.py +45 -0
- esphome/components/ld2450/button/reset_button.cpp +9 -0
- esphome/components/ld2450/button/reset_button.h +18 -0
- esphome/components/ld2450/button/restart_button.cpp +9 -0
- esphome/components/ld2450/button/restart_button.h +18 -0
- esphome/components/ld2450/ld2450.cpp +876 -0
- esphome/components/ld2450/ld2450.h +234 -0
- esphome/components/ld2450/number/__init__.py +121 -0
- esphome/components/ld2450/number/presence_timeout_number.cpp +12 -0
- esphome/components/ld2450/number/presence_timeout_number.h +18 -0
- esphome/components/ld2450/number/zone_coordinate_number.cpp +14 -0
- esphome/components/ld2450/number/zone_coordinate_number.h +19 -0
- esphome/components/ld2450/select/__init__.py +56 -0
- esphome/components/ld2450/select/baud_rate_select.cpp +12 -0
- esphome/components/ld2450/select/baud_rate_select.h +18 -0
- esphome/components/ld2450/select/zone_type_select.cpp +12 -0
- esphome/components/ld2450/select/zone_type_select.h +18 -0
- esphome/components/ld2450/sensor.py +156 -0
- esphome/components/ld2450/switch/__init__.py +45 -0
- esphome/components/ld2450/switch/bluetooth_switch.cpp +12 -0
- esphome/components/ld2450/switch/bluetooth_switch.h +18 -0
- esphome/components/ld2450/switch/multi_target_switch.cpp +12 -0
- esphome/components/ld2450/switch/multi_target_switch.h +18 -0
- esphome/components/ld2450/text_sensor.py +62 -0
- esphome/components/ltr390/ltr390.cpp +7 -7
- esphome/components/ltr390/ltr390.h +0 -1
- esphome/components/lvgl/defines.py +0 -2
- esphome/components/lvgl/font.cpp +1 -1
- esphome/components/lvgl/lvgl_esphome.cpp +27 -19
- esphome/components/lvgl/widgets/img.py +1 -3
- esphome/components/mcp2515/mcp2515.cpp +1 -0
- esphome/components/mlx90393/sensor.py +53 -33
- esphome/components/mlx90393/sensor_mlx90393.cpp +4 -0
- esphome/components/mlx90393/sensor_mlx90393.h +8 -3
- esphome/components/mqtt/__init__.py +2 -2
- esphome/components/msa3xx/__init__.py +189 -0
- esphome/components/msa3xx/binary_sensor.py +40 -0
- esphome/components/msa3xx/msa3xx.cpp +417 -0
- esphome/components/msa3xx/msa3xx.h +311 -0
- esphome/components/msa3xx/sensor.py +42 -0
- esphome/components/msa3xx/text_sensor.py +38 -0
- esphome/components/nfc/binary_sensor/__init__.py +4 -4
- esphome/components/opentherm/binary_sensor/__init__.py +4 -4
- esphome/components/opentherm/generate.py +6 -6
- esphome/components/opentherm/sensor/__init__.py +5 -6
- esphome/components/packages/__init__.py +35 -11
- esphome/components/pn532/binary_sensor.py +4 -4
- esphome/components/rc522/binary_sensor.py +4 -4
- esphome/components/socket/bsd_sockets_impl.cpp +1 -0
- esphome/components/socket/lwip_sockets_impl.cpp +1 -0
- esphome/components/socket/socket.h +3 -1
- esphome/components/ssd1306_base/__init__.py +7 -7
- esphome/components/thermostat/climate.py +1 -1
- esphome/components/tmp1075/tmp1075.cpp +7 -11
- esphome/components/tmp1075/tmp1075.h +1 -2
- esphome/components/tormatic/__init__.py +1 -0
- esphome/components/tormatic/cover.py +47 -0
- esphome/components/tormatic/tormatic_cover.cpp +355 -0
- esphome/components/tormatic/tormatic_cover.h +60 -0
- esphome/components/tormatic/tormatic_protocol.h +211 -0
- esphome/components/touchscreen/binary_sensor/__init__.py +3 -0
- esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp +7 -1
- esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h +3 -1
- esphome/components/touchscreen/touchscreen.cpp +3 -4
- esphome/components/udp/udp_component.h +4 -1
- esphome/components/web_server/list_entities.cpp +70 -66
- esphome/components/web_server/list_entities.h +43 -22
- esphome/components/web_server/web_server.cpp +345 -68
- esphome/components/web_server/web_server.h +138 -6
- esphome/components/web_server_base/__init__.py +1 -1
- esphome/components/web_server_idf/__init__.py +2 -0
- esphome/components/web_server_idf/web_server_idf.cpp +177 -30
- esphome/components/web_server_idf/web_server_idf.h +53 -4
- esphome/config_validation.py +23 -125
- esphome/const.py +5 -1
- esphome/core/config.py +12 -4
- esphome/core/defines.h +1 -1
- esphome/core/helpers.h +5 -3
- esphome/core/time.cpp +1 -0
- esphome/cpp_generator.py +3 -3
- esphome/dashboard/core.py +30 -21
- esphome/dashboard/dns.py +7 -1
- esphome/dashboard/entries.py +83 -16
- esphome/dashboard/settings.py +0 -4
- esphome/dashboard/status/mdns.py +43 -14
- esphome/dashboard/status/mqtt.py +22 -9
- esphome/dashboard/status/ping.py +54 -10
- esphome/dashboard/web_server.py +56 -24
- esphome/storage_json.py +4 -0
- esphome/wizard.py +13 -17
- esphome/writer.py +1 -3
- esphome/yaml_util.py +36 -33
- esphome/zeroconf.py +9 -21
- {esphome-2025.2.1.dist-info → esphome-2025.3.0b1.dist-info}/METADATA +5 -5
- {esphome-2025.2.1.dist-info → esphome-2025.3.0b1.dist-info}/RECORD +139 -99
- esphome/components/cst816/binary_sensor/cst816_button.h +0 -27
- {esphome-2025.2.1.dist-info → esphome-2025.3.0b1.dist-info}/LICENSE +0 -0
- {esphome-2025.2.1.dist-info → esphome-2025.3.0b1.dist-info}/WHEEL +0 -0
- {esphome-2025.2.1.dist-info → esphome-2025.3.0b1.dist-info}/entry_points.txt +0 -0
- {esphome-2025.2.1.dist-info → esphome-2025.3.0b1.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,11 @@
|
|
8
8
|
#include "esphome/core/controller.h"
|
9
9
|
#include "esphome/core/entity_base.h"
|
10
10
|
|
11
|
+
#include <functional>
|
12
|
+
#include <list>
|
11
13
|
#include <map>
|
14
|
+
#include <string>
|
15
|
+
#include <utility>
|
12
16
|
#include <vector>
|
13
17
|
#ifdef USE_ESP32
|
14
18
|
#include <freertos/FreeRTOS.h>
|
@@ -54,6 +58,85 @@ struct SortingGroup {
|
|
54
58
|
|
55
59
|
enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
|
56
60
|
|
61
|
+
/*
|
62
|
+
In order to defer updates in arduino mode, we need to create one AsyncEventSource per incoming request to /events.
|
63
|
+
This is because only minimal changes were made to the ESPAsyncWebServer lib_dep, it was undesirable to put deferred
|
64
|
+
update logic into that library. We need one deferred queue per connection so instead of one AsyncEventSource with
|
65
|
+
multiple clients, we have multiple event sources with one client each. This is slightly awkward which is why it's
|
66
|
+
implemented in a more straightforward way for ESP-IDF. Arudino platform will eventually go away and this workaround
|
67
|
+
can be forgotten.
|
68
|
+
*/
|
69
|
+
#ifdef USE_ARDUINO
|
70
|
+
using message_generator_t = std::string(WebServer *, void *);
|
71
|
+
|
72
|
+
class DeferredUpdateEventSourceList;
|
73
|
+
class DeferredUpdateEventSource : public AsyncEventSource {
|
74
|
+
friend class DeferredUpdateEventSourceList;
|
75
|
+
|
76
|
+
/*
|
77
|
+
This class holds a pointer to the source component that wants to publish a state event, and a pointer to a function
|
78
|
+
that will lazily generate that event. The two pointers allow dedup in the deferred queue if multiple publishes for
|
79
|
+
the same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a
|
80
|
+
std::vector) is the DeferredEvent instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per
|
81
|
+
entry (and no heap fragmentation). Even 100 backed up events (you'd have to have at least 100 sensors publishing
|
82
|
+
because of dedup) would take up only 0.8 kB.
|
83
|
+
*/
|
84
|
+
struct DeferredEvent {
|
85
|
+
friend class DeferredUpdateEventSource;
|
86
|
+
|
87
|
+
protected:
|
88
|
+
void *source_;
|
89
|
+
message_generator_t *message_generator_;
|
90
|
+
|
91
|
+
public:
|
92
|
+
DeferredEvent(void *source, message_generator_t *message_generator)
|
93
|
+
: source_(source), message_generator_(message_generator) {}
|
94
|
+
bool operator==(const DeferredEvent &test) const {
|
95
|
+
return (source_ == test.source_ && message_generator_ == test.message_generator_);
|
96
|
+
}
|
97
|
+
} __attribute__((packed));
|
98
|
+
|
99
|
+
protected:
|
100
|
+
// surface a couple methods from the base class
|
101
|
+
using AsyncEventSource::handleRequest;
|
102
|
+
using AsyncEventSource::try_send;
|
103
|
+
|
104
|
+
ListEntitiesIterator entities_iterator_;
|
105
|
+
// vector is used very specifically for its zero memory overhead even though items are popped from the front (memory
|
106
|
+
// footprint is more important than speed here)
|
107
|
+
std::vector<DeferredEvent> deferred_queue_;
|
108
|
+
WebServer *web_server_;
|
109
|
+
|
110
|
+
// helper for allowing only unique entries in the queue
|
111
|
+
void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator);
|
112
|
+
|
113
|
+
void process_deferred_queue_();
|
114
|
+
|
115
|
+
public:
|
116
|
+
DeferredUpdateEventSource(WebServer *ws, const String &url)
|
117
|
+
: AsyncEventSource(url), entities_iterator_(ListEntitiesIterator(ws, this)), web_server_(ws) {}
|
118
|
+
|
119
|
+
void loop();
|
120
|
+
|
121
|
+
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
|
122
|
+
void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
|
123
|
+
};
|
124
|
+
|
125
|
+
class DeferredUpdateEventSourceList : public std::list<DeferredUpdateEventSource *> {
|
126
|
+
protected:
|
127
|
+
void on_client_connect_(WebServer *ws, DeferredUpdateEventSource *source);
|
128
|
+
void on_client_disconnect_(DeferredUpdateEventSource *source);
|
129
|
+
|
130
|
+
public:
|
131
|
+
void loop();
|
132
|
+
|
133
|
+
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
|
134
|
+
void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
|
135
|
+
|
136
|
+
void add_new_client(WebServer *ws, AsyncWebServerRequest *request);
|
137
|
+
};
|
138
|
+
#endif
|
139
|
+
|
57
140
|
/** This class allows users to create a web server with their ESP nodes.
|
58
141
|
*
|
59
142
|
* Behind the scenes it's using AsyncWebServer to set up the server. It exposes 3 things:
|
@@ -64,6 +147,10 @@ enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
|
|
64
147
|
* can be found under https://esphome.io/web-api/index.html.
|
65
148
|
*/
|
66
149
|
class WebServer : public Controller, public Component, public AsyncWebHandler {
|
150
|
+
#ifdef USE_ARDUINO
|
151
|
+
friend class DeferredUpdateEventSourceList;
|
152
|
+
#endif
|
153
|
+
|
67
154
|
public:
|
68
155
|
WebServer(web_server_base::WebServerBase *base);
|
69
156
|
|
@@ -153,6 +240,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
153
240
|
/// Handle a sensor request under '/sensor/<id>'.
|
154
241
|
void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
155
242
|
|
243
|
+
static std::string sensor_state_json_generator(WebServer *web_server, void *source);
|
244
|
+
static std::string sensor_all_json_generator(WebServer *web_server, void *source);
|
156
245
|
/// Dump the sensor state with its value as a JSON string.
|
157
246
|
std::string sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config);
|
158
247
|
#endif
|
@@ -163,6 +252,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
163
252
|
/// Handle a switch request under '/switch/<id>/</turn_on/turn_off/toggle>'.
|
164
253
|
void handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
165
254
|
|
255
|
+
static std::string switch_state_json_generator(WebServer *web_server, void *source);
|
256
|
+
static std::string switch_all_json_generator(WebServer *web_server, void *source);
|
166
257
|
/// Dump the switch state with its value as a JSON string.
|
167
258
|
std::string switch_json(switch_::Switch *obj, bool value, JsonDetail start_config);
|
168
259
|
#endif
|
@@ -171,6 +262,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
171
262
|
/// Handle a button request under '/button/<id>/press'.
|
172
263
|
void handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
173
264
|
|
265
|
+
static std::string button_state_json_generator(WebServer *web_server, void *source);
|
266
|
+
static std::string button_all_json_generator(WebServer *web_server, void *source);
|
174
267
|
/// Dump the button details with its value as a JSON string.
|
175
268
|
std::string button_json(button::Button *obj, JsonDetail start_config);
|
176
269
|
#endif
|
@@ -181,6 +274,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
181
274
|
/// Handle a binary sensor request under '/binary_sensor/<id>'.
|
182
275
|
void handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
183
276
|
|
277
|
+
static std::string binary_sensor_state_json_generator(WebServer *web_server, void *source);
|
278
|
+
static std::string binary_sensor_all_json_generator(WebServer *web_server, void *source);
|
184
279
|
/// Dump the binary sensor state with its value as a JSON string.
|
185
280
|
std::string binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config);
|
186
281
|
#endif
|
@@ -191,6 +286,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
191
286
|
/// Handle a fan request under '/fan/<id>/</turn_on/turn_off/toggle>'.
|
192
287
|
void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
193
288
|
|
289
|
+
static std::string fan_state_json_generator(WebServer *web_server, void *source);
|
290
|
+
static std::string fan_all_json_generator(WebServer *web_server, void *source);
|
194
291
|
/// Dump the fan state as a JSON string.
|
195
292
|
std::string fan_json(fan::Fan *obj, JsonDetail start_config);
|
196
293
|
#endif
|
@@ -201,6 +298,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
201
298
|
/// Handle a light request under '/light/<id>/</turn_on/turn_off/toggle>'.
|
202
299
|
void handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
203
300
|
|
301
|
+
static std::string light_state_json_generator(WebServer *web_server, void *source);
|
302
|
+
static std::string light_all_json_generator(WebServer *web_server, void *source);
|
204
303
|
/// Dump the light state as a JSON string.
|
205
304
|
std::string light_json(light::LightState *obj, JsonDetail start_config);
|
206
305
|
#endif
|
@@ -211,6 +310,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
211
310
|
/// Handle a text sensor request under '/text_sensor/<id>'.
|
212
311
|
void handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
213
312
|
|
313
|
+
static std::string text_sensor_state_json_generator(WebServer *web_server, void *source);
|
314
|
+
static std::string text_sensor_all_json_generator(WebServer *web_server, void *source);
|
214
315
|
/// Dump the text sensor state with its value as a JSON string.
|
215
316
|
std::string text_sensor_json(text_sensor::TextSensor *obj, const std::string &value, JsonDetail start_config);
|
216
317
|
#endif
|
@@ -221,6 +322,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
221
322
|
/// Handle a cover request under '/cover/<id>/<open/close/stop/set>'.
|
222
323
|
void handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
223
324
|
|
325
|
+
static std::string cover_state_json_generator(WebServer *web_server, void *source);
|
326
|
+
static std::string cover_all_json_generator(WebServer *web_server, void *source);
|
224
327
|
/// Dump the cover state as a JSON string.
|
225
328
|
std::string cover_json(cover::Cover *obj, JsonDetail start_config);
|
226
329
|
#endif
|
@@ -230,6 +333,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
230
333
|
/// Handle a number request under '/number/<id>'.
|
231
334
|
void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
232
335
|
|
336
|
+
static std::string number_state_json_generator(WebServer *web_server, void *source);
|
337
|
+
static std::string number_all_json_generator(WebServer *web_server, void *source);
|
233
338
|
/// Dump the number state with its value as a JSON string.
|
234
339
|
std::string number_json(number::Number *obj, float value, JsonDetail start_config);
|
235
340
|
#endif
|
@@ -239,6 +344,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
239
344
|
/// Handle a date request under '/date/<id>'.
|
240
345
|
void handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
241
346
|
|
347
|
+
static std::string date_state_json_generator(WebServer *web_server, void *source);
|
348
|
+
static std::string date_all_json_generator(WebServer *web_server, void *source);
|
242
349
|
/// Dump the date state with its value as a JSON string.
|
243
350
|
std::string date_json(datetime::DateEntity *obj, JsonDetail start_config);
|
244
351
|
#endif
|
@@ -248,6 +355,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
248
355
|
/// Handle a time request under '/time/<id>'.
|
249
356
|
void handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
250
357
|
|
358
|
+
static std::string time_state_json_generator(WebServer *web_server, void *source);
|
359
|
+
static std::string time_all_json_generator(WebServer *web_server, void *source);
|
251
360
|
/// Dump the time state with its value as a JSON string.
|
252
361
|
std::string time_json(datetime::TimeEntity *obj, JsonDetail start_config);
|
253
362
|
#endif
|
@@ -257,6 +366,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
257
366
|
/// Handle a datetime request under '/datetime/<id>'.
|
258
367
|
void handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
259
368
|
|
369
|
+
static std::string datetime_state_json_generator(WebServer *web_server, void *source);
|
370
|
+
static std::string datetime_all_json_generator(WebServer *web_server, void *source);
|
260
371
|
/// Dump the datetime state with its value as a JSON string.
|
261
372
|
std::string datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config);
|
262
373
|
#endif
|
@@ -266,6 +377,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
266
377
|
/// Handle a text input request under '/text/<id>'.
|
267
378
|
void handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
268
379
|
|
380
|
+
static std::string text_state_json_generator(WebServer *web_server, void *source);
|
381
|
+
static std::string text_all_json_generator(WebServer *web_server, void *source);
|
269
382
|
/// Dump the text state with its value as a JSON string.
|
270
383
|
std::string text_json(text::Text *obj, const std::string &value, JsonDetail start_config);
|
271
384
|
#endif
|
@@ -275,6 +388,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
275
388
|
/// Handle a select request under '/select/<id>'.
|
276
389
|
void handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
277
390
|
|
391
|
+
static std::string select_state_json_generator(WebServer *web_server, void *source);
|
392
|
+
static std::string select_all_json_generator(WebServer *web_server, void *source);
|
278
393
|
/// Dump the select state with its value as a JSON string.
|
279
394
|
std::string select_json(select::Select *obj, const std::string &value, JsonDetail start_config);
|
280
395
|
#endif
|
@@ -284,6 +399,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
284
399
|
/// Handle a climate request under '/climate/<id>'.
|
285
400
|
void handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
286
401
|
|
402
|
+
static std::string climate_state_json_generator(WebServer *web_server, void *source);
|
403
|
+
static std::string climate_all_json_generator(WebServer *web_server, void *source);
|
287
404
|
/// Dump the climate details
|
288
405
|
std::string climate_json(climate::Climate *obj, JsonDetail start_config);
|
289
406
|
#endif
|
@@ -294,6 +411,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
294
411
|
/// Handle a lock request under '/lock/<id>/</lock/unlock/open>'.
|
295
412
|
void handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
296
413
|
|
414
|
+
static std::string lock_state_json_generator(WebServer *web_server, void *source);
|
415
|
+
static std::string lock_all_json_generator(WebServer *web_server, void *source);
|
297
416
|
/// Dump the lock state with its value as a JSON string.
|
298
417
|
std::string lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config);
|
299
418
|
#endif
|
@@ -304,6 +423,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
304
423
|
/// Handle a valve request under '/valve/<id>/<open/close/stop/set>'.
|
305
424
|
void handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
306
425
|
|
426
|
+
static std::string valve_state_json_generator(WebServer *web_server, void *source);
|
427
|
+
static std::string valve_all_json_generator(WebServer *web_server, void *source);
|
307
428
|
/// Dump the valve state as a JSON string.
|
308
429
|
std::string valve_json(valve::Valve *obj, JsonDetail start_config);
|
309
430
|
#endif
|
@@ -314,6 +435,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
314
435
|
/// Handle a alarm_control_panel request under '/alarm_control_panel/<id>'.
|
315
436
|
void handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
316
437
|
|
438
|
+
static std::string alarm_control_panel_state_json_generator(WebServer *web_server, void *source);
|
439
|
+
static std::string alarm_control_panel_all_json_generator(WebServer *web_server, void *source);
|
317
440
|
/// Dump the alarm_control_panel state with its value as a JSON string.
|
318
441
|
std::string alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
|
319
442
|
alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config);
|
@@ -322,6 +445,9 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
322
445
|
#ifdef USE_EVENT
|
323
446
|
void on_event(event::Event *obj, const std::string &event_type) override;
|
324
447
|
|
448
|
+
static std::string event_state_json_generator(WebServer *web_server, void *source);
|
449
|
+
static std::string event_all_json_generator(WebServer *web_server, void *source);
|
450
|
+
|
325
451
|
/// Handle a event request under '/event<id>'.
|
326
452
|
void handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
327
453
|
|
@@ -335,6 +461,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
335
461
|
/// Handle a update request under '/update/<id>'.
|
336
462
|
void handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
337
463
|
|
464
|
+
static std::string update_state_json_generator(WebServer *web_server, void *source);
|
465
|
+
static std::string update_all_json_generator(WebServer *web_server, void *source);
|
338
466
|
/// Dump the update state with its value as a JSON string.
|
339
467
|
std::string update_json(update::UpdateEntity *obj, JsonDetail start_config);
|
340
468
|
#endif
|
@@ -349,14 +477,19 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
349
477
|
void add_entity_config(EntityBase *entity, float weight, uint64_t group);
|
350
478
|
void add_sorting_group(uint64_t group_id, const std::string &group_name, float weight);
|
351
479
|
|
480
|
+
std::map<EntityBase *, SortingComponents> sorting_entitys_;
|
481
|
+
std::map<uint64_t, SortingGroup> sorting_groups_;
|
482
|
+
bool include_internal_{false};
|
483
|
+
|
352
484
|
protected:
|
353
485
|
void schedule_(std::function<void()> &&f);
|
354
|
-
friend ListEntitiesIterator;
|
355
486
|
web_server_base::WebServerBase *base_;
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
487
|
+
#ifdef USE_ARDUINO
|
488
|
+
DeferredUpdateEventSourceList events_;
|
489
|
+
#endif
|
490
|
+
#ifdef USE_ESP_IDF
|
491
|
+
AsyncEventSource events_{"/events", this};
|
492
|
+
#endif
|
360
493
|
|
361
494
|
#if USE_WEBSERVER_VERSION == 1
|
362
495
|
const char *css_url_{nullptr};
|
@@ -368,7 +501,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
368
501
|
#ifdef USE_WEBSERVER_JS_INCLUDE
|
369
502
|
const char *js_include_{nullptr};
|
370
503
|
#endif
|
371
|
-
bool include_internal_{false};
|
372
504
|
bool allow_ota_{true};
|
373
505
|
bool expose_log_{true};
|
374
506
|
#ifdef USE_ESP32
|
@@ -37,4 +37,4 @@ async def to_code(config):
|
|
37
37
|
cg.add_library("FS", None)
|
38
38
|
cg.add_library("Update", None)
|
39
39
|
# https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json
|
40
|
-
cg.add_library("esphome/ESPAsyncWebServer-esphome", "3.
|
40
|
+
cg.add_library("esphome/ESPAsyncWebServer-esphome", "3.3.0")
|
@@ -8,6 +8,10 @@
|
|
8
8
|
#include "esp_tls_crypto.h"
|
9
9
|
|
10
10
|
#include "utils.h"
|
11
|
+
|
12
|
+
#include "esphome/components/web_server/web_server.h"
|
13
|
+
#include "esphome/components/web_server/list_entities.h"
|
14
|
+
|
11
15
|
#include "web_server_idf.h"
|
12
16
|
|
13
17
|
namespace esphome {
|
@@ -276,21 +280,37 @@ AsyncEventSource::~AsyncEventSource() {
|
|
276
280
|
}
|
277
281
|
|
278
282
|
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request) {
|
279
|
-
auto *rsp =
|
283
|
+
auto *rsp = // NOLINT(cppcoreguidelines-owning-memory)
|
284
|
+
new AsyncEventSourceResponse(request, this, this->web_server_);
|
280
285
|
if (this->on_connect_) {
|
281
286
|
this->on_connect_(rsp);
|
282
287
|
}
|
283
288
|
this->sessions_.insert(rsp);
|
284
289
|
}
|
285
290
|
|
286
|
-
void AsyncEventSource::
|
291
|
+
void AsyncEventSource::loop() {
|
292
|
+
for (auto *ses : this->sessions_) {
|
293
|
+
ses->loop();
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
void AsyncEventSource::try_send_nodefer(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
|
298
|
+
for (auto *ses : this->sessions_) {
|
299
|
+
ses->try_send_nodefer(message, event, id, reconnect);
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
void AsyncEventSource::deferrable_send_state(void *source, const char *event_type,
|
304
|
+
message_generator_t *message_generator) {
|
287
305
|
for (auto *ses : this->sessions_) {
|
288
|
-
ses->
|
306
|
+
ses->deferrable_send_state(source, event_type, message_generator);
|
289
307
|
}
|
290
308
|
}
|
291
309
|
|
292
|
-
AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *request,
|
293
|
-
|
310
|
+
AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *request,
|
311
|
+
esphome::web_server_idf::AsyncEventSource *server,
|
312
|
+
esphome::web_server::WebServer *ws)
|
313
|
+
: server_(server), web_server_(ws), entities_iterator_(new esphome::web_server::ListEntitiesIterator(ws, server)) {
|
294
314
|
httpd_req_t *req = *request;
|
295
315
|
|
296
316
|
httpd_resp_set_status(req, HTTPD_200);
|
@@ -309,6 +329,30 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *
|
|
309
329
|
|
310
330
|
this->hd_ = req->handle;
|
311
331
|
this->fd_ = httpd_req_to_sockfd(req);
|
332
|
+
|
333
|
+
// Configure reconnect timeout and send config
|
334
|
+
// this should always go through since the tcp send buffer is empty on connect
|
335
|
+
std::string message = ws->get_config_json();
|
336
|
+
this->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
|
337
|
+
|
338
|
+
for (auto &group : ws->sorting_groups_) {
|
339
|
+
message = json::build_json([group](JsonObject root) {
|
340
|
+
root["name"] = group.second.name;
|
341
|
+
root["sorting_weight"] = group.second.weight;
|
342
|
+
});
|
343
|
+
|
344
|
+
// a (very) large number of these should be able to be queued initially without defer
|
345
|
+
// since the only thing in the send buffer at this point is the initial ping/config
|
346
|
+
this->try_send_nodefer(message.c_str(), "sorting_group");
|
347
|
+
}
|
348
|
+
|
349
|
+
this->entities_iterator_->begin(ws->include_internal_);
|
350
|
+
|
351
|
+
// just dump them all up-front and take advantage of the deferred queue
|
352
|
+
// on second thought that takes too long, but leaving the commented code here for debug purposes
|
353
|
+
// while(!this->entities_iterator_->completed()) {
|
354
|
+
// this->entities_iterator_->advance();
|
355
|
+
//}
|
312
356
|
}
|
313
357
|
|
314
358
|
void AsyncEventSourceResponse::destroy(void *ptr) {
|
@@ -317,52 +361,155 @@ void AsyncEventSourceResponse::destroy(void *ptr) {
|
|
317
361
|
delete rsp; // NOLINT(cppcoreguidelines-owning-memory)
|
318
362
|
}
|
319
363
|
|
320
|
-
|
321
|
-
|
364
|
+
// helper for allowing only unique entries in the queue
|
365
|
+
void AsyncEventSourceResponse::deq_push_back_with_dedup_(void *source, message_generator_t *message_generator) {
|
366
|
+
DeferredEvent item(source, message_generator);
|
367
|
+
|
368
|
+
auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(),
|
369
|
+
[&item](const DeferredEvent &test) -> bool { return test == item; });
|
370
|
+
|
371
|
+
if (iter != this->deferred_queue_.end()) {
|
372
|
+
(*iter) = item;
|
373
|
+
} else {
|
374
|
+
this->deferred_queue_.push_back(item);
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
void AsyncEventSourceResponse::process_deferred_queue_() {
|
379
|
+
while (!deferred_queue_.empty()) {
|
380
|
+
DeferredEvent &de = deferred_queue_.front();
|
381
|
+
std::string message = de.message_generator_(web_server_, de.source_);
|
382
|
+
if (this->try_send_nodefer(message.c_str(), "state")) {
|
383
|
+
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
|
384
|
+
deferred_queue_.erase(deferred_queue_.begin());
|
385
|
+
} else {
|
386
|
+
break;
|
387
|
+
}
|
388
|
+
}
|
389
|
+
}
|
390
|
+
|
391
|
+
void AsyncEventSourceResponse::process_buffer_() {
|
392
|
+
if (event_buffer_.empty()) {
|
393
|
+
return;
|
394
|
+
}
|
395
|
+
if (event_bytes_sent_ == event_buffer_.size()) {
|
396
|
+
event_buffer_.resize(0);
|
397
|
+
event_bytes_sent_ = 0;
|
322
398
|
return;
|
323
399
|
}
|
324
400
|
|
325
|
-
|
401
|
+
int bytes_sent = httpd_socket_send(this->hd_, this->fd_, event_buffer_.c_str() + event_bytes_sent_,
|
402
|
+
event_buffer_.size() - event_bytes_sent_, 0);
|
403
|
+
if (bytes_sent == HTTPD_SOCK_ERR_TIMEOUT || bytes_sent == HTTPD_SOCK_ERR_FAIL) {
|
404
|
+
return;
|
405
|
+
}
|
406
|
+
event_bytes_sent_ += bytes_sent;
|
407
|
+
|
408
|
+
if (event_bytes_sent_ == event_buffer_.size()) {
|
409
|
+
event_buffer_.resize(0);
|
410
|
+
event_bytes_sent_ = 0;
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
void AsyncEventSourceResponse::loop() {
|
415
|
+
process_buffer_();
|
416
|
+
process_deferred_queue_();
|
417
|
+
if (!this->entities_iterator_->completed())
|
418
|
+
this->entities_iterator_->advance();
|
419
|
+
}
|
420
|
+
|
421
|
+
bool AsyncEventSourceResponse::try_send_nodefer(const char *message, const char *event, uint32_t id,
|
422
|
+
uint32_t reconnect) {
|
423
|
+
if (this->fd_ == 0) {
|
424
|
+
return false;
|
425
|
+
}
|
426
|
+
|
427
|
+
process_buffer_();
|
428
|
+
if (!event_buffer_.empty()) {
|
429
|
+
// there is still pending event data to send first
|
430
|
+
return false;
|
431
|
+
}
|
432
|
+
|
433
|
+
// 8 spaces are standing in for the hexidecimal chunk length to print later
|
434
|
+
const char chunk_len_header[] = " " CRLF_STR;
|
435
|
+
const int chunk_len_header_len = sizeof(chunk_len_header) - 1;
|
436
|
+
|
437
|
+
event_buffer_.append(chunk_len_header);
|
326
438
|
|
327
439
|
if (reconnect) {
|
328
|
-
|
329
|
-
|
330
|
-
|
440
|
+
event_buffer_.append("retry: ", sizeof("retry: ") - 1);
|
441
|
+
event_buffer_.append(to_string(reconnect));
|
442
|
+
event_buffer_.append(CRLF_STR, CRLF_LEN);
|
331
443
|
}
|
332
444
|
|
333
445
|
if (id) {
|
334
|
-
|
335
|
-
|
336
|
-
|
446
|
+
event_buffer_.append("id: ", sizeof("id: ") - 1);
|
447
|
+
event_buffer_.append(to_string(id));
|
448
|
+
event_buffer_.append(CRLF_STR, CRLF_LEN);
|
337
449
|
}
|
338
450
|
|
339
451
|
if (event && *event) {
|
340
|
-
|
341
|
-
|
342
|
-
|
452
|
+
event_buffer_.append("event: ", sizeof("event: ") - 1);
|
453
|
+
event_buffer_.append(event);
|
454
|
+
event_buffer_.append(CRLF_STR, CRLF_LEN);
|
343
455
|
}
|
344
456
|
|
345
457
|
if (message && *message) {
|
346
|
-
|
347
|
-
|
348
|
-
|
458
|
+
event_buffer_.append("data: ", sizeof("data: ") - 1);
|
459
|
+
event_buffer_.append(message);
|
460
|
+
event_buffer_.append(CRLF_STR, CRLF_LEN);
|
349
461
|
}
|
350
462
|
|
351
|
-
if (
|
352
|
-
return;
|
463
|
+
if (event_buffer_.empty()) {
|
464
|
+
return true;
|
353
465
|
}
|
354
466
|
|
355
|
-
|
467
|
+
event_buffer_.append(CRLF_STR, CRLF_LEN);
|
468
|
+
event_buffer_.append(CRLF_STR, CRLF_LEN);
|
469
|
+
|
470
|
+
// chunk length header itself and the final chunk terminating CRLF are not counted as part of the chunk
|
471
|
+
int chunk_len = event_buffer_.size() - CRLF_LEN - chunk_len_header_len;
|
472
|
+
char chunk_len_str[9];
|
473
|
+
snprintf(chunk_len_str, 9, "%08x", chunk_len);
|
474
|
+
std::memcpy(&event_buffer_[0], chunk_len_str, 8);
|
356
475
|
|
357
|
-
|
358
|
-
|
359
|
-
|
476
|
+
event_bytes_sent_ = 0;
|
477
|
+
process_buffer_();
|
478
|
+
|
479
|
+
return true;
|
480
|
+
}
|
360
481
|
|
361
|
-
|
362
|
-
|
482
|
+
void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *event_type,
|
483
|
+
message_generator_t *message_generator) {
|
484
|
+
// allow all json "details_all" to go through before publishing bare state events, this avoids unnamed entries showing
|
485
|
+
// up in the web GUI and reduces event load during initial connect
|
486
|
+
if (!entities_iterator_->completed() && 0 != strcmp(event_type, "state_detail_all"))
|
487
|
+
return;
|
363
488
|
|
364
|
-
|
365
|
-
|
489
|
+
if (source == nullptr)
|
490
|
+
return;
|
491
|
+
if (event_type == nullptr)
|
492
|
+
return;
|
493
|
+
if (message_generator == nullptr)
|
494
|
+
return;
|
495
|
+
|
496
|
+
if (0 != strcmp(event_type, "state_detail_all") && 0 != strcmp(event_type, "state")) {
|
497
|
+
ESP_LOGE(TAG, "Can't defer non-state event");
|
498
|
+
}
|
499
|
+
|
500
|
+
process_buffer_();
|
501
|
+
process_deferred_queue_();
|
502
|
+
|
503
|
+
if (!event_buffer_.empty() || !deferred_queue_.empty()) {
|
504
|
+
// outgoing event buffer or deferred queue still not empty which means downstream tcp send buffer full, no point
|
505
|
+
// trying to send first
|
506
|
+
deq_push_back_with_dedup_(source, message_generator);
|
507
|
+
} else {
|
508
|
+
std::string message = message_generator(web_server_, source);
|
509
|
+
if (!this->try_send_nodefer(message.c_str(), "state")) {
|
510
|
+
deq_push_back_with_dedup_(source, message_generator);
|
511
|
+
}
|
512
|
+
}
|
366
513
|
}
|
367
514
|
|
368
515
|
} // namespace web_server_idf
|