ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
homeassistant_service.h
Go to the documentation of this file.
1#pragma once
2
3#include "api_server.h"
4#ifdef USE_API
5#ifdef USE_API_HOMEASSISTANT_SERVICES
6#include <functional>
7#include <string>
8#include <type_traits>
9#include <utility>
10#include "api_pb2.h"
11#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
13#endif
18
19namespace esphome::api {
20
21// Converts a lambda result to the string sent to Home Assistant
22template<typename T>
23 requires(!std::is_pointer_v<std::remove_cvref_t<T>>)
24std::string field_to_string(T &&val) {
25 return to_string(std::forward<T>(val)); // NOLINT
26}
27inline std::string field_to_string(const char *val) { return val ? std::string(val) : std::string(); }
28inline std::string field_to_string(std::string val) { return val; }
29inline std::string field_to_string(StringRef val) { return val.str(); }
30
33template<typename... Ts> struct HomeAssistantField {
34 const char *key;
35 const char *value;
36 std::string (*fn)(const Ts &...);
37
38 template<typename F> static constexpr HomeAssistantField from_lambda(const char *key, F /*lambda*/) {
39 return {key, nullptr, &call_lambda<F>};
40 }
41 template<typename F> static std::string call_lambda(const Ts &...x) { return field_to_string(F{}(x...)); }
42};
43
44#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
45// Represents the response data from a Home Assistant action
46// Note: This class holds a StringRef to the error_message from the protobuf message.
47// The protobuf message must outlive the ActionResponse (which is guaranteed since
48// the callback is invoked synchronously while the message is on the stack).
50 public:
51 ActionResponse(bool success, StringRef error_message) : success_(success), error_message_(error_message) {}
52
53#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
54 ActionResponse(bool success, StringRef error_message, const uint8_t *data, size_t data_len)
55 : success_(success), error_message_(error_message) {
56 if (data == nullptr || data_len == 0)
57 return;
58 JsonDocument tmp = json::parse_json(data, data_len);
59 swap(this->json_document_, tmp);
60 }
61#endif
62
63 bool is_success() const { return this->success_; }
64 // Returns reference to error message - can be implicitly converted to std::string if needed
65 const StringRef &get_error_message() const { return this->error_message_; }
66
67#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
68 // Get data as parsed JSON object (const version returns read-only view)
69 JsonObjectConst get_json() const { return this->json_document_.as<JsonObjectConst>(); }
70#endif
71
72 protected:
75#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
76 JsonDocument json_document_;
77#endif
78};
79
80// Callback type for action responses
81template<typename... Ts> using ActionResponseCallback = std::function<void(const ActionResponse &, Ts...)>;
82#endif
83
84template<typename... Ts> class HomeAssistantServiceCallAction final : public Action<Ts...> {
85 public:
86 using Field = HomeAssistantField<Ts...>;
87
90 HomeAssistantServiceCallAction(APIServer *parent, bool is_event, const Field *fields, uint8_t data_count,
91 uint8_t data_template_count, uint8_t variables_count)
92 : parent_(parent),
93 fields_(fields),
94 data_count_(data_count),
95 data_template_count_(data_template_count),
96 variables_count_(variables_count) {
97 this->flags_.is_event = is_event;
98 }
99
100#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
101 template<typename T> void set_response_template(T response_template) {
102 this->response_template_ = response_template;
103 this->flags_.has_response_template = true;
104 }
105
106 void set_wants_status() { this->flags_.wants_status = true; }
107 void set_wants_response() { this->flags_.wants_response = true; }
108
109#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
111#endif
112 Trigger<Ts...> *get_success_trigger() { return &this->success_trigger_; }
113 Trigger<std::string, Ts...> *get_error_trigger() { return &this->error_trigger_; }
114#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
115
116 void play(const Ts &...x) override {
117 const Field *fields = this->fields_;
118 const size_t total = 1 + this->data_count_ + this->data_template_count_ + this->variables_count_;
119
120 // Lambda results must live until the send; the constant strings are encoded from flash where they are
121 size_t lambda_count = 0;
122 for (size_t i = 0; i < total; i++)
123 lambda_count += fields[i].fn != nullptr;
125 results.init(lambda_count);
126 auto string_ref = [](const char *str) { return ProgmemStringRef(str, ESPHOME_strlen_P(str)); };
127 auto value_ref = [&](const Field &field) {
128 if (field.fn == nullptr)
129 return string_ref(field.value);
130 results.push_back(field.fn(x...));
131 return ProgmemStringRef(StringRef(results.back()));
132 };
133 auto fill = [&](FixedVector<HomeassistantServiceMap> &dest, uint8_t count) {
134 dest.init(count);
135 for (uint8_t i = 0; i < count; i++, fields++) {
136 auto &kv = dest.emplace_back();
137 kv.key = string_ref(fields->key);
138 kv.value = value_ref(*fields);
139 }
140 };
141
143 resp.service = value_ref(*fields++);
144 resp.is_event = this->flags_.is_event;
145 fill(resp.data, this->data_count_);
146 fill(resp.data_template, this->data_template_count_);
147 fill(resp.variables, this->variables_count_);
148
149#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
150#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
151 // IMPORTANT: Declare at outer scope so it lives until send_homeassistant_action returns.
152 std::string response_template_value;
153#endif
154 if (this->flags_.wants_status) {
155 // Generate a unique call ID for this service call
156 static uint32_t call_id_counter = 1;
157 uint32_t call_id = call_id_counter++;
158 resp.call_id = call_id;
159#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
160 if (this->flags_.wants_response) {
161 resp.wants_response = true;
162 // Set response template if provided
163 if (this->flags_.has_response_template) {
164 response_template_value = this->response_template_.value(x...);
165 resp.response_template = StringRef(response_template_value);
166 }
167 }
168#endif
169
170 auto captured_args = std::make_tuple(x...);
171 this->parent_->register_action_response_callback(call_id, [this, captured_args](const ActionResponse &response) {
172 std::apply(
173 [this, &response](auto &&...args) {
174 if (response.is_success()) {
175#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
176 if (this->flags_.wants_response) {
177 this->success_trigger_with_response_.trigger(response.get_json(), args...);
178 } else
179#endif
180 {
181 this->success_trigger_.trigger(args...);
182 }
183 } else {
184 this->error_trigger_.trigger(response.get_error_message(), args...);
185 }
186 },
187 captured_args);
188 });
189 }
190#endif
191
193 }
194
195 protected:
198 uint8_t data_count_;
201 struct Flags {
202 uint8_t is_event : 1;
203 uint8_t wants_status : 1;
204 uint8_t wants_response : 1;
206 uint8_t reserved : 4;
207 } flags_{0};
208#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
209#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
212#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
214 Trigger<std::string, Ts...> error_trigger_;
215#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
216};
217
218} // namespace esphome::api
219
220#endif
221#endif
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Definition helpers.h:624
T & back()
Access last element (no bounds checking - matches std::vector behavior) Caller must ensure vector is ...
Definition helpers.h:788
T & emplace_back(Args &&...args)
Emplace element without bounds checking - constructs in-place with arguments Caller must ensure suffi...
Definition helpers.h:774
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:751
void init(size_t n)
Definition helpers.h:714
A string that may live in flash (on ESP8266): pointer and length, with no byte-reading operations.
Definition string_ref.h:230
StringRef is a reference to a string owned by something else.
Definition string_ref.h:31
Primary TemplatableValue: stores either a constant value or a function pointer.
Definition automation.h:168
T value(X... x) const
Definition automation.h:257
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
Definition automation.h:556
void register_action_response_callback(uint32_t call_id, ActionResponseCallback callback)
void send_homeassistant_action(const HomeassistantActionRequest &call)
ActionResponse(bool success, StringRef error_message)
const StringRef & get_error_message() const
JsonObjectConst get_json() const
ActionResponse(bool success, StringRef error_message, const uint8_t *data, size_t data_len)
Trigger< JsonObjectConst, Ts... > success_trigger_with_response_
HomeAssistantServiceCallAction(APIServer *parent, bool is_event, const Field *fields, uint8_t data_count, uint8_t data_template_count, uint8_t variables_count)
fields is a codegen table: the action or event name (no key), then the data, data_template and variab...
Trigger< std::string, Ts... > * get_error_trigger()
TemplatableValue< std::string, Ts... > response_template_
Trigger< JsonObjectConst, Ts... > * get_success_trigger_with_response()
struct esphome::api::HomeAssistantServiceCallAction::Flags flags_
FixedVector< HomeassistantServiceMap > variables
Definition api_pb2.h:1425
FixedVector< HomeassistantServiceMap > data
Definition api_pb2.h:1423
FixedVector< HomeassistantServiceMap > data_template
Definition api_pb2.h:1424
unsigned count
Definition gif.cpp:51
mopeka_std_values val[3]
std::function< void(const ActionResponse &, Ts...)> ActionResponseCallback
std::string field_to_string(T &&val)
bool parse_json(const std::string &data, const json_parse_t &f)
Parse a JSON string and run the provided json parse function if it's valid.
Definition json_util.cpp:28
static void uint32_t
A key and value from codegen; on ESP8266 the table and its strings are in flash.
static std::string call_lambda(const Ts &...x)
static constexpr HomeAssistantField from_lambda(const char *key, F)
std::string(* fn)(const Ts &...)
uint16_t x
Definition tt21100.cpp:5