Merge branch 'dev' into optional_api

This commit is contained in:
J. Nick Koston 2025-07-25 21:36:44 -10:00 committed by GitHub
commit 369b5e6c86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 67 additions and 4 deletions

View File

@ -53,6 +53,7 @@ SERVICE_ARG_NATIVE_TYPES = {
CONF_ENCRYPTION = "encryption"
CONF_BATCH_DELAY = "batch_delay"
CONF_CUSTOM_SERVICES = "custom_services"
CONF_HOMEASSISTANT_STATES = "homeassistant_states"
def validate_encryption_key(value):
@ -118,6 +119,7 @@ CONFIG_SCHEMA = cv.All(
cv.Range(max=cv.TimePeriod(milliseconds=65535)),
),
cv.Optional(CONF_CUSTOM_SERVICES, default=False): cv.boolean,
cv.Optional(CONF_HOMEASSISTANT_STATES, default=False): cv.boolean,
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
single=True
),
@ -146,6 +148,9 @@ async def to_code(config):
if config.get(CONF_ACTIONS) or config[CONF_CUSTOM_SERVICES]:
cg.add_define("USE_API_SERVICES")
if config[CONF_HOMEASSISTANT_STATES]:
cg.add_define("USE_API_HOMEASSISTANT_STATES")
if actions := config.get(CONF_ACTIONS, []):
for conf in actions:
template_args = []

View File

@ -781,11 +781,13 @@ message HomeassistantServiceResponse {
message SubscribeHomeAssistantStatesRequest {
option (id) = 38;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
}
message SubscribeHomeAssistantStateResponse {
option (id) = 39;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
string entity_id = 1;
string attribute = 2;
bool once = 3;
@ -795,6 +797,7 @@ message HomeAssistantStateResponse {
option (id) = 40;
option (source) = SOURCE_CLIENT;
option (no_delay) = true;
option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
string entity_id = 1;
string state = 2;

View File

@ -242,9 +242,11 @@ void APIConnection::loop() {
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
if (state_subs_at_ >= 0) {
this->process_state_subscriptions_();
}
#endif
}
bool APIConnection::send_disconnect_response(const DisconnectRequest &msg) {
@ -1494,6 +1496,7 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
return this->send_message(resp, DeviceInfoResponse::MESSAGE_TYPE);
}
#ifdef USE_API_HOMEASSISTANT_STATES
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
for (auto &it : this->parent_->get_state_subs()) {
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
@ -1501,6 +1504,7 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
}
}
}
#endif
#ifdef USE_API_SERVICES
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
bool found = false;
@ -1532,9 +1536,11 @@ bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryption
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
state_subs_at_ = 0;
}
#endif
bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
if (this->flags_.remove)
return false;
@ -1572,10 +1578,12 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
// Do not set last_traffic_ on send
return true;
}
#ifdef USE_API_PASSWORD
void APIConnection::on_unauthenticated_access() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s access without authentication", this->get_client_combined_info().c_str());
}
#endif
void APIConnection::on_no_setup_connection() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s access without full connection", this->get_client_combined_info().c_str());

View File

@ -188,7 +188,9 @@ class APIConnection : public APIServerConnection {
// we initiated ping
this->flags_.sent_ping = false;
}
#ifdef USE_API_HOMEASSISTANT_STATES
void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override;
#endif
#ifdef USE_HOMEASSISTANT_TIME
void on_get_time_response(const GetTimeResponse &value) override;
#endif
@ -210,7 +212,9 @@ class APIConnection : public APIServerConnection {
void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override {
this->flags_.service_call_subscription = true;
}
#ifdef USE_API_HOMEASSISTANT_STATES
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
#endif
bool send_get_time_response(const GetTimeRequest &msg) override;
#ifdef USE_API_SERVICES
void execute_service(const ExecuteServiceRequest &msg) override;
@ -228,7 +232,9 @@ class APIConnection : public APIServerConnection {
}
uint8_t get_log_subscription_level() const { return this->flags_.log_subscription; }
void on_fatal_error() override;
#ifdef USE_API_PASSWORD
void on_unauthenticated_access() override;
#endif
void on_no_setup_connection() override;
ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
// FIXME: ensure no recursive writes can happen
@ -495,7 +501,9 @@ class APIConnection : public APIServerConnection {
// Group 4: 4-byte types
uint32_t last_traffic_;
#ifdef USE_API_HOMEASSISTANT_STATES
int state_subs_at_ = -1;
#endif
// Function pointer type for message encoding
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);

View File

@ -871,6 +871,7 @@ void HomeassistantServiceResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_repeated_message(total_size, 1, this->variables);
ProtoSize::add_bool_field(total_size, 1, this->is_event);
}
#ifdef USE_API_HOMEASSISTANT_STATES
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->entity_id_ref_);
buffer.encode_string(2, this->attribute_ref_);
@ -897,6 +898,7 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
}
return true;
}
#endif
bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1:

