mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			ci_test_om
			...
			cond_compi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f2682d9df5 | ||
| 
						 | 
					211a8c872b | ||
| 
						 | 
					f4b7009c96 | ||
| 
						 | 
					226399222d | ||
| 
						 | 
					9a95ec95f9 | ||
| 
						 | 
					2ef4f3c65f | ||
| 
						 | 
					6c362d42c3 | 
@@ -16,23 +16,26 @@ 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_RESPONSE,
 | 
				
			||||||
    CONF_PASSWORD,
 | 
					    CONF_PASSWORD,
 | 
				
			||||||
    CONF_PORT,
 | 
					    CONF_PORT,
 | 
				
			||||||
    CONF_REBOOT_TIMEOUT,
 | 
					    CONF_REBOOT_TIMEOUT,
 | 
				
			||||||
 | 
					    CONF_RESPONSE_TEMPLATE,
 | 
				
			||||||
    CONF_SERVICE,
 | 
					    CONF_SERVICE,
 | 
				
			||||||
    CONF_SERVICES,
 | 
					    CONF_SERVICES,
 | 
				
			||||||
    CONF_TAG,
 | 
					    CONF_TAG,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_VARIABLES,
 | 
					    CONF_VARIABLES,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
 | 
					from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
 | 
				
			||||||
 | 
					from esphome.cpp_generator import TemplateArgsType
 | 
				
			||||||
from esphome.types import ConfigType
 | 
					from esphome.types import ConfigType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_LOGGER = logging.getLogger(__name__)
 | 
					_LOGGER = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DOMAIN = "api"
 | 
					DOMAIN = "api"
 | 
				
			||||||
DEPENDENCIES = ["network"]
 | 
					DEPENDENCIES = ["network"]
 | 
				
			||||||
AUTO_LOAD = ["socket"]
 | 
					AUTO_LOAD = ["socket", "json"]
 | 
				
			||||||
CODEOWNERS = ["@esphome/core"]
 | 
					CODEOWNERS = ["@esphome/core"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
api_ns = cg.esphome_ns.namespace("api")
 | 
					api_ns = cg.esphome_ns.namespace("api")
 | 
				
			||||||
@@ -40,6 +43,10 @@ APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller)
 | 
				
			|||||||
