ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
api_wizard.cpp
Go to the documentation of this file.
1#include "api_wizard.h"
2
3#ifdef USE_API_WIZARD
4
5#include <cstring>
6
7#include "api_connection.h"
8#include "api_pb2.h"
9#include "api_server.h"
10#include "esphome/core/log.h"
11
12namespace esphome::api {
13
14ESPHOME_LOG_TAG(TAG, "api.wizard");
15
16#ifdef USE_API_WIZARD_INPUTS
17#ifdef USE_API_WIZARD_MULTIPLE_INPUTS
18// Walks the comma separated entity ids in value, without the spaces around each. Returns the length they take joined
19// by ',', or 0 when one is empty or has no '.'. Writes the joined list to out unless it is nullptr.
20static size_t wizard_join_entity_ids(const char *value, size_t length, char *out) {
21 size_t joined = 0;
22 const char *end = value + length;
23 while (true) {
24 const char *comma = static_cast<const char *>(memchr(value, ',', end - value));
25 const char *id_end = comma != nullptr ? comma : end;
26 while (value < id_end && *value == ' ')
27 value++;
28 while (id_end > value && id_end[-1] == ' ')
29 id_end--;
30 size_t id_length = id_end - value;
31 if (id_length == 0 || memchr(value, '.', id_length) == nullptr)
32 return 0;
33 if (joined != 0) {
34 if (out != nullptr)
35 out[joined] = ',';
36 joined++;
37 }
38 if (out != nullptr)
39 memcpy(out + joined, value, id_length);
40 joined += id_length;
41 if (comma == nullptr)
42 return joined;
43 value = comma + 1;
44 }
45}
46#endif // USE_API_WIZARD_MULTIPLE_INPUTS
47
48static bool wizard_entity_id_valid(const char *entity_id, size_t length) {
49 return length > 0 && length < WIZARD_ENTITY_ID_BUFFER_SIZE && memchr(entity_id, '.', length) != nullptr;
50}
51
52const char *wizard_set_input(const WizardInputSetRequest &msg) {
53 const char *value = msg.entity_id.c_str();
54 const size_t length = msg.entity_id.size();
55 for (size_t i = 0; i < API_WIZARD_INPUT_COUNT; i++) {
56 // The table is in flash, which ESP8266 can only read through progmem_memcpy
57 WizardInputEntry entry;
58 progmem_memcpy(&entry, &API_WIZARD_INPUTS[i], sizeof(entry));
59 if (entry.key != msg.key)
60 continue;
61#ifdef USE_API_WIZARD_MULTIPLE_INPUTS
62 if (entry.size != 0) {
63 // Two passes, validate and measure then write, so a rejected request leaves the buffer unchanged
64 const size_t joined = wizard_join_entity_ids(value, length, nullptr);
65 if (joined == 0 || joined >= entry.size)
66 break;
67 wizard_join_entity_ids(value, length, entry.entity_id);
68 entry.entity_id[joined] = '\0';
69 return entry.entity_id;
70 }
71#endif
72 if (!wizard_entity_id_valid(value, length))
73 break;
74 memcpy(entry.entity_id, value, length);
75 entry.entity_id[length] = '\0';
76 return entry.entity_id;
77 }
78 ESP_LOGW(TAG, "Ignoring an entity id for an unknown wizard input, or one that is not valid");
79 return nullptr;
80}
81#endif // USE_API_WIZARD_INPUTS
82
85 resp.data = API_WIZARD_DATA;
86 resp.data_len = API_WIZARD_DATA_SIZE;
87 if (!this->send_message(resp)) {
88 this->on_fatal_error();
89 }
90}
91
92#ifdef USE_API_WIZARD_INPUTS
94 const char *entity_id = wizard_set_input(msg);
95 if (entity_id == nullptr)
96 return;
97#ifdef USE_API_WIZARD_LINKED_INPUTS
98 // Entities subscribed to the buffer before it held an entity id, so every client needs to learn of it now
99 for (auto &client : this->parent_->active_clients()) {
100 client->resend_state_subscriptions(entity_id);
101 }
102#endif
103}
104#endif
105
106#ifdef USE_API_WIZARD_LINKED_INPUTS
107void APIConnection::resend_state_subscriptions(const char *entity_id) {
108 if (!this->flags_.home_assistant_states)
109 return;
110 for (const auto &it : this->parent_->get_state_subs()) {
111 if (it.entity_id != entity_id)
112 continue;
114 resp.entity_id = StringRef(it.entity_id);
115 resp.attribute = it.attribute != nullptr ? StringRef(it.attribute) : StringRef("");
116 resp.once = it.once;
117 if (!this->send_message(resp)) {
118 // Could not send now: send every subscription again from the loop
119 this->state_subs_at_ = 0;
120 return;
121 }
122 }
123}
124#endif
125
126} // namespace esphome::api
127
128#endif // USE_API_WIZARD
StringRef is a reference to a string owned by something else.
Definition string_ref.h:31
constexpr const char * c_str() const
Definition string_ref.h:78
constexpr size_type size() const
Definition string_ref.h:79
struct esphome::api::APIConnection::APIFlags flags_
void on_wizard_input_set_request(const WizardInputSetRequest &msg)
void resend_state_subscriptions(const char *entity_id)
Tell this client about the subscriptions whose entity id is stored in the given buffer,...
bool send_message(const T &msg)
Returns false as soon as the TCP buffer is full.
const std::vector< HomeAssistantStateSubscription > & get_state_subs() const
ActiveClientsView active_clients() const
Definition api_server.h:230
std::vector< uint8_t > & out
Definition gif.cpp:49
ESPHOME_LOG_TAG(TAG, "api.connection")
const char * wizard_set_input(const WizardInputSetRequest &msg)
Apply a WizardInputSetRequest: validate it and copy the entity id into the input's buffer.
void progmem_memcpy(void *dst, const void *src, size_t len)
Definition hal.h:56
Where the entity id of an input is kept, found by the key the client uses for it.
Definition api_wizard.h:30
uint32_t key
Definition api_wizard.h:31
uint16_t size
Definition api_wizard.h:34
char * entity_id
Definition api_wizard.h:32
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t length
Definition tt21100.cpp:0