ESPHome 2025.10.2
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 <utility>
8#include <vector>
9#include "api_pb2.h"
10#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
12#endif
15
16namespace esphome::api {
17
18template<typename... X> class TemplatableStringValue : public TemplatableValue<std::string, X...> {
19 private:
20 // Helper to convert value to string - handles the case where value is already a string
21 template<typename T> static std::string value_to_string(T &&val) { return to_string(std::forward<T>(val)); }
22
23 // Overloads for string types - needed because std::to_string doesn't support them
24 static std::string value_to_string(char *val) {
25 return val ? std::string(val) : std::string();
26 } // For lambdas returning char* (e.g., itoa)
27 static std::string value_to_string(const char *val) { return std::string(val); } // For lambdas returning .c_str()
28 static std::string value_to_string(const std::string &val) { return val; }
29 static std::string value_to_string(std::string &&val) { return std::move(val); }
30
31 public:
32 TemplatableStringValue() : TemplatableValue<std::string, X...>() {}
33
34 template<typename F, enable_if_t<!is_invocable<F, X...>::value, int> = 0>
36
37 template<typename F, enable_if_t<is_invocable<F, X...>::value, int> = 0>
39 : TemplatableValue<std::string, X...>([f](X... x) -> std::string { return value_to_string(f(x...)); }) {}
40};
41
42template<typename... Ts> class TemplatableKeyValuePair {
43 public:
44 // Keys are always string literals from YAML dictionary keys (e.g., "code", "event")
45 // and never templatable values or lambdas. Only the value parameter can be a lambda/template.
46 // Using pass-by-value with std::move allows optimal performance for both lvalues and rvalues.
47 template<typename T> TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {}
48 std::string key;
50};
51
52#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
53// Represents the response data from a Home Assistant action
55 public:
56 ActionResponse(bool success, std::string error_message = "")
57 : success_(success), error_message_(std::move(error_message)) {}
58
59#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
60 ActionResponse(bool success, std::string error_message, const uint8_t *data, size_t data_len)
61 : success_(success), error_message_(std::move(error_message)) {
62 if (data == nullptr || data_len == 0)
63 return;
64 this->json_document_ = json::parse_json(data, data_len);
65 }
66#endif
67
68 bool is_success() const { return this->success_; }
69 const std::string &get_error_message() const { return this->error_message_; }
70
71#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
72 // Get data as parsed JSON object (const version returns read-only view)
73 JsonObjectConst get_json() const { return this->json_document_.as<JsonObjectConst>(); }
74#endif
75
76 protected:
78 std::string error_message_;
79#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
80 JsonDocument json_document_;
81#endif
82};
83
84// Callback type for action responses
85template<typename... Ts> using ActionResponseCallback = std::function<void(const ActionResponse &, Ts...)>;
86#endif
87
88template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts...> {
89 public:
90 explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent) {
91 this->flags_.is_event = is_event;
92 }
93
94 template<typename T> void set_service(T service) { this->service_ = service; }
95
96 // Keys are always string literals from the Python code generation (e.g., cg.add(var.add_data("tag_id", templ))).
97 // The value parameter can be a lambda/template, but keys are never templatable.
98 // Using pass-by-value allows the compiler to optimize for both lvalues and rvalues.
99 template<typename T> void add_data(std::string key, T value) { this->data_.emplace_back(std::move(key), value); }
100 template<typename T> void add_data_template(std::string key, T value) {
101 this->data_template_.emplace_back(std::move(key), value);
102 }
103 template<typename T> void add_variable(std::string key, T value) {
104 this->variables_.emplace_back(std::move(key), value);
105 }
106
107#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
108 template<typename T> void set_response_template(T response_template) {
109 this->response_template_ = response_template;
110 this->flags_.has_response_template = true;
111 }
112
113 void set_wants_status() { this->flags_.wants_status = true; }
114 void set_wants_response() { this->flags_.wants_response = true; }
115
116#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
117 Trigger<JsonObjectConst, Ts...> *get_success_trigger_with_response() const {
118 return this->success_trigger_with_response_;
119 }
120#endif
121 Trigger<Ts...> *get_success_trigger() const { return this->success_trigger_; }
122 Trigger<std::string, Ts...> *get_error_trigger() const { return this->error_trigger_; }
123#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
124
125 void play(Ts... x) override {
127 std::string service_value = this->service_.value(x...);
128 resp.set_service(StringRef(service_value));
129 resp.is_event = this->flags_.is_event;
130 for (auto &it : this->data_) {
131 resp.data.emplace_back();
132 auto &kv = resp.data.back();
133 kv.set_key(StringRef(it.key));
134 kv.value = it.value.value(x...);
135 }
136 for (auto &it : this->data_template_) {
137 resp.data_template.emplace_back();
138 auto &kv = resp.data_template.back();
139 kv.set_key(StringRef(it.key));
140 kv.value = it.value.value(x...);
141 }
142 for (auto &it : this->variables_) {
143 resp.variables.emplace_back();
144 auto &kv = resp.variables.back();
145 kv.set_key(StringRef(it.key));
146 kv.value = it.value.value(x...);
147 }
148
149#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
150 if (this->flags_.wants_status) {
151 // Generate a unique call ID for this service call
152 static uint32_t call_id_counter = 1;
153 uint32_t call_id = call_id_counter++;
154 resp.call_id = call_id;
155#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
156 if (this->flags_.wants_response) {
157 resp.wants_response = true;
158 // Set response template if provided
159 if (this->flags_.has_response_template) {
160 std::string response_template_value = this->response_template_.value(x...);
161 resp.response_template = response_template_value;
162 }
163 }
164#endif
165
166 auto captured_args = std::make_tuple(x...);
167 this->parent_->register_action_response_callback(call_id, [this, captured_args](const ActionResponse &response) {
168 std::apply(
169 [this, &response](auto &&...args) {
170 if (response.is_success()) {
171#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
172 if (this->flags_.wants_response) {
173 this->success_trigger_with_response_->trigger(response.get_json(), args...);
174 } else
175#endif
176 {
177 this->success_trigger_->trigger(args...);
178 }
179 } else {
180 this->error_trigger_->trigger(response.get_error_message(), args...);
181 }
182 },
183 captured_args);
184 });
185 }
186#endif
187
188 this->parent_->send_homeassistant_action(resp);
189 }
190
191 protected:
193 TemplatableStringValue<Ts...> service_{};
194 std::vector<TemplatableKeyValuePair<Ts...>> data_;
197#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
198#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
199 TemplatableStringValue<Ts...> response_template_{""};
200 Trigger<JsonObjectConst, Ts...> *success_trigger_with_response_ = new Trigger<JsonObjectConst, Ts...>();
201#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
202 Trigger<Ts...> *success_trigger_ = new Trigger<Ts...>();
203 Trigger<std::string, Ts...> *error_trigger_ = new Trigger<std::string, Ts...>();
204#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
205
206 struct Flags {
207 uint8_t is_event : 1;
208 uint8_t wants_status : 1;
209 uint8_t wants_response : 1;
211 uint8_t reserved : 5;
212 } flags_{0};
213};
214
215} // namespace esphome::api
216
217#endif
218#endif
StringRef is a reference to a string owned by something else.
Definition string_ref.h:22
ActionResponse(bool success, std::string error_message, const uint8_t *data, size_t data_len)
JsonObjectConst get_json() const
const std::string & get_error_message() const
ActionResponse(bool success, std::string error_message="")
HomeAssistantServiceCallAction(APIServer *parent, bool is_event)
std::vector< TemplatableKeyValuePair< Ts... > > variables_
Trigger< JsonObjectConst, Ts... > * get_success_trigger_with_response() const
std::vector< TemplatableKeyValuePair< Ts... > > data_template_
std::vector< TemplatableKeyValuePair< Ts... > > data_
Trigger< std::string, Ts... > * get_error_trigger() const
std::vector< HomeassistantServiceMap > data_template
Definition api_pb2.h:1114
std::vector< HomeassistantServiceMap > data
Definition api_pb2.h:1113
std::vector< HomeassistantServiceMap > variables
Definition api_pb2.h:1115
void set_service(const StringRef &ref)
Definition api_pb2.h:1112
TemplatableKeyValuePair(std::string key, T value)
TemplatableStringValue< Ts... > value
mopeka_std_values val[4]
std::function< void(const ActionResponse &, Ts...)> ActionResponseCallback
uint16_t x
Definition tt21100.cpp:5