bitpack api flags

This commit is contained in:
J. Nick Koston
2025-06-26 12:46:57 +02:00
parent 740dcd72a2
commit 8895c8a987
3 changed files with 62 additions and 44 deletions

View File

@@ -90,10 +90,10 @@ APIConnection::~APIConnection() {
} }
void APIConnection::loop() { void APIConnection::loop() {
if (this->next_close_) { if (this->flags_.next_close) {
// requested a disconnect // requested a disconnect
this->helper_->close(); this->helper_->close();
this->remove_ = true; this->flags_.remove = true;
return; return;
} }
@@ -134,15 +134,14 @@ void APIConnection::loop() {
} else { } else {
this->read_message(0, buffer.type, nullptr); this->read_message(0, buffer.type, nullptr);
} }
if (this->remove_) if (this->flags_.remove)
return; return;
} }
} }
} }
// Process deferred batch if scheduled // Process deferred batch if scheduled
if (this->deferred_batch_.batch_scheduled && if (this->flags_.batch_scheduled && now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
this->process_batch_(); this->process_batch_();
} }
@@ -152,7 +151,7 @@ void APIConnection::loop() {
this->initial_state_iterator_.advance(); this->initial_state_iterator_.advance();
} }
if (this->sent_ping_) { if (this->flags_.sent_ping) {
// Disconnect if not responded within 2.5*keepalive // Disconnect if not responded within 2.5*keepalive
if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) { if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
on_fatal_error(); on_fatal_error();
@@ -160,13 +159,13 @@ void APIConnection::loop() {
} }
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) { } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
ESP_LOGVV(TAG, "Sending keepalive PING"); ESP_LOGVV(TAG, "Sending keepalive PING");
this->sent_ping_ = this->send_message(PingRequest()); this->flags_.sent_ping = this->send_message(PingRequest());
if (!this->sent_ping_) { if (!this->flags_.sent_ping) {
// If we can't send the ping request directly (tx_buffer full), // If we can't send the ping request directly (tx_buffer full),
// schedule it at the front of the batch so it will be sent with priority // schedule it at the front of the batch so it will be sent with priority
ESP_LOGW(TAG, "Buffer full, ping queued"); ESP_LOGW(TAG, "Buffer full, ping queued");
this->schedule_message_front_(nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE); this->schedule_message_front_(nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE);
this->sent_ping_ = true; // Mark as sent to avoid scheduling multiple pings this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
} }
} }
@@ -226,13 +225,13 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
// don't close yet, we still need to send the disconnect response // don't close yet, we still need to send the disconnect response
// close will happen on next loop // close will happen on next loop
ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str()); ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
this->next_close_ = true; this->flags_.next_close = true;
DisconnectResponse resp; DisconnectResponse resp;
return resp; return resp;
} }
void APIConnection::on_disconnect_response(const DisconnectResponse &value) { void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
this->helper_->close(); this->helper_->close();
this->remove_ = true; this->flags_.remove = true;
} }
// Encodes a message to the buffer and returns the total number of bytes used, // Encodes a message to the buffer and returns the total number of bytes used,
@@ -1158,7 +1157,7 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) { void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_) if (!this->flags_.state_subscription)
return; return;
if (this->image_reader_.available()) if (this->image_reader_.available())
return; return;
@@ -1512,7 +1511,7 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
#endif #endif
bool APIConnection::try_send_log_message(int level, const char *tag, const char *line) { bool APIConnection::try_send_log_message(int level, const char *tag, const char *line) {
if (this->log_subscription_ < level) if (this->flags_.log_subscription < level)
return false; return false;
// Pre-calculate message size to avoid reallocations // Pre-calculate message size to avoid reallocations
@@ -1552,7 +1551,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name(); resp.name = App.get_name();
this->connection_state_ = ConnectionState::CONNECTED; this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::CONNECTED);
return resp; return resp;
} }
ConnectResponse APIConnection::connect(const ConnectRequest &msg) { ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
@@ -1563,7 +1562,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
resp.invalid_password = !correct; resp.invalid_password = !correct;
if (correct) { if (correct) {
ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str()); ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
this->connection_state_ = ConnectionState::AUTHENTICATED; this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->helper_->getpeername()); this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->helper_->getpeername());
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME
if (homeassistant::global_homeassistant_time != nullptr) { if (homeassistant::global_homeassistant_time != nullptr) {
@@ -1677,7 +1676,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
state_subs_at_ = 0; state_subs_at_ = 0;
} }
bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
if (this->remove_) if (this->flags_.remove)
return false; return false;
if (this->helper_->can_write_without_blocking()) if (this->helper_->can_write_without_blocking())
return true; return true;
@@ -1727,7 +1726,7 @@ void APIConnection::on_no_setup_connection() {
} }
void APIConnection::on_fatal_error() { void APIConnection::on_fatal_error() {
this->helper_->close(); this->helper_->close();
this->remove_ = true; this->flags_.remove = true;
} }
void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) { void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
@@ -1752,8 +1751,8 @@ void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCre
} }
bool APIConnection::schedule_batch_() { bool APIConnection::schedule_batch_() {
if (!this->deferred_batch_.batch_scheduled) { if (!this->flags_.batch_scheduled) {
this->deferred_batch_.batch_scheduled = true; this->flags_.batch_scheduled = true;
this->deferred_batch_.batch_start_time = App.get_loop_component_start_time(); this->deferred_batch_.batch_start_time = App.get_loop_component_start_time();
} }
return true; return true;
@@ -1769,7 +1768,7 @@ ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
void APIConnection::process_batch_() { void APIConnection::process_batch_() {
if (this->deferred_batch_.empty()) { if (this->deferred_batch_.empty()) {
this->deferred_batch_.batch_scheduled = false; this->flags_.batch_scheduled = false;
return; return;
} }

View File

@@ -125,7 +125,7 @@ class APIConnection : public APIServerConnection {
#endif #endif
bool try_send_log_message(int level, const char *tag, const char *line); bool try_send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
if (!this->service_call_subscription_) if (!this->flags_.service_call_subscription)
return; return;
this->send_message(call); this->send_message(call);
} }
@@ -185,7 +185,7 @@ class APIConnection : public APIServerConnection {
void on_disconnect_response(const DisconnectResponse &value) override; void on_disconnect_response(const DisconnectResponse &value) override;
void on_ping_response(const PingResponse &value) override { void on_ping_response(const PingResponse &value) override {
// we initiated ping // we initiated ping
this->sent_ping_ = false; this->flags_.sent_ping = false;
} }
void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override; void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override;
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME
@@ -198,16 +198,16 @@ class APIConnection : public APIServerConnection {
DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override; DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override;
void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); } void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); }
void subscribe_states(const SubscribeStatesRequest &msg) override { void subscribe_states(const SubscribeStatesRequest &msg) override {
this->state_subscription_ = true; this->flags_.state_subscription = true;
this->initial_state_iterator_.begin(); this->initial_state_iterator_.begin();
} }
void subscribe_logs(const SubscribeLogsRequest &msg) override { void subscribe_logs(const SubscribeLogsRequest &msg) override {
this->log_subscription_ = msg.level; this->flags_.log_subscription = msg.level;
if (msg.dump_config) if (msg.dump_config)
App.schedule_dump_config(); App.schedule_dump_config();
} }
void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override { void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override {
this->service_call_subscription_ = true; this->flags_.service_call_subscription = true;
} }
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override; void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
GetTimeResponse get_time(const GetTimeRequest &msg) override { GetTimeResponse get_time(const GetTimeRequest &msg) override {
@@ -219,9 +219,12 @@ class APIConnection : public APIServerConnection {
NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override; NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override;
#endif #endif
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; } bool is_authenticated() override {
return static_cast<ConnectionState>(this->flags_.connection_state) == ConnectionState::AUTHENTICATED;
}
bool is_connection_setup() override { bool is_connection_setup() override {
return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated(); return static_cast<ConnectionState>(this->flags_.connection_state) == ConnectionState::CONNECTED ||
this->is_authenticated();
} }
void on_fatal_error() override; void on_fatal_error() override;
void on_unauthenticated_access() override; void on_unauthenticated_access() override;
@@ -460,19 +463,37 @@ class APIConnection : public APIServerConnection {
uint16_t client_api_version_major_{0}; uint16_t client_api_version_major_{0};
uint16_t client_api_version_minor_{0}; uint16_t client_api_version_minor_{0};
// Group all 1-byte types together to minimize padding // Connection state enum
enum class ConnectionState : uint8_t { enum class ConnectionState : uint8_t {
WAITING_FOR_HELLO, WAITING_FOR_HELLO = 0,
CONNECTED, CONNECTED = 1,
AUTHENTICATED, AUTHENTICATED = 2,
} connection_state_{ConnectionState::WAITING_FOR_HELLO}; };
uint8_t log_subscription_{ESPHOME_LOG_LEVEL_NONE};
bool remove_{false}; // Group all 1-byte types together to minimize padding
bool state_subscription_{false}; struct APIFlags {
bool sent_ping_{false}; uint8_t connection_state : 2; // ConnectionState only needs 2 bits (3 states)
bool service_call_subscription_{false}; uint8_t log_subscription : 3; // Log levels 0-7 need 3 bits
bool next_close_ = false; uint8_t remove : 1;
// 7 bytes used, 1 byte padding uint8_t state_subscription : 1;
uint8_t sent_ping : 1;
uint8_t service_call_subscription : 1;
uint8_t next_close : 1;
uint8_t batch_scheduled : 1; // Moved from DeferredBatch
uint8_t reserved : 5; // Reserved for future use
APIFlags()
: connection_state(0),
log_subscription(ESPHOME_LOG_LEVEL_NONE),
remove(0),
state_subscription(0),
sent_ping(0),
service_call_subscription(0),
next_close(0),
batch_scheduled(0),
reserved(0) {}
} flags_; // 2 bytes total instead of 7+ bytes
// Larger objects at the end // Larger objects at the end
InitialStateIterator initial_state_iterator_; InitialStateIterator initial_state_iterator_;
@@ -590,7 +611,6 @@ class APIConnection : public APIServerConnection {
std::vector<BatchItem> items; std::vector<BatchItem> items;
uint32_t batch_start_time{0}; uint32_t batch_start_time{0};
bool batch_scheduled{false};
DeferredBatch() { DeferredBatch() {
// Pre-allocate capacity for typical batch sizes to avoid reallocation // Pre-allocate capacity for typical batch sizes to avoid reallocation
@@ -603,7 +623,6 @@ class APIConnection : public APIServerConnection {
void add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type); void add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type);
void clear() { void clear() {
items.clear(); items.clear();
batch_scheduled = false;
batch_start_time = 0; batch_start_time = 0;
} }
bool empty() const { return items.empty(); } bool empty() const { return items.empty(); }

View File

@@ -104,7 +104,7 @@ void APIServer::setup() {
return; return;
} }
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (!c->remove_) if (!c->flags_.remove)
c->try_send_log_message(level, tag, message); c->try_send_log_message(level, tag, message);
} }
}); });
@@ -116,7 +116,7 @@ void APIServer::setup() {
esp32_camera::global_esp32_camera->add_image_callback( esp32_camera::global_esp32_camera->add_image_callback(
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) { [this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (!c->remove_) if (!c->flags_.remove)
c->set_camera_state(image); c->set_camera_state(image);
} }
}); });
@@ -176,7 +176,7 @@ void APIServer::loop() {
while (client_index < this->clients_.size()) { while (client_index < this->clients_.size()) {
auto &client = this->clients_[client_index]; auto &client = this->clients_[client_index];
if (!client->remove_) { if (!client->flags_.remove) {
// Common case: process active client // Common case: process active client
client->loop(); client->loop();
client_index++; client_index++;