mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Split response and error triggers
Simplify variables in response lambdas to JsonObject Use `const char *` for message and parse to json right away
This commit is contained in:
		| @@ -16,6 +16,7 @@ from esphome.const import ( | |||||||
|     CONF_KEY, |     CONF_KEY, | ||||||
|     CONF_ON_CLIENT_CONNECTED, |     CONF_ON_CLIENT_CONNECTED, | ||||||
|     CONF_ON_CLIENT_DISCONNECTED, |     CONF_ON_CLIENT_DISCONNECTED, | ||||||
|  |     CONF_ON_ERROR, | ||||||
|     CONF_ON_RESPONSE, |     CONF_ON_RESPONSE, | ||||||
|     CONF_PASSWORD, |     CONF_PASSWORD, | ||||||
|     CONF_PORT, |     CONF_PORT, | ||||||
| @@ -304,14 +305,8 @@ HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All( | |||||||
|                 {cv.string: cv.returning_lambda} |                 {cv.string: cv.returning_lambda} | ||||||
|             ), |             ), | ||||||
|             cv.Optional(CONF_RESPONSE_TEMPLATE): cv.templatable(cv.string), |             cv.Optional(CONF_RESPONSE_TEMPLATE): cv.templatable(cv.string), | ||||||
|             cv.Optional(CONF_ON_RESPONSE): automation.validate_automation( |             cv.Optional(CONF_ON_RESPONSE): automation.validate_automation(single=True), | ||||||
|                 { |             cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True), | ||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( |  | ||||||
|                         HomeAssistantActionResponseTrigger |  | ||||||
|                     ), |  | ||||||
|                 }, |  | ||||||
|                 single=True, |  | ||||||
|             ), |  | ||||||
|         } |         } | ||||||
|     ), |     ), | ||||||
|     cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION), |     cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION), | ||||||
| @@ -357,17 +352,23 @@ async def homeassistant_service_to_code( | |||||||
|  |  | ||||||
|     if on_response := config.get(CONF_ON_RESPONSE): |     if on_response := config.get(CONF_ON_RESPONSE): | ||||||
|         cg.add_define("USE_API_HOMEASSISTANT_ACTION_RESPONSES") |         cg.add_define("USE_API_HOMEASSISTANT_ACTION_RESPONSES") | ||||||
|         trigger = cg.new_Pvariable( |         cg.add(var.set_wants_response()) | ||||||
|             on_response[CONF_TRIGGER_ID], |  | ||||||
|             template_arg, |  | ||||||
|             var, |  | ||||||
|         ) |  | ||||||
|         await automation.build_automation( |         await automation.build_automation( | ||||||
|             trigger, |             var.get_response_trigger(), | ||||||
|             [(cg.std_shared_ptr.template(ActionResponse), "response"), *args], |             [(cg.JsonObject, "response"), *args], | ||||||
|             on_response, |             on_response, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     if on_error := config.get(CONF_ON_ERROR): | ||||||
|  |         cg.add_define("USE_API_HOMEASSISTANT_ACTION_RESPONSES") | ||||||
|  |         cg.add_define("USE_API_HOMEASSISTANT_ACTION_RESPONSES_ERRORS") | ||||||
|  |         cg.add(var.set_wants_response()) | ||||||
|  |         await automation.build_automation( | ||||||
|  |             var.get_error_trigger(), | ||||||
|  |             [(cg.std_string, "error"), *args], | ||||||
|  |             on_error, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     return var |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -780,8 +780,8 @@ message HomeassistantActionRequest { | |||||||
|   repeated HomeassistantServiceMap data_template = 3; |   repeated HomeassistantServiceMap data_template = 3; | ||||||
|   repeated HomeassistantServiceMap variables = 4; |   repeated HomeassistantServiceMap variables = 4; | ||||||
|   bool is_event = 5; |   bool is_event = 5; | ||||||
|   uint32 call_id = 6; // Call ID for response tracking |   uint32 call_id = 6 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES"]; // Call ID for response tracking | ||||||
|   string response_template = 7 [(no_zero_copy) = true]; // Optional Jinja template for response processing |   string response_template = 7 [(no_zero_copy) = true, (field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES"]; // Optional Jinja template for response processing | ||||||
| } | } | ||||||
|  |  | ||||||
| // Message sent by Home Assistant to ESPHome with service call response data | // Message sent by Home Assistant to ESPHome with service call response data | ||||||
| @@ -789,12 +789,12 @@ message HomeassistantActionResponse { | |||||||
|   option (id) = 130; |   option (id) = 130; | ||||||
|   option (source) = SOURCE_CLIENT; |   option (source) = SOURCE_CLIENT; | ||||||
|   option (no_delay) = true; |   option (no_delay) = true; | ||||||
|   option (ifdef) = "USE_API_HOMEASSISTANT_SERVICES"; |   option (ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES"; | ||||||
|  |  | ||||||
|   uint32 call_id = 1; // Matches the call_id from HomeassistantActionRequest |   uint32 call_id = 1; // Matches the call_id from HomeassistantActionRequest | ||||||
|   bool success = 2; // Whether the service call succeeded |   bool success = 2; // Whether the service call succeeded | ||||||
|   string error_message = 3; // Error message if success = false |   string error_message = 3; // Error message if success = false | ||||||
|   string response_data = 4; // Service response data |   bytes response_data = 4 [(pointer_to_buffer) = true]; // Service response data | ||||||
| } | } | ||||||
|  |  | ||||||
| // ==================== IMPORT HOME ASSISTANT STATES ==================== | // ==================== IMPORT HOME ASSISTANT STATES ==================== | ||||||
|   | |||||||
| @@ -8,9 +8,9 @@ | |||||||
| #endif | #endif | ||||||
| #include <cerrno> | #include <cerrno> | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
| #include <utility> |  | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <limits> | #include <limits> | ||||||
|  | #include <utility> | ||||||
| #include "esphome/components/network/util.h" | #include "esphome/components/network/util.h" | ||||||
| #include "esphome/core/application.h" | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/entity_base.h" | #include "esphome/core/entity_base.h" | ||||||
| @@ -1550,9 +1550,10 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) { | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_API_HOMEASSISTANT_SERVICES | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
| void APIConnection::on_homeassistant_action_response(const HomeassistantActionResponse &msg) { | void APIConnection::on_homeassistant_action_response(const HomeassistantActionResponse &msg) { | ||||||
|   this->parent_->handle_action_response(msg.call_id, msg.success, msg.error_message, msg.response_data); |   this->parent_->handle_action_response(msg.call_id, msg.success, msg.error_message, msg.response_data, | ||||||
|  |                                         msg.response_data_len); | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_API_NOISE | #ifdef USE_API_NOISE | ||||||
|   | |||||||
| @@ -137,8 +137,10 @@ class APIConnection final : public APIServerConnection { | |||||||
|       return; |       return; | ||||||
|     this->send_message(call, HomeassistantActionRequest::MESSAGE_TYPE); |     this->send_message(call, HomeassistantActionRequest::MESSAGE_TYPE); | ||||||
|   } |   } | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   void on_homeassistant_action_response(const HomeassistantActionResponse &msg) override; |   void on_homeassistant_action_response(const HomeassistantActionResponse &msg) override; | ||||||
| #endif | #endif  // USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|  | #endif  // USE_API_HOMEASSISTANT_SERVICES | ||||||
| #ifdef USE_BLUETOOTH_PROXY | #ifdef USE_BLUETOOTH_PROXY | ||||||
|   void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; |   void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; | ||||||
|   void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override; |   void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override; | ||||||
|   | |||||||
| @@ -884,8 +884,12 @@ void HomeassistantActionRequest::encode(ProtoWriteBuffer buffer) const { | |||||||
|     buffer.encode_message(4, it, true); |     buffer.encode_message(4, it, true); | ||||||
|   } |   } | ||||||
|   buffer.encode_bool(5, this->is_event); |   buffer.encode_bool(5, this->is_event); | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   buffer.encode_uint32(6, this->call_id); |   buffer.encode_uint32(6, this->call_id); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   buffer.encode_string(7, this->response_template); |   buffer.encode_string(7, this->response_template); | ||||||
|  | #endif | ||||||
| } | } | ||||||
| void HomeassistantActionRequest::calculate_size(ProtoSize &size) const { | void HomeassistantActionRequest::calculate_size(ProtoSize &size) const { | ||||||
|   size.add_length(1, this->service_ref_.size()); |   size.add_length(1, this->service_ref_.size()); | ||||||
| @@ -893,9 +897,15 @@ void HomeassistantActionRequest::calculate_size(ProtoSize &size) const { | |||||||
|   size.add_repeated_message(1, this->data_template); |   size.add_repeated_message(1, this->data_template); | ||||||
|   size.add_repeated_message(1, this->variables); |   size.add_repeated_message(1, this->variables); | ||||||
|   size.add_bool(1, this->is_event); |   size.add_bool(1, this->is_event); | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   size.add_uint32(1, this->call_id); |   size.add_uint32(1, this->call_id); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   size.add_length(1, this->response_template.size()); |   size.add_length(1, this->response_template.size()); | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
| bool HomeassistantActionResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | bool HomeassistantActionResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||||
|   switch (field_id) { |   switch (field_id) { | ||||||
|     case 1: |     case 1: | ||||||
| @@ -914,9 +924,12 @@ bool HomeassistantActionResponse::decode_length(uint32_t field_id, ProtoLengthDe | |||||||
|     case 3: |     case 3: | ||||||
|       this->error_message = value.as_string(); |       this->error_message = value.as_string(); | ||||||
|       break; |       break; | ||||||
|     case 4: |     case 4: { | ||||||
|       this->response_data = value.as_string(); |       // Use raw data directly to avoid allocation | ||||||
|  |       this->response_data = value.data(); | ||||||
|  |       this->response_data_len = value.size(); | ||||||
|       break; |       break; | ||||||
|  |     } | ||||||
|     default: |     default: | ||||||
|       return false; |       return false; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1114,8 +1114,12 @@ class HomeassistantActionRequest final : public ProtoMessage { | |||||||
|   std::vector<HomeassistantServiceMap> data_template{}; |   std::vector<HomeassistantServiceMap> data_template{}; | ||||||
|   std::vector<HomeassistantServiceMap> variables{}; |   std::vector<HomeassistantServiceMap> variables{}; | ||||||
|   bool is_event{false}; |   bool is_event{false}; | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   uint32_t call_id{0}; |   uint32_t call_id{0}; | ||||||
|  | #endif | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   std::string response_template{}; |   std::string response_template{}; | ||||||
|  | #endif | ||||||
|   void encode(ProtoWriteBuffer buffer) const override; |   void encode(ProtoWriteBuffer buffer) const override; | ||||||
|   void calculate_size(ProtoSize &size) const override; |   void calculate_size(ProtoSize &size) const override; | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
| @@ -1124,17 +1128,20 @@ class HomeassistantActionRequest final : public ProtoMessage { | |||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
| }; | }; | ||||||
|  | #endif | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
| class HomeassistantActionResponse final : public ProtoDecodableMessage { | class HomeassistantActionResponse final : public ProtoDecodableMessage { | ||||||
|  public: |  public: | ||||||
|   static constexpr uint8_t MESSAGE_TYPE = 130; |   static constexpr uint8_t MESSAGE_TYPE = 130; | ||||||
|   static constexpr uint8_t ESTIMATED_SIZE = 24; |   static constexpr uint8_t ESTIMATED_SIZE = 34; | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|   const char *message_name() const override { return "homeassistant_action_response"; } |   const char *message_name() const override { return "homeassistant_action_response"; } | ||||||
| #endif | #endif | ||||||
|   uint32_t call_id{0}; |   uint32_t call_id{0}; | ||||||
|   bool success{false}; |   bool success{false}; | ||||||
|   std::string error_message{}; |   std::string error_message{}; | ||||||
|   std::string response_data{}; |   const uint8_t *response_data{nullptr}; | ||||||
|  |   uint16_t response_data_len{0}; | ||||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | #ifdef HAS_PROTO_MESSAGE_DUMP | ||||||
|   void dump_to(std::string &out) const override; |   void dump_to(std::string &out) const override; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -1122,15 +1122,23 @@ void HomeassistantActionRequest::dump_to(std::string &out) const { | |||||||
|     out.append("\n"); |     out.append("\n"); | ||||||
|   } |   } | ||||||
|   dump_field(out, "is_event", this->is_event); |   dump_field(out, "is_event", this->is_event); | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   dump_field(out, "call_id", this->call_id); |   dump_field(out, "call_id", this->call_id); | ||||||
|  | #endif | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   dump_field(out, "response_template", this->response_template); |   dump_field(out, "response_template", this->response_template); | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
| void HomeassistantActionResponse::dump_to(std::string &out) const { | void HomeassistantActionResponse::dump_to(std::string &out) const { | ||||||
|   MessageDumpHelper helper(out, "HomeassistantActionResponse"); |   MessageDumpHelper helper(out, "HomeassistantActionResponse"); | ||||||
|   dump_field(out, "call_id", this->call_id); |   dump_field(out, "call_id", this->call_id); | ||||||
|   dump_field(out, "success", this->success); |   dump_field(out, "success", this->success); | ||||||
|   dump_field(out, "error_message", this->error_message); |   dump_field(out, "error_message", this->error_message); | ||||||
|   dump_field(out, "response_data", this->response_data); |   out.append("  response_data: "); | ||||||
|  |   out.append(format_hex_pretty(this->response_data, this->response_data_len)); | ||||||
|  |   out.append("\n"); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| #ifdef USE_API_HOMEASSISTANT_STATES | #ifdef USE_API_HOMEASSISTANT_STATES | ||||||
|   | |||||||
| @@ -611,7 +611,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, | |||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
| #ifdef USE_API_HOMEASSISTANT_SERVICES | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|     case HomeassistantActionResponse::MESSAGE_TYPE: { |     case HomeassistantActionResponse::MESSAGE_TYPE: { | ||||||
|       HomeassistantActionResponse msg; |       HomeassistantActionResponse msg; | ||||||
|       msg.decode(msg_data, msg_size); |       msg.decode(msg_data, msg_size); | ||||||
|   | |||||||
| @@ -66,7 +66,7 @@ class APIServerConnectionBase : public ProtoService { | |||||||
|   virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){}; |   virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){}; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_API_HOMEASSISTANT_SERVICES | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   virtual void on_homeassistant_action_response(const HomeassistantActionResponse &value){}; |   virtual void on_homeassistant_action_response(const HomeassistantActionResponse &value){}; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_API_HOMEASSISTANT_STATES | #ifdef USE_API_HOMEASSISTANT_STATES | ||||||
|   | |||||||
| @@ -403,27 +403,23 @@ void APIServer::send_homeassistant_action(const HomeassistantActionRequest &call | |||||||
|     client->send_homeassistant_action(call); |     client->send_homeassistant_action(call); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
| void APIServer::register_action_response_callback(uint32_t call_id, ActionResponseCallback callback) { | void APIServer::register_action_response_callback(uint32_t call_id, ActionResponseCallback callback) { | ||||||
|   this->action_response_callbacks_[call_id] = std::move(callback); |   this->action_response_callbacks_[call_id] = std::move(callback); | ||||||
| } | } | ||||||
|  |  | ||||||
| void APIServer::handle_action_response(uint32_t call_id, bool success, const std::string &error_message, | void APIServer::handle_action_response(uint32_t call_id, bool success, const std::string &error_message, | ||||||
|                                        const std::string &response_data) { |                                        const uint8_t *response_data, size_t response_data_len) { | ||||||
|   auto it = this->action_response_callbacks_.find(call_id); |   auto it = this->action_response_callbacks_.find(call_id); | ||||||
|   if (it != this->action_response_callbacks_.end()) { |   if (it != this->action_response_callbacks_.end()) { | ||||||
|     // Create the response object |     auto callback = std::move(it->second); | ||||||
|     auto response = std::make_shared<class ActionResponse>(success, error_message); |  | ||||||
|     response->set_data(response_data); |  | ||||||
|  |  | ||||||
|     // Call the callback |  | ||||||
|     it->second(response); |  | ||||||
|  |  | ||||||
|     // Remove the callback as it's one-time use |  | ||||||
|     this->action_response_callbacks_.erase(it); |     this->action_response_callbacks_.erase(it); | ||||||
|  |     auto response = std::make_shared<ActionResponse>(success, error_message, response_data, response_data_len); | ||||||
|  |     callback(response); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| #endif | #endif  // USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|  | #endif  // USE_API_HOMEASSISTANT_SERVICES | ||||||
|  |  | ||||||
| #ifdef USE_API_HOMEASSISTANT_STATES | #ifdef USE_API_HOMEASSISTANT_STATES | ||||||
| void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute, | void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute, | ||||||
|   | |||||||
| @@ -112,12 +112,14 @@ class APIServer : public Component, public Controller { | |||||||
| #ifdef USE_API_HOMEASSISTANT_SERVICES | #ifdef USE_API_HOMEASSISTANT_SERVICES | ||||||
|   void send_homeassistant_action(const HomeassistantActionRequest &call); |   void send_homeassistant_action(const HomeassistantActionRequest &call); | ||||||
|  |  | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   // Action response handling |   // Action response handling | ||||||
|   using ActionResponseCallback = std::function<void(std::shared_ptr<class ActionResponse>)>; |   using ActionResponseCallback = std::function<void(std::shared_ptr<class ActionResponse>)>; | ||||||
|   void register_action_response_callback(uint32_t call_id, ActionResponseCallback callback); |   void register_action_response_callback(uint32_t call_id, ActionResponseCallback callback); | ||||||
|   void handle_action_response(uint32_t call_id, bool success, const std::string &error_message, |   void handle_action_response(uint32_t call_id, bool success, const std::string &error_message, | ||||||
|                               const std::string &response_data); |                               const uint8_t *response_data, size_t response_data_len); | ||||||
| #endif | #endif  // USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|  | #endif  // USE_API_HOMEASSISTANT_SERVICES | ||||||
| #ifdef USE_API_SERVICES | #ifdef USE_API_SERVICES | ||||||
|   void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } |   void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } | ||||||
| #endif | #endif | ||||||
| @@ -193,7 +195,7 @@ class APIServer : public Component, public Controller { | |||||||
| #ifdef USE_API_SERVICES | #ifdef USE_API_SERVICES | ||||||
|   std::vector<UserServiceDescriptor *> user_services_; |   std::vector<UserServiceDescriptor *> user_services_; | ||||||
| #endif | #endif | ||||||
| #ifdef USE_API_HOMEASSISTANT_SERVICES | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   std::map<uint32_t, ActionResponseCallback> action_response_callbacks_; |   std::map<uint32_t, ActionResponseCallback> action_response_callbacks_; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
| @@ -47,42 +47,33 @@ template<typename... Ts> class TemplatableKeyValuePair { | |||||||
|   TemplatableStringValue<Ts...> value; |   TemplatableStringValue<Ts...> value; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
| // Represents the response data from a Home Assistant action | // Represents the response data from a Home Assistant action | ||||||
| class ActionResponse { | class ActionResponse { | ||||||
|  public: |  public: | ||||||
|   ActionResponse(bool success, std::string error_message = "") |   ActionResponse(bool success, std::string error_message = "", const uint8_t *data = nullptr, size_t data_len = 0) | ||||||
|       : success_(success), error_message_(std::move(error_message)) {} |       : success_(success), error_message_(std::move(error_message)) { | ||||||
|  |     if (data == nullptr || data_len == 0) | ||||||
|  |       return; | ||||||
|  |     this->json_document_ = json::parse_json(data, data_len); | ||||||
|  |     this->json_ = this->json_document_.as<JsonObject>(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool is_success() const { return this->success_; } |   bool is_success() const { return this->success_; } | ||||||
|   const std::string &get_error_message() const { return this->error_message_; } |   const std::string &get_error_message() const { return this->error_message_; } | ||||||
|   const std::string &get_data() const { return this->data_; } |  | ||||||
|   // Get data as parsed JSON object |   // Get data as parsed JSON object | ||||||
|   // Returns unbound JsonObject if data is empty or invalid JSON |   JsonObject get_json() { return this->json_; } | ||||||
|   JsonObject get_json() { |  | ||||||
|     if (this->data_.empty()) |  | ||||||
|       return JsonObject();  // Return unbound JsonObject if no data |  | ||||||
|  |  | ||||||
|     if (!this->parsed_json_) { |  | ||||||
|       this->json_document_ = json::parse_json(this->data_); |  | ||||||
|       this->json_ = this->json_document_.as<JsonObject>(); |  | ||||||
|       this->parsed_json_ = true; |  | ||||||
|     } |  | ||||||
|     return this->json_; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void set_data(const std::string &data) { this->data_ = data; } |  | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   bool success_; |   bool success_; | ||||||
|   std::string error_message_; |   std::string error_message_; | ||||||
|   std::string data_; |  | ||||||
|   JsonDocument json_document_; |   JsonDocument json_document_; | ||||||
|   JsonObject json_; |   JsonObject json_; | ||||||
|   bool parsed_json_{false}; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Callback type for action responses | // Callback type for action responses | ||||||
| template<typename... Ts> using ActionResponseCallback = std::function<void(std::shared_ptr<ActionResponse>, Ts...)>; | template<typename... Ts> using ActionResponseCallback = std::function<void(std::shared_ptr<ActionResponse>, Ts...)>; | ||||||
|  | #endif | ||||||
|  |  | ||||||
| template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts...> { | template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts...> { | ||||||
|  public: |  public: | ||||||
| @@ -101,15 +92,19 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts | |||||||
|     this->variables_.emplace_back(std::move(key), value); |     this->variables_.emplace_back(std::move(key), value); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   template<typename T> void set_response_template(T response_template) { |   template<typename T> void set_response_template(T response_template) { | ||||||
|     this->response_template_ = response_template; |     this->response_template_ = response_template; | ||||||
|     this->has_response_template_ = true; |     this->has_response_template_ = true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void set_response_callback(ActionResponseCallback<Ts...> callback) { |   void set_wants_response() { this->wants_response_ = true; } | ||||||
|     this->wants_response_ = true; |  | ||||||
|     this->response_callback_ = callback; |   Trigger<JsonObject, Ts...> *get_response_trigger() const { return this->response_trigger_; } | ||||||
|   } | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_ERRORS | ||||||
|  |   Trigger<std::string, Ts...> *get_error_trigger() const { return this->error_trigger_; } | ||||||
|  | #endif  // USE_API_HOMEASSISTANT_ACTION_RESPONSES_ERRORS | ||||||
|  | #endif  // USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|  |  | ||||||
|   void play(Ts... x) override { |   void play(Ts... x) override { | ||||||
|     HomeassistantActionRequest resp; |     HomeassistantActionRequest resp; | ||||||
| @@ -135,6 +130,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts | |||||||
|       kv.value = it.value.value(x...); |       kv.value = it.value.value(x...); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|     if (this->wants_response_) { |     if (this->wants_response_) { | ||||||
|       // Generate a unique call ID for this service call |       // Generate a unique call ID for this service call | ||||||
|       static uint32_t call_id_counter = 1; |       static uint32_t call_id_counter = 1; | ||||||
| @@ -147,11 +143,25 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts | |||||||
|       } |       } | ||||||
|  |  | ||||||
|       auto captured_args = std::make_tuple(x...); |       auto captured_args = std::make_tuple(x...); | ||||||
|       this->parent_->register_action_response_callback(call_id, [this, captured_args]( |       this->parent_->register_action_response_callback( | ||||||
|                                                                     std::shared_ptr<ActionResponse> response) { |           call_id, [this, captured_args](std::shared_ptr<ActionResponse> response) { | ||||||
|         std::apply([this, &response](auto &&...args) { this->response_callback_(response, args...); }, captured_args); |             std::apply( | ||||||
|       }); |                 [this, &response](auto &&...args) { | ||||||
|  |                   if (response->is_success()) { | ||||||
|  |                     if (this->response_trigger_ != nullptr) { | ||||||
|  |                       this->response_trigger_->trigger(response->get_json(), args...); | ||||||
|  |                     } | ||||||
|  |                   } | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_ERRORS | ||||||
|  |                   else if (this->error_trigger_ != nullptr) { | ||||||
|  |                     this->error_trigger_->trigger(response->get_error_message(), args...); | ||||||
|  |                   } | ||||||
|  | #endif  // USE_API_HOMEASSISTANT_ACTION_RESPONSES_ERRORS | ||||||
|  |                 }, | ||||||
|  |                 captured_args); | ||||||
|  |           }); | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     this->parent_->send_homeassistant_action(resp); |     this->parent_->send_homeassistant_action(resp); | ||||||
|   } |   } | ||||||
| @@ -163,21 +173,18 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts | |||||||
|   std::vector<TemplatableKeyValuePair<Ts...>> data_; |   std::vector<TemplatableKeyValuePair<Ts...>> data_; | ||||||
|   std::vector<TemplatableKeyValuePair<Ts...>> data_template_; |   std::vector<TemplatableKeyValuePair<Ts...>> data_template_; | ||||||
|   std::vector<TemplatableKeyValuePair<Ts...>> variables_; |   std::vector<TemplatableKeyValuePair<Ts...>> variables_; | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|   TemplatableStringValue<Ts...> response_template_{""}; |   TemplatableStringValue<Ts...> response_template_{""}; | ||||||
|   ActionResponseCallback<Ts...> response_callback_; |   Trigger<JsonObject, Ts...> *response_trigger_ = new Trigger<JsonObject, Ts...>(); | ||||||
|  | #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_ERRORS | ||||||
|  |   Trigger<std::string, Ts...> *error_trigger_ = new Trigger<std::string, Ts...>(); | ||||||
|  | #endif | ||||||
|   bool wants_response_{false}; |   bool wants_response_{false}; | ||||||
|   bool has_response_template_{false}; |   bool has_response_template_{false}; | ||||||
| }; | #endif | ||||||
|  |  | ||||||
| template<typename... Ts> |  | ||||||
| class HomeAssistantActionResponseTrigger : public Trigger<std::shared_ptr<ActionResponse>, Ts...> { |  | ||||||
|  public: |  | ||||||
|   HomeAssistantActionResponseTrigger(HomeAssistantServiceCallAction<Ts...> *action) { |  | ||||||
|     action->set_response_callback( |  | ||||||
|         [this](std::shared_ptr<ActionResponse> response, Ts... x) { this->trigger(response, x...); }); |  | ||||||
|   } |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace esphome::api | }  // namespace esphome::api | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -110,6 +110,8 @@ | |||||||
| #define USE_API | #define USE_API | ||||||
| #define USE_API_CLIENT_CONNECTED_TRIGGER | #define USE_API_CLIENT_CONNECTED_TRIGGER | ||||||
| #define USE_API_CLIENT_DISCONNECTED_TRIGGER | #define USE_API_CLIENT_DISCONNECTED_TRIGGER | ||||||
|  | #define USE_API_HOMEASSISTANT_ACTION_RESPONSES | ||||||
|  | #define USE_API_HOMEASSISTANT_ACTION_RESPONSES_ERRORS | ||||||
| #define USE_API_HOMEASSISTANT_SERVICES | #define USE_API_HOMEASSISTANT_SERVICES | ||||||
| #define USE_API_HOMEASSISTANT_STATES | #define USE_API_HOMEASSISTANT_STATES | ||||||
| #define USE_API_NOISE | #define USE_API_NOISE | ||||||
|   | |||||||
| @@ -17,14 +17,12 @@ esphome: | |||||||
|             type: hourly |             type: hourly | ||||||
|           on_response: |           on_response: | ||||||
|             - lambda: |- |             - lambda: |- | ||||||
|                 if (response->is_success()) { |                 JsonObject next_hour = response["response"]["weather.forecast_home"]["forecast"][0]; | ||||||
|                   JsonObject json = response->get_json(); |                 float next_temperature = next_hour["temperature"].as<float>(); | ||||||
|                   JsonObject next_hour = json["response"]["weather.forecast_home"]["forecast"][0]; |                 ESP_LOGD("main", "Next hour temperature: %f", next_temperature); | ||||||
|                   float next_temperature = next_hour["temperature"].as<float>(); |           on_error: | ||||||
|                   ESP_LOGD("main", "Next hour temperature: %f", next_temperature); |             - lambda: |- | ||||||
|                 } else { |                 ESP_LOGE("main", "Action failed with error: %s", error.c_str()); | ||||||
|                   ESP_LOGE("main", "Action failed: %s", response->get_error_message().c_str()); |  | ||||||
|                 } |  | ||||||
|       - homeassistant.action: |       - homeassistant.action: | ||||||
|           action: weather.get_forecasts |           action: weather.get_forecasts | ||||||
|           data: |           data: | ||||||
| @@ -33,13 +31,8 @@ esphome: | |||||||
|           response_template: "{{ response['weather.forecast_home']['forecast'][0]['temperature'] }}" |           response_template: "{{ response['weather.forecast_home']['forecast'][0]['temperature'] }}" | ||||||
|           on_response: |           on_response: | ||||||
|             - lambda: |- |             - lambda: |- | ||||||
|                 if (response->is_success()) { |                 float temperature = response["response"].as<float>(); | ||||||
|                   JsonObject json = response->get_json(); |                 ESP_LOGD("main", "Next hour temperature: %f", temperature); | ||||||
|                   float temperature = json["response"].as<float>(); |  | ||||||
|                   ESP_LOGD("main", "Next hour temperature: %f", temperature); |  | ||||||
|                 } else { |  | ||||||
|                   ESP_LOGE("main", "Action failed: %s", response->get_error_message().c_str()); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
| api: | api: | ||||||
|   port: 8000 |   port: 8000 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user