HomeAssistantServiceCallAction = api_ns.class_(
 | 
					HomeAssistantServiceCallAction = api_ns.class_(
 | 
				
			||||||
    "HomeAssistantServiceCallAction", automation.Action
 | 
					    "HomeAssistantServiceCallAction", automation.Action
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					ActionResponse = api_ns.class_("ActionResponse")
 | 
				
			||||||
 | 
					HomeAssistantActionResponseTrigger = api_ns.class_(
 | 
				
			||||||
 | 
					    "HomeAssistantActionResponseTrigger", automation.Trigger
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
APIConnectedCondition = api_ns.class_("APIConnectedCondition", Condition)
 | 
					APIConnectedCondition = api_ns.class_("APIConnectedCondition", Condition)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger)
 | 
					UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger)
 | 
				
			||||||
@@ -273,6 +280,14 @@ async def to_code(config):
 | 
				
			|||||||
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
 | 
					KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _validate_response_config(config):
 | 
				
			||||||
 | 
					    if CONF_RESPONSE_TEMPLATE in config and not config.get(CONF_ON_RESPONSE):
 | 
				
			||||||
 | 
					        raise cv.Invalid(
 | 
				
			||||||
 | 
					            f"`{CONF_RESPONSE_TEMPLATE}` requires `{CONF_ON_RESPONSE}` to be set."
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
 | 
					HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
 | 
				
			||||||
    cv.Schema(
 | 
					    cv.Schema(
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -288,10 +303,20 @@ HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
 | 
				
			|||||||
            cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
					            cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
				
			||||||
                {cv.string: cv.returning_lambda}
 | 
					                {cv.string: cv.returning_lambda}
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_RESPONSE_TEMPLATE): cv.templatable(cv.string),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ON_RESPONSE): automation.validate_automation(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    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),
 | 
				
			||||||
    cv.rename_key(CONF_SERVICE, CONF_ACTION),
 | 
					    cv.rename_key(CONF_SERVICE, CONF_ACTION),
 | 
				
			||||||
 | 
					    _validate_response_config,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -305,7 +330,12 @@ HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
 | 
				
			|||||||
    HomeAssistantServiceCallAction,
 | 
					    HomeAssistantServiceCallAction,
 | 
				
			||||||
    HOMEASSISTANT_ACTION_ACTION_SCHEMA,
 | 
					    HOMEASSISTANT_ACTION_ACTION_SCHEMA,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
async def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
					async def homeassistant_service_to_code(
 | 
				
			||||||
 | 
					    config: ConfigType,
 | 
				
			||||||
 | 
					    action_id: ID,
 | 
				
			||||||
 | 
					    template_arg: cg.TemplateArguments,
 | 
				
			||||||
 | 
					    args: TemplateArgsType,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
    cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
 | 
					    cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
 | 
				
			||||||
    serv = await cg.get_variable(config[CONF_ID])
 | 
					    serv = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg, serv, False)
 | 
					    var = cg.new_Pvariable(action_id, template_arg, serv, False)
 | 
				
			||||||
@@ -320,6 +350,24 @@ async def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
				
			|||||||
    for key, value in config[CONF_VARIABLES].items():
 | 
					    for key, value in config[CONF_VARIABLES].items():
 | 
				
			||||||
        templ = await cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
        cg.add(var.add_variable(key, templ))
 | 
					        cg.add(var.add_variable(key, templ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if response_template := config.get(CONF_RESPONSE_TEMPLATE):
 | 
				
			||||||
 | 
					        templ = await cg.templatable(response_template, args, cg.std_string)
 | 
				
			||||||
 | 
					        cg.add(var.set_response_template(templ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if on_response := config.get(CONF_ON_RESPONSE):
 | 
				
			||||||
 | 
					        cg.add_define("USE_API_HOMEASSISTANT_ACTION_RESPONSES")
 | 
				
			||||||
 | 
					        trigger = cg.new_Pvariable(
 | 
				
			||||||
 | 
					            on_response[CONF_TRIGGER_ID],
 | 
				
			||||||
 | 
					            template_arg,
 | 
				
			||||||
 | 
					            var,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        await automation.build_automation(
 | 
				
			||||||
 | 
					            trigger,
 | 
				
			||||||
 | 
					            [(cg.std_shared_ptr.template(ActionResponse), "response"), *args],
 | 
				
			||||||
 | 
					            on_response,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -780,6 +780,21 @@ 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
 | 
				
			||||||
 | 
					  string response_template = 7 [(no_zero_copy) = true]; // Optional Jinja template for response processing
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Message sent by Home Assistant to ESPHome with service call response data
 | 
				
			||||||
 | 
					message HomeassistantActionResponse {
 | 
				
			||||||
 | 
					  option (id) = 130;
 | 
				
			||||||
 | 
					  option (source) = SOURCE_CLIENT;
 | 
				
			||||||
 | 
					  option (no_delay) = true;
 | 
				
			||||||
 | 
					  option (ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32 call_id = 1; // Matches the call_id from HomeassistantActionRequest
 | 
				
			||||||
 | 
					  bool success = 2; // Whether the service call succeeded
 | 
				
			||||||
 | 
					  string error_message = 3; // Error message if success = false
 | 
				
			||||||
 | 
					  bytes response_data = 4 [(pointer_to_buffer) = true]; // Service response data
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ==================== IMPORT HOME ASSISTANT STATES ====================
 | 
					// ==================== IMPORT HOME ASSISTANT STATES ====================
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1549,6 +1549,13 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(USE_API_HOMEASSISTANT_SERVICES) && defined(USE_API_HOMEASSISTANT_ACTION_RESPONSES)
 | 
				
			||||||
 | 
					void APIConnection::on_homeassistant_action_response(const HomeassistantActionResponse &msg) {
 | 
				
			||||||
 | 
					  this->parent_->handle_action_response(msg.call_id, msg.success, msg.error_message,
 | 
				
			||||||
 | 
					                                        reinterpret_cast<const char *>(msg.response_data), msg.response_data_len);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#ifdef USE_API_NOISE
 | 
					#ifdef USE_API_NOISE
 | 
				
			||||||
bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
 | 
					bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
 | 
				
			||||||
  NoiseEncryptionSetKeyResponse resp;
 | 
					  NoiseEncryptionSetKeyResponse resp;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -137,6 +137,9 @@ 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;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -884,6 +884,8 @@ 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);
 | 
				
			||||||
 | 
					  buffer.encode_uint32(6, this->call_id);
 | 
				
			||||||
 | 
					  buffer.encode_string(7, this->response_template);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
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());
 | 
				
			||||||
@@ -891,6 +893,39 @@ 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);
 | 
				
			||||||
 | 
					  size.add_uint32(1, this->call_id);
 | 
				
			||||||
 | 
					  size.add_length(1, this->response_template.size());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
 | 
				
			||||||
 | 
					bool HomeassistantActionResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
 | 
					  switch (field_id) {
 | 
				
			||||||
 | 
					    case 1:
 | 
				
			||||||
 | 
					      this->call_id = value.as_uint32();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 2:
 | 
				
			||||||
 | 
					      this->success = value.as_bool();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool HomeassistantActionResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
				
			||||||
 | 
					  switch (field_id) {
 | 
				
			||||||
 | 
					    case 3:
 | 
				
			||||||
 | 
					      this->error_message = value.as_string();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 4: {
 | 
				
			||||||
 | 
					      // Use raw data directly to avoid allocation
 | 
				
			||||||
 | 
					      this->response_data = value.data();
 | 
				
			||||||
 | 
					      this->response_data_len = value.size();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_API_HOMEASSISTANT_STATES
 | 
					#ifdef USE_API_HOMEASSISTANT_STATES
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1104,7 +1104,7 @@ class HomeassistantServiceMap final : public ProtoMessage {
 | 
				
			|||||||
class HomeassistantActionRequest final : public ProtoMessage {
 | 
					class HomeassistantActionRequest final : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  static constexpr uint8_t MESSAGE_TYPE = 35;
 | 
					  static constexpr uint8_t MESSAGE_TYPE = 35;
 | 
				
			||||||
  static constexpr uint8_t ESTIMATED_SIZE = 113;
 | 
					  static constexpr uint8_t ESTIMATED_SIZE = 126;
 | 
				
			||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
					#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
				
			||||||
  const char *message_name() const override { return "homeassistant_action_request"; }
 | 
					  const char *message_name() const override { return "homeassistant_action_request"; }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -1114,6 +1114,8 @@ 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};
 | 
				
			||||||
 | 
					  uint32_t call_id{0};
 | 
				
			||||||
 | 
					  std::string response_template{};
 | 
				
			||||||
  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
 | 
				
			||||||
@@ -1123,6 +1125,28 @@ class HomeassistantActionRequest final : public ProtoMessage {
 | 
				
			|||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
 | 
				
			||||||
 | 
					class HomeassistantActionResponse final : public ProtoDecodableMessage {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  static constexpr uint8_t MESSAGE_TYPE = 130;
 | 
				
			||||||
 | 
					  static constexpr uint8_t ESTIMATED_SIZE = 34;
 | 
				
			||||||
 | 
					#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
				
			||||||
 | 
					  const char *message_name() const override { return "homeassistant_action_response"; }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  uint32_t call_id{0};
 | 
				
			||||||
 | 
					  bool success{false};
 | 
				
			||||||
 | 
					  std::string error_message{};
 | 
				
			||||||
 | 
					  const uint8_t *response_data{nullptr};
 | 
				
			||||||
 | 
					  uint16_t response_data_len{0};
 | 
				
			||||||
 | 
					#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
				
			||||||
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
				
			||||||
 | 
					  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#ifdef USE_API_HOMEASSISTANT_STATES
 | 
					#ifdef USE_API_HOMEASSISTANT_STATES
 | 
				
			||||||
class SubscribeHomeAssistantStatesRequest final : public ProtoMessage {
 | 
					class SubscribeHomeAssistantStatesRequest final : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1122,6 +1122,19 @@ 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);
 | 
				
			||||||
 | 
					  dump_field(out, "call_id", this->call_id);
 | 
				
			||||||
 | 
					  dump_field(out, "response_template", this->response_template);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
 | 
				
			||||||
 | 
					void HomeassistantActionResponse::dump_to(std::string &out) const {
 | 
				
			||||||
 | 
					  MessageDumpHelper helper(out, "HomeassistantActionResponse");
 | 
				
			||||||
 | 
					  dump_field(out, "call_id", this->call_id);
 | 
				
			||||||
 | 
					  dump_field(out, "success", this->success);
 | 
				
			||||||
 | 
					  dump_field(out, "error_message", this->error_message);
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -610,6 +610,17 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
				
			|||||||
      this->on_z_wave_proxy_request(msg);
 | 
					      this->on_z_wave_proxy_request(msg);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
 | 
				
			||||||
 | 
					    case HomeassistantActionResponse::MESSAGE_TYPE: {
 | 
				
			||||||
 | 
					      HomeassistantActionResponse msg;
 | 
				
			||||||
 | 
					      msg.decode(msg_data, msg_size);
 | 
				
			||||||
 | 
					#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
				
			||||||
 | 
					      ESP_LOGVV(TAG, "on_homeassistant_action_response: %s", msg.dump().c_str());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					      this->on_homeassistant_action_response(msg);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,6 +66,9 @@ 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_ACTION_RESPONSES
 | 
				
			||||||
 | 
					  virtual void on_homeassistant_action_response(const HomeassistantActionResponse &value){};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#ifdef USE_API_HOMEASSISTANT_STATES
 | 
					#ifdef USE_API_HOMEASSISTANT_STATES
 | 
				
			||||||
  virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
 | 
					  virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,12 +9,16 @@
 | 
				
			|||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
#include "esphome/core/util.h"
 | 
					#include "esphome/core/util.h"
 | 
				
			||||||
#include "esphome/core/version.h"
 | 
					#include "esphome/core/version.h"
 | 
				
			||||||
 | 
					#ifdef USE_API_HOMEASSISTANT_SERVICES
 | 
				
			||||||
 | 
					#include "homeassistant_service.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_LOGGER
 | 
					#ifdef USE_LOGGER
 | 
				
			||||||
#include "esphome/components/logger/logger.h"
 | 
					#include "esphome/components/logger/logger.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome::api {
 | 
					namespace esphome::api {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -399,6 +403,27 @@ 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) {
 | 
				
			||||||
 | 
					  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,
 | 
				
			||||||
 | 
					                                       const char *response_data, size_t response_data_len) {
 | 
				
			||||||
 | 
					  auto it = this->action_response_callbacks_.find(call_id);
 | 
				
			||||||
 | 
					  if (it != this->action_response_callbacks_.end()) {
 | 
				
			||||||
 | 
					    // Create the response object
 | 
				
			||||||
 | 
					    auto response = std::make_shared<class ActionResponse>(success, error_message, response_data, response_data_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Call the callback
 | 
				
			||||||
 | 
					    it->second(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Remove the callback as it's one-time use
 | 
				
			||||||
 | 
					    this->action_response_callbacks_.erase(it);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_API_HOMEASSISTANT_STATES
 | 
					#ifdef USE_API_HOMEASSISTANT_STATES
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@
 | 
				
			|||||||
#include "user_services.h"
 | 
					#include "user_services.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome::api {
 | 
					namespace esphome::api {
 | 
				
			||||||
@@ -110,7 +111,13 @@ class APIServer : public Component, public Controller {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
#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
 | 
				
			||||||
 | 
					  using ActionResponseCallback = std::function<void(std::shared_ptr<class ActionResponse>)>;
 | 
				
			||||||
 | 
					  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,
 | 
				
			||||||
 | 
					                              const char *response_data, size_t response_data_len);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#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); }
 | 
				
			||||||
@@ -187,6 +194,9 @@ 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_ACTION_RESPONSES
 | 
				
			||||||
 | 
					  std::map<uint32_t, ActionResponseCallback> action_response_callbacks_;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Group smaller types together
 | 
					  // Group smaller types together
 | 
				
			||||||
  uint16_t port_{6053};
 | 
					  uint16_t port_{6053};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,11 @@
 | 
				
			|||||||
#include "api_server.h"
 | 
					#include "api_server.h"
 | 
				
			||||||
#ifdef USE_API
 | 
					#ifdef USE_API
 | 
				
			||||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
 | 
					#ifdef USE_API_HOMEASSISTANT_SERVICES
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include "api_pb2.h"
 | 
					#include "api_pb2.h"
 | 
				
			||||||
 | 
					#include "esphome/components/json/json_util.h"
 | 
				
			||||||
#include "esphome/core/automation.h"
 | 
					#include "esphome/core/automation.h"
 | 
				
			||||||
#include "esphome/core/helpers.h"
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,6 +47,45 @@ 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
 | 
				
			||||||
 | 
					class ActionResponse {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  ActionResponse(bool success, std::string error_message, const char *data, size_t data_len)
 | 
				
			||||||
 | 
					      : success_(success), error_message_(std::move(error_message)), data_(data), data_len_(data_len) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool is_success() const { return this->success_; }
 | 
				
			||||||
 | 
					  const std::string &get_error_message() const { return this->error_message_; }
 | 
				
			||||||
 | 
					  const char *get_data() const { return this->data_; }
 | 
				
			||||||
 | 
					  size_t get_data_len() const { return this->data_len_; }
 | 
				
			||||||
 | 
					  // Get data as parsed JSON object
 | 
				
			||||||
 | 
					  // Returns unbound JsonObject if data is empty or invalid JSON
 | 
				
			||||||
 | 
					  JsonObject get_json() {
 | 
				
			||||||
 | 
					    if (this->data_len_ == 0)
 | 
				
			||||||
 | 
					      return JsonObject();  // Return unbound JsonObject if no data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!this->parsed_json_) {
 | 
				
			||||||
 | 
					      this->json_document_ = json::parse_json(this->data_, this->data_len_);
 | 
				
			||||||
 | 
					      this->json_ = this->json_document_.as<JsonObject>();
 | 
				
			||||||
 | 
					      this->parsed_json_ = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return this->json_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  bool success_;
 | 
				
			||||||
 | 
					  std::string error_message_;
 | 
				
			||||||
 | 
					  const char *data_;
 | 
				
			||||||
 | 
					  size_t data_len_;
 | 
				
			||||||
 | 
					  JsonDocument json_document_;
 | 
				
			||||||
 | 
					  JsonObject json_;
 | 
				
			||||||
 | 
					  bool parsed_json_{false};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Callback type for action responses
 | 
				
			||||||
 | 
					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:
 | 
				
			||||||
  explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent), is_event_(is_event) {}
 | 
					  explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent), is_event_(is_event) {}
 | 
				
			||||||
@@ -61,6 +103,18 @@ 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) {
 | 
				
			||||||
 | 
					    this->response_template_ = response_template;
 | 
				
			||||||
 | 
					    this->has_response_template_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_response_callback(ActionResponseCallback<Ts...> callback) {
 | 
				
			||||||
 | 
					    this->wants_response_ = true;
 | 
				
			||||||
 | 
					    this->response_callback_ = callback;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void play(Ts... x) override {
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
    HomeassistantActionRequest resp;
 | 
					    HomeassistantActionRequest resp;
 | 
				
			||||||
    std::string service_value = this->service_.value(x...);
 | 
					    std::string service_value = this->service_.value(x...);
 | 
				
			||||||
@@ -84,6 +138,27 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
 | 
				
			|||||||
      kv.set_key(StringRef(it.key));
 | 
					      kv.set_key(StringRef(it.key));
 | 
				
			||||||
      kv.value = it.value.value(x...);
 | 
					      kv.value = it.value.value(x...);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
 | 
				
			||||||
 | 
					    if (this->wants_response_) {
 | 
				
			||||||
 | 
					      // Generate a unique call ID for this service call
 | 
				
			||||||
 | 
					      static uint32_t call_id_counter = 1;
 | 
				
			||||||
 | 
					      uint32_t call_id = call_id_counter++;
 | 
				
			||||||
 | 
					      resp.call_id = call_id;
 | 
				
			||||||
 | 
					      // Set response template if provided
 | 
				
			||||||
 | 
					      if (this->has_response_template_) {
 | 
				
			||||||
 | 
					        std::string response_template_value = this->response_template_.value(x...);
 | 
				
			||||||
 | 
					        resp.response_template = response_template_value;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      auto captured_args = std::make_tuple(x...);
 | 
				
			||||||
 | 
					      this->parent_->register_action_response_callback(call_id, [this, captured_args](
 | 
				
			||||||
 | 
					                                                                    std::shared_ptr<ActionResponse> response) {
 | 
				
			||||||
 | 
					        std::apply([this, &response](auto &&...args) { this->response_callback_(response, args...); }, captured_args);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this->parent_->send_homeassistant_action(resp);
 | 
					    this->parent_->send_homeassistant_action(resp);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -94,8 +169,25 @@ 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_{""};
 | 
				
			||||||
 | 
					  ActionResponseCallback<Ts...> response_callback_;
 | 
				
			||||||
 | 
					  bool wants_response_{false};
 | 
				
			||||||
 | 
					  bool has_response_template_{false};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
 | 
				
			||||||
 | 
					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...); });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace esphome::api
 | 
					}  // namespace esphome::api
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@ bool parse_json(const std::string &data, const json_parse_t &f) {
 | 
				
			|||||||
  // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
 | 
					  // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
JsonDocument parse_json(const std::string &data) {
 | 
					JsonDocument parse_json(const char *data, size_t len) {
 | 
				
			||||||
  // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
 | 
					  // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
 | 
				
			||||||
#ifdef USE_PSRAM
 | 
					#ifdef USE_PSRAM
 | 
				
			||||||
  auto doc_allocator = SpiRamAllocator();
 | 
					  auto doc_allocator = SpiRamAllocator();
 | 
				
			||||||
@@ -38,12 +38,12 @@ JsonDocument parse_json(const std::string &data) {
 | 
				
			|||||||
    ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
 | 
					    ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
 | 
				
			||||||
    return JsonObject();  // return unbound object
 | 
					    return JsonObject();  // return unbound object
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  DeserializationError err = deserializeJson(json_document, data);
 | 
					  DeserializationError err = deserializeJson(json_document, data, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (err == DeserializationError::Ok) {
 | 
					  if (err == DeserializationError::Ok) {
 | 
				
			||||||
    return json_document;
 | 
					    return json_document;
 | 
				
			||||||
  } else if (err == DeserializationError::NoMemory) {
 | 
					  } else if (err == DeserializationError::NoMemory) {
 | 
				
			||||||
    ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
 | 
					    ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source buffer smaller");
 | 
				
			||||||
    return JsonObject();  // return unbound object
 | 
					    return JsonObject();  // return unbound object
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ESP_LOGE(TAG, "Parse error: %s", err.c_str());
 | 
					  ESP_LOGE(TAG, "Parse error: %s", err.c_str());
 | 
				
			||||||
@@ -51,6 +51,8 @@ JsonDocument parse_json(const std::string &data) {
 | 
				
			|||||||
  // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
 | 
					  // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					JsonDocument parse_json(const std::string &data) { return parse_json(data.c_str(), data.size()); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string JsonBuilder::serialize() {
 | 
					std::string JsonBuilder::serialize() {
 | 
				
			||||||
  if (doc_.overflowed()) {
 | 
					  if (doc_.overflowed()) {
 | 
				
			||||||
    ESP_LOGE(TAG, "JSON document overflow");
 | 
					    ESP_LOGE(TAG, "JSON document overflow");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,6 +51,8 @@ std::string build_json(const json_build_t &f);
 | 
				
			|||||||
bool parse_json(const std::string &data, const json_parse_t &f);
 | 
					bool parse_json(const std::string &data, const json_parse_t &f);
 | 
				
			||||||
/// Parse a JSON string and return the root JsonDocument (or an unbound object on error)
 | 
					/// Parse a JSON string and return the root JsonDocument (or an unbound object on error)
 | 
				
			||||||
JsonDocument parse_json(const std::string &data);
 | 
					JsonDocument parse_json(const std::string &data);
 | 
				
			||||||
 | 
					/// Parse JSON from a buffer and return the root JsonDocument (or an unbound object on error)
 | 
				
			||||||
 | 
					JsonDocument parse_json(const char *data, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Builder class for creating JSON documents without lambdas
 | 
					/// Builder class for creating JSON documents without lambdas
 | 
				
			||||||
class JsonBuilder {
 | 
					class JsonBuilder {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -817,6 +817,7 @@ CONF_RESET_DURATION = "reset_duration"
 | 
				
			|||||||
CONF_RESET_PIN = "reset_pin"
 | 
					CONF_RESET_PIN = "reset_pin"
 | 
				
			||||||
CONF_RESIZE = "resize"
 | 
					CONF_RESIZE = "resize"
 | 
				
			||||||
CONF_RESOLUTION = "resolution"
 | 
					CONF_RESOLUTION = "resolution"
 | 
				
			||||||
 | 
					CONF_RESPONSE_TEMPLATE = "response_template"
 | 
				
			||||||
CONF_RESTART = "restart"
 | 
					CONF_RESTART = "restart"
 | 
				
			||||||
CONF_RESTORE = "restore"
 | 
					CONF_RESTORE = "restore"
 | 
				
			||||||
CONF_RESTORE_MODE = "restore_mode"
 | 
					CONF_RESTORE_MODE = "restore_mode"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,6 +111,7 @@
 | 
				
			|||||||
#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_SERVICES
 | 
					#define USE_API_HOMEASSISTANT_SERVICES
 | 
				
			||||||
 | 
					#define USE_API_HOMEASSISTANT_ACTION_RESPONSES
 | 
				
			||||||
#define USE_API_HOMEASSISTANT_STATES
 | 
					#define USE_API_HOMEASSISTANT_STATES
 | 
				
			||||||
#define USE_API_NOISE
 | 
					#define USE_API_NOISE
 | 
				
			||||||
#define USE_API_PLAINTEXT
 | 
					#define USE_API_PLAINTEXT
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,36 @@ esphome:
 | 
				
			|||||||
          data:
 | 
					          data:
 | 
				
			||||||
            message: Button was pressed
 | 
					            message: Button was pressed
 | 
				
			||||||
      - homeassistant.tag_scanned: pulse
 | 
					      - homeassistant.tag_scanned: pulse
 | 
				
			||||||
 | 
					      - homeassistant.action:
 | 
				
			||||||
 | 
					          action: weather.get_forecasts
 | 
				
			||||||
 | 
					          data:
 | 
				
			||||||
 | 
					            entity_id: weather.forecast_home
 | 
				
			||||||
 | 
					            type: hourly
 | 
				
			||||||
 | 
					          on_response:
 | 
				
			||||||
 | 
					            - lambda: |-
 | 
				
			||||||
 | 
					                if (response->is_success()) {
 | 
				
			||||||
 | 
					                  JsonObject json = response->get_json();
 | 
				
			||||||
 | 
					                  JsonObject next_hour = json["response"]["weather.forecast_home"]["forecast"][0];
 | 
				
			||||||
 | 
					                  float next_temperature = next_hour["temperature"].as<float>();
 | 
				
			||||||
 | 
					                  ESP_LOGD("main", "Next hour temperature: %f", next_temperature);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                  ESP_LOGE("main", "Action failed: %s", response->get_error_message().c_str());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					      - homeassistant.action:
 | 
				
			||||||
 | 
					          action: weather.get_forecasts
 | 
				
			||||||
 | 
					          data:
 | 
				
			||||||
 | 
					            entity_id: weather.forecast_home
 | 
				
			||||||
 | 
					            type: hourly
 | 
				
			||||||
 | 
					          response_template: "{{ response['weather.forecast_home']['forecast'][0]['temperature'] }}"
 | 
				
			||||||
 | 
					          on_response:
 | 
				
			||||||
 | 
					            - lambda: |-
 | 
				
			||||||
 | 
					                if (response->is_success()) {
 | 
				
			||||||
 | 
					                  JsonObject json = response->get_json();
 | 
				
			||||||
 | 
					                  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