View File

@ -1092,6 +1092,7 @@ class HomeassistantServiceResponse : public ProtoMessage {
protected:
};
#ifdef USE_API_HOMEASSISTANT_STATES
class SubscribeHomeAssistantStatesRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 38;
@ -1142,6 +1143,7 @@ class HomeAssistantStateResponse : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
#endif
class GetTimeRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 36;

View File

@ -1066,6 +1066,7 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const {
}
dump_field(out, "is_event", this->is_event);
}
#ifdef USE_API_HOMEASSISTANT_STATES
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
out.append("SubscribeHomeAssistantStatesRequest {}");
}
@ -1081,6 +1082,7 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
dump_field(out, "state", this->state);
dump_field(out, "attribute", this->attribute);
}
#endif
void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); }
void GetTimeResponse::dump_to(std::string &out) const { dump_field(out, "epoch_seconds", this->epoch_seconds); }
#ifdef USE_API_SERVICES

View File

@ -176,6 +176,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_get_time_response(msg);
break;
}
#ifdef USE_API_HOMEASSISTANT_STATES
case SubscribeHomeAssistantStatesRequest::MESSAGE_TYPE: {
SubscribeHomeAssistantStatesRequest msg;
msg.decode(msg_data, msg_size);
@ -185,6 +186,8 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_subscribe_home_assistant_states_request(msg);
break;
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
case HomeAssistantStateResponse::MESSAGE_TYPE: {
HomeAssistantStateResponse msg;
msg.decode(msg_data, msg_size);
@ -194,6 +197,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_home_assistant_state_response(msg);
break;
}
#endif
#ifdef USE_API_SERVICES
case ExecuteServiceRequest::MESSAGE_TYPE: {
ExecuteServiceRequest msg;
@ -641,11 +645,13 @@ void APIServerConnection::on_subscribe_homeassistant_services_request(
this->subscribe_homeassistant_services(msg);
}
}
#ifdef USE_API_HOMEASSISTANT_STATES
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
if (this->check_authenticated_()) {
this->subscribe_home_assistant_states(msg);
}
}
#endif
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
if (this->check_connection_setup_() && !this->send_get_time_response(msg)) {
this->on_fatal_error();

View File

@ -62,9 +62,13 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
#ifdef USE_API_HOMEASSISTANT_STATES
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){};
#endif
virtual void on_get_time_request(const GetTimeRequest &value){};
virtual void on_get_time_response(const GetTimeResponse &value){};
@ -215,7 +219,9 @@ class APIServerConnection : public APIServerConnectionBase {
virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0;
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
#ifdef USE_API_HOMEASSISTANT_STATES
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
#endif
virtual bool send_get_time_response(const GetTimeRequest &msg) = 0;
#ifdef USE_API_SERVICES
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
@ -333,7 +339,9 @@ class APIServerConnection : public APIServerConnectionBase {
void on_subscribe_states_request(const SubscribeStatesRequest &msg) override;
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
#ifdef USE_API_HOMEASSISTANT_STATES
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
#endif
void on_get_time_request(const GetTimeRequest &msg) override;
#ifdef USE_API_SERVICES
void on_execute_service_request(const ExecuteServiceRequest &msg) override;

View File

@ -375,6 +375,7 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
}
}
#ifdef USE_API_HOMEASSISTANT_STATES
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->state_subs_.push_back(HomeAssistantStateSubscription{
@ -398,6 +399,7 @@ void APIServer::get_home_assistant_state(std::string entity_id, optional<std::st
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
return this->state_subs_;
}
#endif
uint16_t APIServer::get_port() const { return this->port_; }

View File

@ -126,6 +126,7 @@ class APIServer : public Component, public Controller {
bool is_connected() const;
#ifdef USE_API_HOMEASSISTANT_STATES
struct HomeAssistantStateSubscription {
std::string entity_id;
optional<std::string> attribute;
@ -138,6 +139,7 @@ class APIServer : public Component, public Controller {
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f);
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
#endif
#ifdef USE_API_SERVICES
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
#endif
@ -171,7 +173,9 @@ class APIServer : public Component, public Controller {
std::string password_;
#endif
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
#ifdef USE_API_HOMEASSISTANT_STATES
std::vector<HomeAssistantStateSubscription> state_subs_;
#endif
#ifdef USE_API_SERVICES
std::vector<UserServiceDescriptor *> user_services_;
#endif

View File

@ -83,6 +83,7 @@ class CustomAPIDevice {
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
*
* Usage:
@ -134,6 +135,7 @@ class CustomAPIDevice {
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
}
#endif
/** Call a Home Assistant service from ESPHome.
*

View File

@ -859,7 +859,9 @@ class ProtoService {
virtual bool is_authenticated() = 0;
virtual bool is_connection_setup() = 0;
virtual void on_fatal_error() = 0;
#ifdef USE_API_PASSWORD
virtual void on_unauthenticated_access() = 0;
#endif
virtual void on_no_setup_connection() = 0;
/**
* Create a buffer with a reserved size.
@ -897,6 +899,7 @@ class ProtoService {
}
bool check_authenticated_() {
#ifdef USE_API_PASSWORD
if (!this->check_connection_setup_()) {
return false;
}
@ -905,6 +908,9 @@ class ProtoService {
return false;
}
return true;
#else
return this->check_connection_setup_();
#endif
}
};

View File

@ -38,3 +38,4 @@ def setup_home_assistant_entity(var, config):
cg.add(var.set_entity_id(config[CONF_ENTITY_ID]))
if CONF_ATTRIBUTE in config:
cg.add(var.set_attribute(config[CONF_ATTRIBUTE]))
cg.add_define("USE_API_HOMEASSISTANT_STATES")

View File

@ -151,13 +151,13 @@ void IDFI2CBus::dump_config() {
break;
}
if (this->scan_) {
ESP_LOGI(TAG, "Results from bus scan:");
ESP_LOGCONFIG(TAG, "Results from bus scan:");
if (scan_results_.empty()) {
ESP_LOGI(TAG, "Found no devices");
ESP_LOGCONFIG(TAG, "Found no devices");
} else {
for (const auto &s : scan_results_) {
if (s.second) {
ESP_LOGI(TAG, "Found device at address 0x%02X", s.first);
ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", s.first);
} else {
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
}

View File

@ -421,6 +421,7 @@ CONF_LOGGER_LOG = "logger.log"
LOGGER_LOG_ACTION_SCHEMA = cv.All(
cv.maybe_simple_value(
{
cv.GenerateID(CONF_LOGGER_ID): cv.use_id(Logger),
cv.Required(CONF_FORMAT): cv.string,
cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
cv.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(

View File

@ -109,6 +109,7 @@
#define USE_API
#define USE_API_CLIENT_CONNECTED_TRIGGER
#define USE_API_CLIENT_DISCONNECTED_TRIGGER
#define USE_API_HOMEASSISTANT_STATES
#define USE_API_NOISE
#define USE_API_PLAINTEXT
#define USE_API_SERVICES

View File

@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
esptool==4.9.0
click==8.1.7
esphome-dashboard==20250514.0
aioesphomeapi==37.0.4
aioesphomeapi==37.1.0
zeroconf==0.147.0
puremagic==1.30
ruamel.yaml==0.18.14 # dashboard_import

View File

@ -4,6 +4,8 @@ esphome:
esp32:
board: esp32dev
logger:
text:
- platform: template
name: "test 1 text"