mirror of
https://github.com/esphome/esphome.git
synced 2025-08-06 18:37:47 +00:00
Merge remote-tracking branch 'upstream/dev' into idf_webserver_ota
This commit is contained in:
commit
22d1a18d22
@ -332,6 +332,7 @@ esphome/components/pca6416a/* @Mat931
|
||||
esphome/components/pca9554/* @clydebarrow @hwstar
|
||||
esphome/components/pcf85063/* @brogon
|
||||
esphome/components/pcf8563/* @KoenBreeman
|
||||
esphome/components/pi4ioe5v6408/* @jesserockz
|
||||
esphome/components/pid/* @OttoWinter
|
||||
esphome/components/pipsolar/* @andreashergert1984
|
||||
esphome/components/pm1006/* @habbie
|
||||
|
@ -110,9 +110,10 @@ CONFIG_SCHEMA = cv.All(
|
||||
): ACTIONS_SCHEMA,
|
||||
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
|
||||
cv.Optional(CONF_ENCRYPTION): _encryption_schema,
|
||||
cv.Optional(
|
||||
CONF_BATCH_DELAY, default="100ms"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_BATCH_DELAY, default="100ms"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(max=cv.TimePeriod(milliseconds=65535)),
|
||||
),
|
||||
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
||||
single=True
|
||||
),
|
||||
|
@ -93,21 +93,21 @@ APIConnection::~APIConnection() {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void APIConnection::log_batch_item_(const DeferredBatch::BatchItem &item) {
|
||||
// Set log-only mode
|
||||
this->log_only_mode_ = true;
|
||||
this->flags_.log_only_mode = true;
|
||||
|
||||
// Call the creator - it will create the message and log it via encode_message_to_buffer
|
||||
item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true, item.message_type);
|
||||
|
||||
// Clear log-only mode
|
||||
this->log_only_mode_ = false;
|
||||
this->flags_.log_only_mode = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void APIConnection::loop() {
|
||||
if (this->next_close_) {
|
||||
if (this->flags_.next_close) {
|
||||
// requested a disconnect
|
||||
this->helper_->close();
|
||||
this->remove_ = true;
|
||||
this->flags_.remove = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -148,15 +148,14 @@ void APIConnection::loop() {
|
||||
} else {
|
||||
this->read_message(0, buffer.type, nullptr);
|
||||
}
|
||||
if (this->remove_)
|
||||
if (this->flags_.remove)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process deferred batch if scheduled
|
||||
if (this->deferred_batch_.batch_scheduled &&
|
||||
now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
|
||||
if (this->flags_.batch_scheduled && now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
|
||||
this->process_batch_();
|
||||
}
|
||||
|
||||
@ -166,7 +165,7 @@ void APIConnection::loop() {
|
||||
this->initial_state_iterator_.advance();
|
||||
}
|
||||
|
||||
if (this->sent_ping_) {
|
||||
if (this->flags_.sent_ping) {
|
||||
// Disconnect if not responded within 2.5*keepalive
|
||||
if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
|
||||
on_fatal_error();
|
||||
@ -174,13 +173,13 @@ void APIConnection::loop() {
|
||||
}
|
||||
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
|
||||
ESP_LOGVV(TAG, "Sending keepalive PING");
|
||||
this->sent_ping_ = this->send_message(PingRequest());
|
||||
if (!this->sent_ping_) {
|
||||
this->flags_.sent_ping = this->send_message(PingRequest());
|
||||
if (!this->flags_.sent_ping) {
|
||||
// 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
|
||||
ESP_LOGW(TAG, "Buffer full, ping queued");
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,13 +239,13 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
||||
// don't close yet, we still need to send the disconnect response
|
||||
// close will happen on next loop
|
||||
ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
|
||||
this->next_close_ = true;
|
||||
this->flags_.next_close = true;
|
||||
DisconnectResponse resp;
|
||||
return resp;
|
||||
}
|
||||
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
||||
this->helper_->close();
|
||||
this->remove_ = true;
|
||||
this->flags_.remove = true;
|
||||
}
|
||||
|
||||
// Encodes a message to the buffer and returns the total number of bytes used,
|
||||
@ -255,7 +254,7 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t mes
|
||||
uint32_t remaining_size, bool is_single) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
// If in log-only mode, just log and return
|
||||
if (conn->log_only_mode_) {
|
||||
if (conn->flags_.log_only_mode) {
|
||||
conn->log_send_message_(msg.message_name(), msg.dump());
|
||||
return 1; // Return non-zero to indicate "success" for logging
|
||||
}
|
||||
@ -304,10 +303,6 @@ bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary
|
||||
return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
|
||||
BinarySensorStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
|
||||
this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info,
|
||||
ListEntitiesBinarySensorResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -335,9 +330,6 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne
|
||||
bool APIConnection::send_cover_state(cover::Cover *cover) {
|
||||
return this->schedule_message_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_cover_info(cover::Cover *cover) {
|
||||
this->schedule_message_(cover, &APIConnection::try_send_cover_info, ListEntitiesCoverResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *cover = static_cast<cover::Cover *>(entity);
|
||||
@ -399,9 +391,6 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||
bool APIConnection::send_fan_state(fan::Fan *fan) {
|
||||
return this->schedule_message_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_fan_info(fan::Fan *fan) {
|
||||
this->schedule_message_(fan, &APIConnection::try_send_fan_info, ListEntitiesFanResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *fan = static_cast<fan::Fan *>(entity);
|
||||
@ -461,9 +450,6 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
bool APIConnection::send_light_state(light::LightState *light) {
|
||||
return this->schedule_message_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_light_info(light::LightState *light) {
|
||||
this->schedule_message_(light, &APIConnection::try_send_light_info, ListEntitiesLightResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *light = static_cast<light::LightState *>(entity);
|
||||
@ -556,9 +542,6 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||
bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
|
||||
return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||
this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -591,9 +574,6 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *
|
||||
bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
|
||||
return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_switch_info(switch_::Switch *a_switch) {
|
||||
this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -632,10 +612,6 @@ bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor)
|
||||
return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state,
|
||||
TextSensorStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
|
||||
this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info,
|
||||
ListEntitiesTextSensorResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -696,9 +672,6 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
|
||||
resp.target_humidity = climate->target_humidity;
|
||||
return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_climate_info(climate::Climate *climate) {
|
||||
this->schedule_message_(climate, &APIConnection::try_send_climate_info, ListEntitiesClimateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *climate = static_cast<climate::Climate *>(entity);
|
||||
@ -766,9 +739,6 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
bool APIConnection::send_number_state(number::Number *number) {
|
||||
return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_number_info(number::Number *number) {
|
||||
this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -820,9 +790,6 @@ uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *c
|
||||
fill_entity_state_base(date, resp);
|
||||
return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_date_info(datetime::DateEntity *date) {
|
||||
this->schedule_message_(date, &APIConnection::try_send_date_info, ListEntitiesDateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *date = static_cast<datetime::DateEntity *>(entity);
|
||||
@ -857,9 +824,6 @@ uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *c
|
||||
fill_entity_state_base(time, resp);
|
||||
return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_time_info(datetime::TimeEntity *time) {
|
||||
this->schedule_message_(time, &APIConnection::try_send_time_info, ListEntitiesTimeResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *time = static_cast<datetime::TimeEntity *>(entity);
|
||||
@ -896,9 +860,6 @@ uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnectio
|
||||
fill_entity_state_base(datetime, resp);
|
||||
return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
|
||||
this->schedule_message_(datetime, &APIConnection::try_send_datetime_info, ListEntitiesDateTimeResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
|
||||
@ -922,9 +883,6 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
|
||||
bool APIConnection::send_text_state(text::Text *text) {
|
||||
return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_text_info(text::Text *text) {
|
||||
this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -963,9 +921,6 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
|
||||
bool APIConnection::send_select_state(select::Select *select) {
|
||||
return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_select_info(select::Select *select) {
|
||||
this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -999,9 +954,6 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
|
||||
#endif
|
||||
|
||||
#ifdef USE_BUTTON
|
||||
void esphome::api::APIConnection::send_button_info(button::Button *button) {
|
||||
this->schedule_message_(button, &APIConnection::try_send_button_info, ListEntitiesButtonResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *button = static_cast<button::Button *>(entity);
|
||||
@ -1024,9 +976,6 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg
|
||||
bool APIConnection::send_lock_state(lock::Lock *a_lock) {
|
||||
return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_lock_info(lock::Lock *a_lock) {
|
||||
this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
@ -1080,9 +1029,6 @@ uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *
|
||||
fill_entity_state_base(valve, resp);
|
||||
return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_valve_info(valve::Valve *valve) {
|
||||
this->schedule_message_(valve, &APIConnection::try_send_valve_info, ListEntitiesValveResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *valve = static_cast<valve::Valve *>(entity);
|
||||
@ -1128,10 +1074,6 @@ uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConne
|
||||
fill_entity_state_base(media_player, resp);
|
||||
return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
|
||||
this->schedule_message_(media_player, &APIConnection::try_send_media_player_info,
|
||||
ListEntitiesMediaPlayerResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
|
||||
@ -1175,7 +1117,7 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
|
||||
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
||||
if (!this->state_subscription_)
|
||||
if (!this->flags_.state_subscription)
|
||||
return;
|
||||
if (this->image_reader_.available())
|
||||
return;
|
||||
@ -1183,9 +1125,6 @@ void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage>
|
||||
image->was_requested_by(esphome::esp32_camera::IDLE))
|
||||
this->image_reader_.set_image(std::move(image));
|
||||
}
|
||||
void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
||||
this->schedule_message_(camera, &APIConnection::try_send_camera_info, ListEntitiesCameraResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *camera = static_cast<esp32_camera::ESP32Camera *>(entity);
|
||||
@ -1392,10 +1331,6 @@ uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, A
|
||||
fill_entity_state_base(a_alarm_control_panel, resp);
|
||||
return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||
this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info,
|
||||
ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
|
||||
uint32_t remaining_size, bool is_single) {
|
||||
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
|
||||
@ -1446,9 +1381,6 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
|
||||
void APIConnection::send_event(event::Event *event, const std::string &event_type) {
|
||||
this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE);
|
||||
}
|
||||
void APIConnection::send_event_info(event::Event *event) {
|
||||
this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
|
||||
uint32_t remaining_size, bool is_single) {
|
||||
EventResponse resp;
|
||||
@ -1494,9 +1426,6 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection
|
||||
fill_entity_state_base(update, resp);
|
||||
return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||
}
|
||||
void APIConnection::send_update_info(update::UpdateEntity *update) {
|
||||
this->schedule_message_(update, &APIConnection::try_send_update_info, ListEntitiesUpdateResponse::MESSAGE_TYPE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single) {
|
||||
auto *update = static_cast<update::UpdateEntity *>(entity);
|
||||
@ -1529,7 +1458,7 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
// Pre-calculate message size to avoid reallocations
|
||||
@ -1570,7 +1499,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||
resp.name = App.get_name();
|
||||
|
||||
this->connection_state_ = ConnectionState::CONNECTED;
|
||||
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::CONNECTED);
|
||||
return resp;
|
||||
}
|
||||
ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
|
||||
@ -1581,7 +1510,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
|
||||
resp.invalid_password = !correct;
|
||||
if (correct) {
|
||||
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->client_peername_);
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
if (homeassistant::global_homeassistant_time != nullptr) {
|
||||
@ -1695,7 +1624,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
|
||||
state_subs_at_ = 0;
|
||||
}
|
||||
bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
||||
if (this->remove_)
|
||||
if (this->flags_.remove)
|
||||
return false;
|
||||
if (this->helper_->can_write_without_blocking())
|
||||
return true;
|
||||
@ -1745,7 +1674,7 @@ void APIConnection::on_no_setup_connection() {
|
||||
}
|
||||
void APIConnection::on_fatal_error() {
|
||||
this->helper_->close();
|
||||
this->remove_ = true;
|
||||
this->flags_.remove = true;
|
||||
}
|
||||
|
||||
void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
|
||||
@ -1770,8 +1699,8 @@ void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCre
|
||||
}
|
||||
|
||||
bool APIConnection::schedule_batch_() {
|
||||
if (!this->deferred_batch_.batch_scheduled) {
|
||||
this->deferred_batch_.batch_scheduled = true;
|
||||
if (!this->flags_.batch_scheduled) {
|
||||
this->flags_.batch_scheduled = true;
|
||||
this->deferred_batch_.batch_start_time = App.get_loop_component_start_time();
|
||||
}
|
||||
return true;
|
||||
@ -1780,14 +1709,14 @@ bool APIConnection::schedule_batch_() {
|
||||
ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); }
|
||||
|
||||
ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
|
||||
ProtoWriteBuffer result = this->prepare_message_buffer(size, this->batch_first_message_);
|
||||
this->batch_first_message_ = false;
|
||||
ProtoWriteBuffer result = this->prepare_message_buffer(size, this->flags_.batch_first_message);
|
||||
this->flags_.batch_first_message = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void APIConnection::process_batch_() {
|
||||
if (this->deferred_batch_.empty()) {
|
||||
this->deferred_batch_.batch_scheduled = false;
|
||||
this->flags_.batch_scheduled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1840,7 +1769,7 @@ void APIConnection::process_batch_() {
|
||||
|
||||
// Reserve based on estimated size (much more accurate than 24-byte worst-case)
|
||||
this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead);
|
||||
this->batch_first_message_ = true;
|
||||
this->flags_.batch_first_message = true;
|
||||
|
||||
size_t items_processed = 0;
|
||||
uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
|
||||
|
@ -22,6 +22,7 @@ static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
|
||||
class APIConnection : public APIServerConnection {
|
||||
public:
|
||||
friend class APIServer;
|
||||
friend class ListEntitiesIterator;
|
||||
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
|
||||
virtual ~APIConnection();
|
||||
|
||||
@ -34,98 +35,79 @@ class APIConnection : public APIServerConnection {
|
||||
}
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor);
|
||||
void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool send_cover_state(cover::Cover *cover);
|
||||
void send_cover_info(cover::Cover *cover);
|
||||
void cover_command(const CoverCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool send_fan_state(fan::Fan *fan);
|
||||
void send_fan_info(fan::Fan *fan);
|
||||
void fan_command(const FanCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool send_light_state(light::LightState *light);
|
||||
void send_light_info(light::LightState *light);
|
||||
void light_command(const LightCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool send_sensor_state(sensor::Sensor *sensor);
|
||||
void send_sensor_info(sensor::Sensor *sensor);
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool send_switch_state(switch_::Switch *a_switch);
|
||||
void send_switch_info(switch_::Switch *a_switch);
|
||||
void switch_command(const SwitchCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
|
||||
void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
|
||||
#endif
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
|
||||
void send_camera_info(esp32_camera::ESP32Camera *camera);
|
||||
void camera_image(const CameraImageRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool send_climate_state(climate::Climate *climate);
|
||||
void send_climate_info(climate::Climate *climate);
|
||||
void climate_command(const ClimateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_number_state(number::Number *number);
|
||||
void send_number_info(number::Number *number);
|
||||
void number_command(const NumberCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
bool send_date_state(datetime::DateEntity *date);
|
||||
void send_date_info(datetime::DateEntity *date);
|
||||
void date_command(const DateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool send_time_state(datetime::TimeEntity *time);
|
||||
void send_time_info(datetime::TimeEntity *time);
|
||||
void time_command(const TimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool send_datetime_state(datetime::DateTimeEntity *datetime);
|
||||
void send_datetime_info(datetime::DateTimeEntity *datetime);
|
||||
void datetime_command(const DateTimeCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool send_text_state(text::Text *text);
|
||||
void send_text_info(text::Text *text);
|
||||
void text_command(const TextCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_select_state(select::Select *select);
|
||||
void send_select_info(select::Select *select);
|
||||
void select_command(const SelectCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
void send_button_info(button::Button *button);
|
||||
void button_command(const ButtonCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
bool send_lock_state(lock::Lock *a_lock);
|
||||
void send_lock_info(lock::Lock *a_lock);
|
||||
void lock_command(const LockCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool send_valve_state(valve::Valve *valve);
|
||||
void send_valve_info(valve::Valve *valve);
|
||||
void valve_command(const ValveCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
bool send_media_player_state(media_player::MediaPlayer *media_player);
|
||||
void send_media_player_info(media_player::MediaPlayer *media_player);
|
||||
void media_player_command(const MediaPlayerCommandRequest &msg) override;
|
||||
#endif
|
||||
bool try_send_log_message(int level, const char *tag, const char *line);
|
||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||
if (!this->service_call_subscription_)
|
||||
if (!this->flags_.service_call_subscription)
|
||||
return;
|
||||
this->send_message(call);
|
||||
}
|
||||
@ -167,25 +149,22 @@ class APIConnection : public APIServerConnection {
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
||||
void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
||||
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void send_event(event::Event *event, const std::string &event_type);
|
||||
void send_event_info(event::Event *event);
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
bool send_update_state(update::UpdateEntity *update);
|
||||
void send_update_info(update::UpdateEntity *update);
|
||||
void update_command(const UpdateCommandRequest &msg) override;
|
||||
#endif
|
||||
|
||||
void on_disconnect_response(const DisconnectResponse &value) override;
|
||||
void on_ping_response(const PingResponse &value) override {
|
||||
// we initiated ping
|
||||
this->sent_ping_ = false;
|
||||
this->flags_.sent_ping = false;
|
||||
}
|
||||
void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override;
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
@ -198,16 +177,16 @@ class APIConnection : public APIServerConnection {
|
||||
DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override;
|
||||
void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); }
|
||||
void subscribe_states(const SubscribeStatesRequest &msg) override {
|
||||
this->state_subscription_ = true;
|
||||
this->flags_.state_subscription = true;
|
||||
this->initial_state_iterator_.begin();
|
||||
}
|
||||
void subscribe_logs(const SubscribeLogsRequest &msg) override {
|
||||
this->log_subscription_ = msg.level;
|
||||
this->flags_.log_subscription = msg.level;
|
||||
if (msg.dump_config)
|
||||
App.schedule_dump_config();
|
||||
}
|
||||
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;
|
||||
GetTimeResponse get_time(const GetTimeRequest &msg) override {
|
||||
@ -219,9 +198,12 @@ class APIConnection : public APIServerConnection {
|
||||
NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override;
|
||||
#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 {
|
||||
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_unauthenticated_access() override;
|
||||
@ -444,49 +426,28 @@ class APIConnection : public APIServerConnection {
|
||||
static uint16_t try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single);
|
||||
|
||||
// Pointers first (4 bytes each, naturally aligned)
|
||||
// === Optimal member ordering for 32-bit systems ===
|
||||
|
||||
// Group 1: Pointers (4 bytes each on 32-bit)
|
||||
std::unique_ptr<APIFrameHelper> helper_;
|
||||
APIServer *parent_;
|
||||
|
||||
// 4-byte aligned types
|
||||
uint32_t last_traffic_;
|
||||
int state_subs_at_ = -1;
|
||||
|
||||
// Strings (12 bytes each on 32-bit)
|
||||
std::string client_info_;
|
||||
std::string client_peername_;
|
||||
|
||||
// 2-byte aligned types
|
||||
uint16_t client_api_version_major_{0};
|
||||
uint16_t client_api_version_minor_{0};
|
||||
|
||||
// Group all 1-byte types together to minimize padding
|
||||
enum class ConnectionState : uint8_t {
|
||||
WAITING_FOR_HELLO,
|
||||
CONNECTED,
|
||||
AUTHENTICATED,
|
||||
} connection_state_{ConnectionState::WAITING_FOR_HELLO};
|
||||
uint8_t log_subscription_{ESPHOME_LOG_LEVEL_NONE};
|
||||
bool remove_{false};
|
||||
bool state_subscription_{false};
|
||||
bool sent_ping_{false};
|
||||
bool service_call_subscription_{false};
|
||||
bool next_close_ = false;
|
||||
// 7 bytes used, 1 byte padding
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
// When true, encode_message_to_buffer will only log, not encode
|
||||
bool log_only_mode_{false};
|
||||
#endif
|
||||
uint8_t ping_retries_{0};
|
||||
// 8 bytes used, no padding needed
|
||||
|
||||
// Larger objects at the end
|
||||
// Group 2: Larger objects (must be 4-byte aligned)
|
||||
// These contain vectors/pointers internally, so putting them early ensures good alignment
|
||||
InitialStateIterator initial_state_iterator_;
|
||||
ListEntitiesIterator list_entities_iterator_;
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
esp32_camera::CameraImageReader image_reader_;
|
||||
#endif
|
||||
|
||||
// Group 3: Strings (12 bytes each on 32-bit, 4-byte aligned)
|
||||
std::string client_info_;
|
||||
std::string client_peername_;
|
||||
|
||||
// Group 4: 4-byte types
|
||||
uint32_t last_traffic_;
|
||||
int state_subs_at_ = -1;
|
||||
|
||||
// Function pointer type for message encoding
|
||||
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
|
||||
|
||||
@ -596,7 +557,6 @@ class APIConnection : public APIServerConnection {
|
||||
|
||||
std::vector<BatchItem> items;
|
||||
uint32_t batch_start_time{0};
|
||||
bool batch_scheduled{false};
|
||||
|
||||
DeferredBatch() {
|
||||
// Pre-allocate capacity for typical batch sizes to avoid reallocation
|
||||
@ -609,13 +569,47 @@ class APIConnection : public APIServerConnection {
|
||||
void add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type);
|
||||
void clear() {
|
||||
items.clear();
|
||||
batch_scheduled = false;
|
||||
batch_start_time = 0;
|
||||
}
|
||||
bool empty() const { return items.empty(); }
|
||||
};
|
||||
|
||||
// DeferredBatch here (16 bytes, 4-byte aligned)
|
||||
DeferredBatch deferred_batch_;
|
||||
|
||||
// ConnectionState enum for type safety
|
||||
enum class ConnectionState : uint8_t {
|
||||
WAITING_FOR_HELLO = 0,
|
||||
CONNECTED = 1,
|
||||
AUTHENTICATED = 2,
|
||||
};
|
||||
|
||||
// Group 5: Pack all small members together to minimize padding
|
||||
// This group starts at a 4-byte boundary after DeferredBatch
|
||||
struct APIFlags {
|
||||
// Connection state only needs 2 bits (3 states)
|
||||
uint8_t connection_state : 2;
|
||||
// Log subscription needs 3 bits (log levels 0-7)
|
||||
uint8_t log_subscription : 3;
|
||||
// Boolean flags (1 bit each)
|
||||
uint8_t remove : 1;
|
||||
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;
|
||||
uint8_t batch_first_message : 1; // For batch buffer allocation
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
uint8_t log_only_mode : 1;
|
||||
#endif
|
||||
} flags_{}; // 2 bytes total
|
||||
|
||||
// 2-byte types immediately after flags_ (no padding between them)
|
||||
uint16_t client_api_version_major_{0};
|
||||
uint16_t client_api_version_minor_{0};
|
||||
// Total: 2 (flags) + 2 + 2 = 6 bytes, then 2 bytes padding to next 4-byte boundary
|
||||
|
||||
uint32_t get_batch_delay_ms_() const;
|
||||
// Message will use 8 more bytes than the minimum size, and typical
|
||||
// MTU is 1500. Sometimes users will see as low as 1460 MTU.
|
||||
@ -633,9 +627,6 @@ class APIConnection : public APIServerConnection {
|
||||
bool schedule_batch_();
|
||||
void process_batch_();
|
||||
|
||||
// State for batch buffer allocation
|
||||
bool batch_first_message_{false};
|
||||
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void log_batch_item_(const DeferredBatch::BatchItem &item);
|
||||
#endif
|
||||
|
@ -14,7 +14,7 @@ void APIServerConnectionBase::log_send_message_(const char *name, const std::str
|
||||
}
|
||||
#endif
|
||||
|
||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||
void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||
switch (msg_type) {
|
||||
case 1: {
|
||||
HelloRequest msg;
|
||||
@ -106,50 +106,50 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
this->on_subscribe_logs_request(msg);
|
||||
break;
|
||||
}
|
||||
case 30: {
|
||||
#ifdef USE_COVER
|
||||
case 30: {
|
||||
CoverCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_cover_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 31: {
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
case 31: {
|
||||
FanCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_fan_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
case 32: {
|
||||
LightCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_light_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 33: {
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
case 33: {
|
||||
SwitchCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_switch_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 34: {
|
||||
SubscribeHomeassistantServicesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
@ -204,395 +204,394 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
this->on_execute_service_request(msg);
|
||||
break;
|
||||
}
|
||||
case 45: {
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
case 45: {
|
||||
CameraImageRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_camera_image_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 48: {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
case 48: {
|
||||
ClimateCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_climate_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 51: {
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
case 51: {
|
||||
NumberCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_number_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 54: {
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
case 54: {
|
||||
SelectCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_select_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 57: {
|
||||
#endif
|
||||
#ifdef USE_SIREN
|
||||
case 57: {
|
||||
SirenCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_siren_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_siren_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 60: {
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
case 60: {
|
||||
LockCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_lock_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_lock_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 62: {
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
case 62: {
|
||||
ButtonCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_button_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 65: {
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
case 65: {
|
||||
MediaPlayerCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_media_player_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_media_player_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 66: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 66: {
|
||||
SubscribeBluetoothLEAdvertisementsRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_bluetooth_le_advertisements_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 68: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 68: {
|
||||
BluetoothDeviceRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_device_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_device_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 70: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 70: {
|
||||
BluetoothGATTGetServicesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_get_services_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_gatt_get_services_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 73: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 73: {
|
||||
BluetoothGATTReadRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_read_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_gatt_read_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 75: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 75: {
|
||||
BluetoothGATTWriteRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_write_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_gatt_write_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 76: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 76: {
|
||||
BluetoothGATTReadDescriptorRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_read_descriptor_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_gatt_read_descriptor_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 77: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 77: {
|
||||
BluetoothGATTWriteDescriptorRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_write_descriptor_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_gatt_write_descriptor_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 78: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 78: {
|
||||
BluetoothGATTNotifyRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_gatt_notify_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_gatt_notify_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 80: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 80: {
|
||||
SubscribeBluetoothConnectionsFreeRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_bluetooth_connections_free_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 87: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 87: {
|
||||
UnsubscribeBluetoothLEAdvertisementsRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 89: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 89: {
|
||||
SubscribeVoiceAssistantRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_voice_assistant_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_voice_assistant_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 91: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 91: {
|
||||
VoiceAssistantResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_response(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 92: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 92: {
|
||||
VoiceAssistantEventResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_event_response(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 96: {
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
case 96: {
|
||||
AlarmControlPanelCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_alarm_control_panel_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_alarm_control_panel_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 99: {
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
case 99: {
|
||||
TextCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_text_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_text_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 102: {
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
case 102: {
|
||||
DateCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_date_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_date_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 105: {
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
case 105: {
|
||||
TimeCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_time_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_time_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 106: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 106: {
|
||||
VoiceAssistantAudio msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_audio: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_audio(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 111: {
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
case 111: {
|
||||
ValveCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_valve_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_valve_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 114: {
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
case 114: {
|
||||
DateTimeCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_date_time_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 115: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 115: {
|
||||
VoiceAssistantTimerEventResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_timer_event_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_timer_event_response(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 118: {
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
case 118: {
|
||||
UpdateCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_update_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_update_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 119: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 119: {
|
||||
VoiceAssistantAnnounceRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_announce_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_announce_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 121: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 121: {
|
||||
VoiceAssistantConfigurationRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_configuration_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_configuration_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 123: {
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
case 123: {
|
||||
VoiceAssistantSetConfiguration msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_set_configuration(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 124: {
|
||||
#endif
|
||||
#ifdef USE_API_NOISE
|
||||
case 124: {
|
||||
NoiseEncryptionSetKeyRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_noise_encryption_set_key_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_noise_encryption_set_key_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 127: {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
case 127: {
|
||||
BluetoothScannerSetModeRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_bluetooth_scanner_set_mode_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_bluetooth_scanner_set_mode_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
|
||||
|
@ -199,7 +199,7 @@ class APIServerConnectionBase : public ProtoService {
|
||||
virtual void on_update_command_request(const UpdateCommandRequest &value){};
|
||||
#endif
|
||||
protected:
|
||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||
void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||
};
|
||||
|
||||
class APIServerConnection : public APIServerConnectionBase {
|
||||
|
@ -316,15 +316,13 @@ class ProtoSize {
|
||||
/**
|
||||
* @brief Calculates and adds the size of a nested message field to the total message size
|
||||
*
|
||||
* This templated version directly takes a message object, calculates its size internally,
|
||||
* This version takes a ProtoMessage object, calculates its size internally,
|
||||
* and updates the total_size reference. This eliminates the need for a temporary variable
|
||||
* at the call site.
|
||||
*
|
||||
* @tparam MessageType The type of the nested message (inferred from parameter)
|
||||
* @param message The nested message object
|
||||
*/
|
||||
template<typename MessageType>
|
||||
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const MessageType &message,
|
||||
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message,
|
||||
bool force = false) {
|
||||
uint32_t nested_size = 0;
|
||||
message.calculate_size(nested_size);
|
||||
|
@ -104,7 +104,7 @@ void APIServer::setup() {
|
||||
return;
|
||||
}
|
||||
for (auto &c : this->clients_) {
|
||||
if (!c->remove_)
|
||||
if (!c->flags_.remove)
|
||||
c->try_send_log_message(level, tag, message);
|
||||
}
|
||||
});
|
||||
@ -116,7 +116,7 @@ void APIServer::setup() {
|
||||
esp32_camera::global_esp32_camera->add_image_callback(
|
||||
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
||||
for (auto &c : this->clients_) {
|
||||
if (!c->remove_)
|
||||
if (!c->flags_.remove)
|
||||
c->set_camera_state(image);
|
||||
}
|
||||
});
|
||||
@ -176,7 +176,7 @@ void APIServer::loop() {
|
||||
while (client_index < this->clients_.size()) {
|
||||
auto &client = this->clients_[client_index];
|
||||
|
||||
if (!client->remove_) {
|
||||
if (!client->flags_.remove) {
|
||||
// Common case: process active client
|
||||
client->loop();
|
||||
client_index++;
|
||||
@ -431,7 +431,7 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||
|
||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
||||
|
||||
void APIServer::set_batch_delay(uint32_t batch_delay) { this->batch_delay_ = batch_delay; }
|
||||
void APIServer::set_batch_delay(uint16_t batch_delay) { this->batch_delay_ = batch_delay; }
|
||||
|
||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||
for (auto &client : this->clients_) {
|
||||
@ -502,7 +502,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
void APIServer::request_time() {
|
||||
for (auto &client : this->clients_) {
|
||||
if (!client->remove_ && client->is_authenticated())
|
||||
if (!client->flags_.remove && client->is_authenticated())
|
||||
client->send_time_request();
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ class APIServer : public Component, public Controller {
|
||||
void set_port(uint16_t port);
|
||||
void set_password(const std::string &password);
|
||||
void set_reboot_timeout(uint32_t reboot_timeout);
|
||||
void set_batch_delay(uint32_t batch_delay);
|
||||
uint32_t get_batch_delay() const { return batch_delay_; }
|
||||
void set_batch_delay(uint16_t batch_delay);
|
||||
uint16_t get_batch_delay() const { return batch_delay_; }
|
||||
|
||||
// Get reference to shared buffer for API connections
|
||||
std::vector<uint8_t> &get_shared_buffer_ref() { return shared_write_buffer_; }
|
||||
@ -150,7 +150,6 @@ class APIServer : public Component, public Controller {
|
||||
|
||||
// 4-byte aligned types
|
||||
uint32_t reboot_timeout_{300000};
|
||||
uint32_t batch_delay_{100};
|
||||
|
||||
// Vectors and strings (12 bytes each on 32-bit)
|
||||
std::vector<std::unique_ptr<APIConnection>> clients_;
|
||||
@ -161,8 +160,9 @@ class APIServer : public Component, public Controller {
|
||||
|
||||
// Group smaller types together
|
||||
uint16_t port_{6053};
|
||||
uint16_t batch_delay_{100};
|
||||
bool shutting_down_ = false;
|
||||
// 3 bytes used, 1 byte padding
|
||||
// 5 bytes used, 3 bytes padding
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "list_entities.h"
|
||||
#ifdef USE_API
|
||||
#include "api_connection.h"
|
||||
#include "api_pb2.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
@ -8,155 +9,85 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
// Generate entity handler implementations using macros
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
||||
this->client_->send_binary_sensor_info(binary_sensor);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(binary_sensor, binary_sensor::BinarySensor, ListEntitiesBinarySensorResponse)
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
|
||||
this->client_->send_cover_info(cover);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(cover, cover::Cover, ListEntitiesCoverResponse)
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool ListEntitiesIterator::on_fan(fan::Fan *fan) {
|
||||
this->client_->send_fan_info(fan);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(fan, fan::Fan, ListEntitiesFanResponse)
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool ListEntitiesIterator::on_light(light::LightState *light) {
|
||||
this->client_->send_light_info(light);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(light, light::LightState, ListEntitiesLightResponse)
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
|
||||
this->client_->send_sensor_info(sensor);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(sensor, sensor::Sensor, ListEntitiesSensorResponse)
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
|
||||
this->client_->send_switch_info(a_switch);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(switch, switch_::Switch, ListEntitiesSwitchResponse)
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
bool ListEntitiesIterator::on_button(button::Button *button) {
|
||||
this->client_->send_button_info(button);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(button, button::Button, ListEntitiesButtonResponse)
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
||||
this->client_->send_text_sensor_info(text_sensor);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(text_sensor, text_sensor::TextSensor, ListEntitiesTextSensorResponse)
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
|
||||
this->client_->send_lock_info(a_lock);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(lock, lock::Lock, ListEntitiesLockResponse)
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool ListEntitiesIterator::on_valve(valve::Valve *valve) {
|
||||
this->client_->send_valve_info(valve);
|
||||
return true;
|
||||
}
|
||||
LIST_ENTITIES_HANDLER(valve, valve::Valve, ListEntitiesValveResponse)
|
||||
#endif
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
LIST_ENTITIES_HANDLER(camera, esp32_camera::ESP32Camera, ListEntitiesCameraResponse)
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
LIST_ENTITIES_HANDLER(climate, climate::Climate, ListEntitiesClimateResponse)
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
LIST_ENTITIES_HANDLER(number, number::Number, ListEntitiesNumberResponse)
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
LIST_ENTITIES_HANDLER(date, datetime::DateEntity, ListEntitiesDateResponse)
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
LIST_ENTITIES_HANDLER(time, datetime::TimeEntity, ListEntitiesTimeResponse)
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
LIST_ENTITIES_HANDLER(datetime, datetime::DateTimeEntity, ListEntitiesDateTimeResponse)
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
LIST_ENTITIES_HANDLER(text, text::Text, ListEntitiesTextResponse)
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
LIST_ENTITIES_HANDLER(select, select::Select, ListEntitiesSelectResponse)
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
LIST_ENTITIES_HANDLER(media_player, media_player::MediaPlayer, ListEntitiesMediaPlayerResponse)
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
LIST_ENTITIES_HANDLER(alarm_control_panel, alarm_control_panel::AlarmControlPanel,
|
||||
ListEntitiesAlarmControlPanelResponse)
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
LIST_ENTITIES_HANDLER(event, event::Event, ListEntitiesEventResponse)
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
LIST_ENTITIES_HANDLER(update, update::UpdateEntity, ListEntitiesUpdateResponse)
|
||||
#endif
|
||||
|
||||
// Special cases that don't follow the pattern
|
||||
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
|
||||
|
||||
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
|
||||
|
||||
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
|
||||
auto resp = service->encode_list_service_response();
|
||||
return this->client_->send_message(resp);
|
||||
}
|
||||
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
|
||||
this->client_->send_camera_info(camera);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
|
||||
this->client_->send_climate_info(climate);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
bool ListEntitiesIterator::on_number(number::Number *number) {
|
||||
this->client_->send_number_info(number);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) {
|
||||
this->client_->send_date_info(date);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) {
|
||||
this->client_->send_time_info(time);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
||||
this->client_->send_datetime_info(datetime);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
bool ListEntitiesIterator::on_text(text::Text *text) {
|
||||
this->client_->send_text_info(text);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
bool ListEntitiesIterator::on_select(select::Select *select) {
|
||||
this->client_->send_select_info(select);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) {
|
||||
this->client_->send_media_player_info(media_player);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||
this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool ListEntitiesIterator::on_event(event::Event *event) {
|
||||
this->client_->send_event_info(event);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
bool ListEntitiesIterator::on_update(update::UpdateEntity *update) {
|
||||
this->client_->send_update_info(update);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
#endif
|
||||
|
@ -9,75 +9,83 @@ namespace api {
|
||||
|
||||
class APIConnection;
|
||||
|
||||
// Macro for generating ListEntitiesIterator handlers
|
||||
// Calls schedule_message_ with try_send_*_info
|
||||
#define LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \
|
||||
bool ListEntitiesIterator::on_##entity_type(EntityClass *entity) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
return this->client_->schedule_message_(entity, &APIConnection::try_send_##entity_type##_info, \
|
||||
ResponseType::MESSAGE_TYPE); \
|
||||
}
|
||||
|
||||
class ListEntitiesIterator : public ComponentIterator {
|
||||
public:
|
||||
ListEntitiesIterator(APIConnection *client);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
|
||||
bool on_binary_sensor(binary_sensor::BinarySensor *entity) override;
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool on_cover(cover::Cover *cover) override;
|
||||
bool on_cover(cover::Cover *entity) override;
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool on_fan(fan::Fan *fan) override;
|
||||
bool on_fan(fan::Fan *entity) override;
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool on_light(light::LightState *light) override;
|
||||
bool on_light(light::LightState *entity) override;
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool on_sensor(sensor::Sensor *sensor) override;
|
||||
bool on_sensor(sensor::Sensor *entity) override;
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool on_switch(switch_::Switch *a_switch) override;
|
||||
bool on_switch(switch_::Switch *entity) override;
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
bool on_button(button::Button *button) override;
|
||||
bool on_button(button::Button *entity) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
|
||||
bool on_text_sensor(text_sensor::TextSensor *entity) override;
|
||||
#endif
|
||||
bool on_service(UserServiceDescriptor *service) override;
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool on_camera(esp32_camera::ESP32Camera *camera) override;
|
||||
bool on_camera(esp32_camera::ESP32Camera *entity) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool on_climate(climate::Climate *climate) override;
|
||||
bool on_climate(climate::Climate *entity) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool on_number(number::Number *number) override;
|
||||
bool on_number(number::Number *entity) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
bool on_date(datetime::DateEntity *date) override;
|
||||
bool on_date(datetime::DateEntity *entity) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool on_time(datetime::TimeEntity *time) override;
|
||||
bool on_time(datetime::TimeEntity *entity) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool on_datetime(datetime::DateTimeEntity *datetime) override;
|
||||
bool on_datetime(datetime::DateTimeEntity *entity) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool on_text(text::Text *text) override;
|
||||
bool on_text(text::Text *entity) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool on_select(select::Select *select) override;
|
||||
bool on_select(select::Select *entity) override;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
bool on_lock(lock::Lock *a_lock) override;
|
||||
bool on_lock(lock::Lock *entity) override;
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool on_valve(valve::Valve *valve) override;
|
||||
bool on_valve(valve::Valve *entity) override;
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
||||
bool on_media_player(media_player::MediaPlayer *entity) override;
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *entity) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override;
|
||||
bool on_event(event::Event *entity) override;
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
bool on_update(update::UpdateEntity *update) override;
|
||||
bool on_update(update::UpdateEntity *entity) override;
|
||||
#endif
|
||||
bool on_end() override;
|
||||
bool completed() { return this->state_ == IteratorState::NONE; }
|
||||
|
@ -364,7 +364,7 @@ class ProtoService {
|
||||
*/
|
||||
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
|
||||
virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0;
|
||||
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
|
||||
virtual void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
|
||||
|
||||
// Optimized method that pre-allocates buffer based on message size
|
||||
bool send_message_(const ProtoMessage &msg, uint16_t message_type) {
|
||||
|
@ -6,73 +6,67 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
// Generate entity handler implementations using macros
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
||||
return this->client_->send_binary_sensor_state(binary_sensor);
|
||||
}
|
||||
INITIAL_STATE_HANDLER(binary_sensor, binary_sensor::BinarySensor)
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); }
|
||||
INITIAL_STATE_HANDLER(cover, cover::Cover)
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_state(fan); }
|
||||
INITIAL_STATE_HANDLER(fan, fan::Fan)
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
|
||||
INITIAL_STATE_HANDLER(light, light::LightState)
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); }
|
||||
INITIAL_STATE_HANDLER(sensor, sensor::Sensor)
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); }
|
||||
INITIAL_STATE_HANDLER(switch, switch_::Switch)
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
||||
return this->client_->send_text_sensor_state(text_sensor);
|
||||
}
|
||||
INITIAL_STATE_HANDLER(text_sensor, text_sensor::TextSensor)
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
|
||||
INITIAL_STATE_HANDLER(climate, climate::Climate)
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); }
|
||||
INITIAL_STATE_HANDLER(number, number::Number)
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
|
||||
INITIAL_STATE_HANDLER(date, datetime::DateEntity)
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); }
|
||||
INITIAL_STATE_HANDLER(time, datetime::TimeEntity)
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
||||
return this->client_->send_datetime_state(datetime);
|
||||
}
|
||||
INITIAL_STATE_HANDLER(datetime, datetime::DateTimeEntity)
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); }
|
||||
INITIAL_STATE_HANDLER(text, text::Text)
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); }
|
||||
INITIAL_STATE_HANDLER(select, select::Select)
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); }
|
||||
INITIAL_STATE_HANDLER(lock, lock::Lock)
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); }
|
||||
INITIAL_STATE_HANDLER(valve, valve::Valve)
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) {
|
||||
return this->client_->send_media_player_state(media_player);
|
||||
}
|
||||
INITIAL_STATE_HANDLER(media_player, media_player::MediaPlayer)
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool InitialStateIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||
return this->client_->send_alarm_control_panel_state(a_alarm_control_panel);
|
||||
}
|
||||
INITIAL_STATE_HANDLER(alarm_control_panel, alarm_control_panel::AlarmControlPanel)
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
bool InitialStateIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_state(update); }
|
||||
INITIAL_STATE_HANDLER(update, update::UpdateEntity)
|
||||
#endif
|
||||
|
||||
// Special cases (button and event) are already defined inline in subscribe_state.h
|
||||
|
||||
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
|
||||
|
||||
} // namespace api
|
||||
|
@ -10,71 +10,78 @@ namespace api {
|
||||
|
||||
class APIConnection;
|
||||
|
||||
// Macro for generating InitialStateIterator handlers
|
||||
// Calls send_*_state
|
||||
#define INITIAL_STATE_HANDLER(entity_type, EntityClass) \
|
||||
bool InitialStateIterator::on_##entity_type(EntityClass *entity) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
return this->client_->send_##entity_type##_state(entity); \
|
||||
}
|
||||
|
||||
class InitialStateIterator : public ComponentIterator {
|
||||
public:
|
||||
InitialStateIterator(APIConnection *client);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
|
||||
bool on_binary_sensor(binary_sensor::BinarySensor *entity) override;
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool on_cover(cover::Cover *cover) override;
|
||||
bool on_cover(cover::Cover *entity) override;
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool on_fan(fan::Fan *fan) override;
|
||||
bool on_fan(fan::Fan *entity) override;
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool on_light(light::LightState *light) override;
|
||||
bool on_light(light::LightState *entity) override;
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool on_sensor(sensor::Sensor *sensor) override;
|
||||
bool on_sensor(sensor::Sensor *entity) override;
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool on_switch(switch_::Switch *a_switch) override;
|
||||
bool on_switch(switch_::Switch *entity) override;
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
bool on_button(button::Button *button) override { return true; };
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
|
||||
bool on_text_sensor(text_sensor::TextSensor *entity) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool on_climate(climate::Climate *climate) override;
|
||||
bool on_climate(climate::Climate *entity) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool on_number(number::Number *number) override;
|
||||
bool on_number(number::Number *entity) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
bool on_date(datetime::DateEntity *date) override;
|
||||
bool on_date(datetime::DateEntity *entity) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
bool on_time(datetime::TimeEntity *time) override;
|
||||
bool on_time(datetime::TimeEntity *entity) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
bool on_datetime(datetime::DateTimeEntity *datetime) override;
|
||||
bool on_datetime(datetime::DateTimeEntity *entity) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
bool on_text(text::Text *text) override;
|
||||
bool on_text(text::Text *entity) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool on_select(select::Select *select) override;
|
||||
bool on_select(select::Select *entity) override;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
bool on_lock(lock::Lock *a_lock) override;
|
||||
bool on_lock(lock::Lock *entity) override;
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
bool on_valve(valve::Valve *valve) override;
|
||||
bool on_valve(valve::Valve *entity) override;
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
||||
bool on_media_player(media_player::MediaPlayer *entity) override;
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *entity) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
bool on_event(event::Event *event) override { return true; };
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
bool on_update(update::UpdateEntity *update) override;
|
||||
bool on_update(update::UpdateEntity *entity) override;
|
||||
#endif
|
||||
bool completed() { return this->state_ == IteratorState::NONE; }
|
||||
|
||||
|
@ -10,11 +10,24 @@ GPIOBinarySensor = gpio_ns.class_(
|
||||
"GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONF_USE_INTERRUPT = "use_interrupt"
|
||||
CONF_INTERRUPT_TYPE = "interrupt_type"
|
||||
|
||||
INTERRUPT_TYPES = {
|
||||
"RISING": gpio_ns.INTERRUPT_RISING_EDGE,
|
||||
"FALLING": gpio_ns.INTERRUPT_FALLING_EDGE,
|
||||
"ANY": gpio_ns.INTERRUPT_ANY_EDGE,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
binary_sensor.binary_sensor_schema(GPIOBinarySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
|
||||
cv.Optional(CONF_USE_INTERRUPT, default=True): cv.boolean,
|
||||
cv.Optional(CONF_INTERRUPT_TYPE, default="ANY"): cv.enum(
|
||||
INTERRUPT_TYPES, upper=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
@ -27,3 +40,7 @@ async def to_code(config):
|
||||
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
|
||||
cg.add(var.set_use_interrupt(config[CONF_USE_INTERRUPT]))
|
||||
if config[CONF_USE_INTERRUPT]:
|
||||
cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE]))
|
||||
|
@ -6,17 +6,91 @@ namespace gpio {
|
||||
|
||||
static const char *const TAG = "gpio.binary_sensor";
|
||||
|
||||
void IRAM_ATTR GPIOBinarySensorStore::gpio_intr(GPIOBinarySensorStore *arg) {
|
||||
bool new_state = arg->isr_pin_.digital_read();
|
||||
if (new_state != arg->last_state_) {
|
||||
arg->state_ = new_state;
|
||||
arg->last_state_ = new_state;
|
||||
arg->changed_ = true;
|
||||
// Wake up the component from its disabled loop state
|
||||
if (arg->component_ != nullptr) {
|
||||
arg->component_->enable_loop_soon_any_context();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPIOBinarySensorStore::setup(InternalGPIOPin *pin, gpio::InterruptType type, Component *component) {
|
||||
pin->setup();
|
||||
this->isr_pin_ = pin->to_isr();
|
||||
this->component_ = component;
|
||||
|
||||
// Read initial state
|
||||
this->last_state_ = pin->digital_read();
|
||||
this->state_ = this->last_state_;
|
||||
|
||||
// Attach interrupt - from this point on, any changes will be caught by the interrupt
|
||||
pin->attach_interrupt(&GPIOBinarySensorStore::gpio_intr, this, type);
|
||||
}
|
||||
|
||||
void GPIOBinarySensor::setup() {
|
||||
this->pin_->setup();
|
||||
this->publish_initial_state(this->pin_->digital_read());
|
||||
if (this->use_interrupt_ && !this->pin_->is_internal()) {
|
||||
ESP_LOGD(TAG, "GPIO is not internal, falling back to polling mode");
|
||||
this->use_interrupt_ = false;
|
||||
}
|
||||
|
||||
if (this->use_interrupt_) {
|
||||
auto *internal_pin = static_cast<InternalGPIOPin *>(this->pin_);
|
||||
this->store_.setup(internal_pin, this->interrupt_type_, this);
|
||||
this->publish_initial_state(this->store_.get_state());
|
||||
} else {
|
||||
this->pin_->setup();
|
||||
this->publish_initial_state(this->pin_->digital_read());
|
||||
}
|
||||
}
|
||||
|
||||
void GPIOBinarySensor::dump_config() {
|
||||
LOG_BINARY_SENSOR("", "GPIO Binary Sensor", this);
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
const char *mode = this->use_interrupt_ ? "interrupt" : "polling";
|
||||
ESP_LOGCONFIG(TAG, " Mode: %s", mode);
|
||||
if (this->use_interrupt_) {
|
||||
const char *interrupt_type;
|
||||
switch (this->interrupt_type_) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
interrupt_type = "RISING_EDGE";
|
||||
break;
|
||||
case gpio::INTERRUPT_FALLING_EDGE:
|
||||
interrupt_type = "FALLING_EDGE";
|
||||
break;
|
||||
case gpio::INTERRUPT_ANY_EDGE:
|
||||
interrupt_type = "ANY_EDGE";
|
||||
break;
|
||||
default:
|
||||
interrupt_type = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Interrupt Type: %s", interrupt_type);
|
||||
}
|
||||
}
|
||||
|
||||
void GPIOBinarySensor::loop() { this->publish_state(this->pin_->digital_read()); }
|
||||
void GPIOBinarySensor::loop() {
|
||||
if (this->use_interrupt_) {
|
||||
if (this->store_.is_changed()) {
|
||||
// Clear the flag immediately to minimize the window where we might miss changes
|
||||
this->store_.clear_changed();
|
||||
// Read the state and publish it
|
||||
// Note: If the ISR fires between clear_changed() and get_state(), that's fine -
|
||||
// we'll process the new change on the next loop iteration
|
||||
bool state = this->store_.get_state();
|
||||
this->publish_state(state);
|
||||
} else {
|
||||
// No changes, disable the loop until the next interrupt
|
||||
this->disable_loop();
|
||||
}
|
||||
} else {
|
||||
this->publish_state(this->pin_->digital_read());
|
||||
}
|
||||
}
|
||||
|
||||
float GPIOBinarySensor::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
|
@ -2,14 +2,51 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace gpio {
|
||||
|
||||
// Store class for ISR data (no vtables, ISR-safe)
|
||||
class GPIOBinarySensorStore {
|
||||
public:
|
||||
void setup(InternalGPIOPin *pin, gpio::InterruptType type, Component *component);
|
||||
|
||||
static void gpio_intr(GPIOBinarySensorStore *arg);
|
||||
|
||||
bool get_state() const {
|
||||
// No lock needed: state_ is atomically updated by ISR
|
||||
// Volatile ensures we read the latest value
|
||||
return this->state_;
|
||||
}
|
||||
|
||||
bool is_changed() const {
|
||||
// Simple read of volatile bool - no clearing here
|
||||
return this->changed_;
|
||||
}
|
||||
|
||||
void clear_changed() {
|
||||
// Separate method to clear the flag
|
||||
this->changed_ = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
ISRInternalGPIOPin isr_pin_;
|
||||
volatile bool state_{false};
|
||||
volatile bool last_state_{false};
|
||||
volatile bool changed_{false};
|
||||
Component *component_{nullptr}; // Pointer to the component for enable_loop_soon_any_context()
|
||||
};
|
||||
|
||||
class GPIOBinarySensor : public binary_sensor::BinarySensor, public Component {
|
||||
public:
|
||||
// No destructor needed: ESPHome components are created at boot and live forever.
|
||||
// Interrupts are only detached on reboot when memory is cleared anyway.
|
||||
|
||||
void set_pin(GPIOPin *pin) { pin_ = pin; }
|
||||
void set_use_interrupt(bool use_interrupt) { use_interrupt_ = use_interrupt; }
|
||||
void set_interrupt_type(gpio::InterruptType type) { interrupt_type_ = type; }
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
/// Setup pin
|
||||
@ -22,6 +59,9 @@ class GPIOBinarySensor : public binary_sensor::BinarySensor, public Component {
|
||||
|
||||
protected:
|
||||
GPIOPin *pin_;
|
||||
bool use_interrupt_{true};
|
||||
gpio::InterruptType interrupt_type_{gpio::INTERRUPT_ANY_EDGE};
|
||||
GPIOBinarySensorStore store_;
|
||||
};
|
||||
|
||||
} // namespace gpio
|
||||
|
84
esphome/components/pi4ioe5v6408/__init__.py
Normal file
84
esphome/components/pi4ioe5v6408/__init__.py
Normal file
@ -0,0 +1,84 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OUTPUT,
|
||||
CONF_PULLDOWN,
|
||||
CONF_PULLUP,
|
||||
CONF_RESET,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["gpio_expander"]
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
|
||||
pi4ioe5v6408_ns = cg.esphome_ns.namespace("pi4ioe5v6408")
|
||||
PI4IOE5V6408Component = pi4ioe5v6408_ns.class_(
|
||||
"PI4IOE5V6408Component", cg.Component, i2c.I2CDevice
|
||||
)
|
||||
PI4IOE5V6408GPIOPin = pi4ioe5v6408_ns.class_("PI4IOE5V6408GPIOPin", cg.GPIOPin)
|
||||
|
||||
CONF_PI4IOE5V6408 = "pi4ioe5v6408"
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(PI4IOE5V6408Component),
|
||||
cv.Optional(CONF_RESET, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(i2c.i2c_device_schema(0x43))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_reset(config[CONF_RESET]))
|
||||
|
||||
|
||||
def validate_mode(value):
|
||||
if not (value[CONF_INPUT] or value[CONF_OUTPUT]):
|
||||
raise cv.Invalid("Mode must be either input or output")
|
||||
if value[CONF_INPUT] and value[CONF_OUTPUT]:
|
||||
raise cv.Invalid("Mode must be either input or output")
|
||||
return value
|
||||
|
||||
|
||||
PI4IOE5V6408_PIN_SCHEMA = pins.gpio_base_schema(
|
||||
PI4IOE5V6408GPIOPin,
|
||||
cv.int_range(min=0, max=7),
|
||||
modes=[
|
||||
CONF_INPUT,
|
||||
CONF_OUTPUT,
|
||||
CONF_PULLUP,
|
||||
CONF_PULLDOWN,
|
||||
],
|
||||
mode_validator=validate_mode,
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_PI4IOE5V6408): cv.use_id(PI4IOE5V6408Component),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_PI4IOE5V6408, PI4IOE5V6408_PIN_SCHEMA)
|
||||
async def pi4ioe5v6408_pin_schema(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_parented(var, config[CONF_PI4IOE5V6408])
|
||||
|
||||
cg.add(var.set_pin(config[CONF_NUMBER]))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
171
esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp
Normal file
171
esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
#include "pi4ioe5v6408.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace pi4ioe5v6408 {
|
||||
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_DEVICE_ID = 0x01;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_IO_DIR = 0x03;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_OUT_SET = 0x05;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_OUT_HIGH_IMPEDENCE = 0x07;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_IN_DEFAULT_STATE = 0x09;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_PULL_ENABLE = 0x0B;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_PULL_SELECT = 0x0D;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_IN_STATE = 0x0F;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_INTERRUPT_ENABLE_MASK = 0x11;
|
||||
static const uint8_t PI4IOE5V6408_REGISTER_INTERRUPT_STATUS = 0x13;
|
||||
|
||||
static const char *const TAG = "pi4ioe5v6408";
|
||||
|
||||
void PI4IOE5V6408Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Running setup");
|
||||
if (this->reset_) {
|
||||
this->reg(PI4IOE5V6408_REGISTER_DEVICE_ID) |= 0b00000001;
|
||||
this->reg(PI4IOE5V6408_REGISTER_OUT_HIGH_IMPEDENCE) = 0b00000000;
|
||||
} else {
|
||||
if (!this->read_gpio_modes_()) {
|
||||
this->mark_failed();
|
||||
ESP_LOGE(TAG, "Failed to read GPIO modes");
|
||||
return;
|
||||
}
|
||||
if (!this->read_gpio_outputs_()) {
|
||||
this->mark_failed();
|
||||
ESP_LOGE(TAG, "Failed to read GPIO outputs");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void PI4IOE5V6408Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "PI4IOE5V6408:");
|
||||
LOG_I2C_DEVICE(this)
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
}
|
||||
void PI4IOE5V6408Component::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
if (flags & gpio::FLAG_OUTPUT) {
|
||||
// Set mode mask bit
|
||||
this->mode_mask_ |= 1 << pin;
|
||||
} else if (flags & gpio::FLAG_INPUT) {
|
||||
// Clear mode mask bit
|
||||
this->mode_mask_ &= ~(1 << pin);
|
||||
if (flags & gpio::FLAG_PULLUP) {
|
||||
this->pull_up_down_mask_ |= 1 << pin;
|
||||
this->pull_enable_mask_ |= 1 << pin;
|
||||
} else if (flags & gpio::FLAG_PULLDOWN) {
|
||||
this->pull_up_down_mask_ &= ~(1 << pin);
|
||||
this->pull_enable_mask_ |= 1 << pin;
|
||||
}
|
||||
}
|
||||
// Write GPIO to enable input mode
|
||||
this->write_gpio_modes_();
|
||||
}
|
||||
|
||||
void PI4IOE5V6408Component::loop() { this->reset_pin_cache_(); }
|
||||
|
||||
bool PI4IOE5V6408Component::read_gpio_outputs_() {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
uint8_t data;
|
||||
if (!this->read_byte(PI4IOE5V6408_REGISTER_OUT_SET, &data)) {
|
||||
this->status_set_warning("Failed to read output register");
|
||||
return false;
|
||||
}
|
||||
this->output_mask_ = data;
|
||||
this->status_clear_warning();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PI4IOE5V6408Component::read_gpio_modes_() {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
uint8_t data;
|
||||
if (!this->read_byte(PI4IOE5V6408_REGISTER_IO_DIR, &data)) {
|
||||
this->status_set_warning("Failed to read GPIO modes");
|
||||
return false;
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
ESP_LOGV(TAG, "Read GPIO modes: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(data));
|
||||
#endif
|
||||
this->mode_mask_ = data;
|
||||
this->status_clear_warning();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PI4IOE5V6408Component::digital_read_hw(uint8_t pin) {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
uint8_t data;
|
||||
if (!this->read_byte(PI4IOE5V6408_REGISTER_IN_STATE, &data)) {
|
||||
this->status_set_warning("Failed to read GPIO state");
|
||||
return false;
|
||||
}
|
||||
this->input_mask_ = data;
|
||||
this->status_clear_warning();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PI4IOE5V6408Component::digital_write_hw(uint8_t pin, bool value) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
if (value) {
|
||||
this->output_mask_ |= (1 << pin);
|
||||
} else {
|
||||
this->output_mask_ &= ~(1 << pin);
|
||||
}
|
||||
if (!this->write_byte(PI4IOE5V6408_REGISTER_OUT_SET, this->output_mask_)) {
|
||||
this->status_set_warning("Failed to write output register");
|
||||
return;
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
ESP_LOGV(TAG, "Wrote GPIO output: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(this->output_mask_));
|
||||
#endif
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
bool PI4IOE5V6408Component::write_gpio_modes_() {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
if (!this->write_byte(PI4IOE5V6408_REGISTER_IO_DIR, this->mode_mask_)) {
|
||||
this->status_set_warning("Failed to write GPIO modes");
|
||||
return false;
|
||||
}
|
||||
if (!this->write_byte(PI4IOE5V6408_REGISTER_PULL_SELECT, this->pull_up_down_mask_)) {
|
||||
this->status_set_warning("Failed to write GPIO pullup/pulldown");
|
||||
return false;
|
||||
}
|
||||
if (!this->write_byte(PI4IOE5V6408_REGISTER_PULL_ENABLE, this->pull_enable_mask_)) {
|
||||
this->status_set_warning("Failed to write GPIO pull enable");
|
||||
return false;
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
ESP_LOGV(TAG,
|
||||
"Wrote GPIO modes: 0b" BYTE_TO_BINARY_PATTERN "\n"
|
||||
"Wrote GPIO pullup/pulldown: 0b" BYTE_TO_BINARY_PATTERN "\n"
|
||||
"Wrote GPIO pull enable: 0b" BYTE_TO_BINARY_PATTERN,
|
||||
BYTE_TO_BINARY(this->mode_mask_), BYTE_TO_BINARY(this->pull_up_down_mask_),
|
||||
BYTE_TO_BINARY(this->pull_enable_mask_));
|
||||
#endif
|
||||
this->status_clear_warning();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PI4IOE5V6408Component::digital_read_cache(uint8_t pin) { return (this->input_mask_ & (1 << pin)); }
|
||||
|
||||
float PI4IOE5V6408Component::get_setup_priority() const { return setup_priority::IO; }
|
||||
|
||||
void PI4IOE5V6408GPIOPin::setup() { this->pin_mode(this->flags_); }
|
||||
void PI4IOE5V6408GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
||||
bool PI4IOE5V6408GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void PI4IOE5V6408GPIOPin::digital_write(bool value) {
|
||||
this->parent_->digital_write(this->pin_, value != this->inverted_);
|
||||
}
|
||||
std::string PI4IOE5V6408GPIOPin::dump_summary() const { return str_sprintf("%u via PI4IOE5V6408", this->pin_); }
|
||||
|
||||
} // namespace pi4ioe5v6408
|
||||
} // namespace esphome
|
70
esphome/components/pi4ioe5v6408/pi4ioe5v6408.h
Normal file
70
esphome/components/pi4ioe5v6408/pi4ioe5v6408.h
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/gpio_expander/cached_gpio.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace pi4ioe5v6408 {
|
||||
class PI4IOE5V6408Component : public Component,
|
||||
public i2c::I2CDevice,
|
||||
public gpio_expander::CachedGpioExpander<uint8_t, 8> {
|
||||
public:
|
||||
PI4IOE5V6408Component() = default;
|
||||
|
||||
void setup() override;
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
float get_setup_priority() const override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
||||
/// Indicate if the component should reset the state during setup
|
||||
void set_reset(bool reset) { this->reset_ = reset; }
|
||||
|
||||
protected:
|
||||
bool digital_read_hw(uint8_t pin) override;
|
||||
bool digital_read_cache(uint8_t pin) override;
|
||||
void digital_write_hw(uint8_t pin, bool value) override;
|
||||
|
||||
/// Mask for the pin mode - 1 means output, 0 means input
|
||||
uint8_t mode_mask_{0x00};
|
||||
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
||||
uint8_t output_mask_{0x00};
|
||||
/// The state read in digital_read_hw - 1 means HIGH, 0 means LOW
|
||||
uint8_t input_mask_{0x00};
|
||||
/// The mask to write as input buffer state - 1 means enabled, 0 means disabled
|
||||
uint8_t pull_enable_mask_{0x00};
|
||||
/// The mask to write as pullup state - 1 means pullup, 0 means pulldown
|
||||
uint8_t pull_up_down_mask_{0x00};
|
||||
|
||||
bool reset_{true};
|
||||
|
||||
bool read_gpio_modes_();
|
||||
bool write_gpio_modes_();
|
||||
bool read_gpio_outputs_();
|
||||
};
|
||||
|
||||
class PI4IOE5V6408GPIOPin : public GPIOPin, public Parented<PI4IOE5V6408Component> {
|
||||
public:
|
||||
void setup() override;
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
std::string dump_summary() const override;
|
||||
|
||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
|
||||
|
||||
gpio::Flags get_flags() const override { return this->flags_; }
|
||||
|
||||
protected:
|
||||
uint8_t pin_;
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
} // namespace pi4ioe5v6408
|
||||
} // namespace esphome
|
@ -197,6 +197,7 @@ async def add_entity_config(entity, config):
|
||||
sorting_weight = config.get(CONF_SORTING_WEIGHT, 50)
|
||||
sorting_group_hash = hash(config.get(CONF_SORTING_GROUP_ID))
|
||||
|
||||
cg.add_define("USE_WEBSERVER_SORTING")
|
||||
cg.add(
|
||||
web_server.add_entity_config(
|
||||
entity,
|
||||
@ -284,4 +285,5 @@ async def to_code(config):
|
||||
cg.add_define("USE_WEBSERVER_LOCAL")
|
||||
|
||||
if (sorting_group_config := config.get(CONF_SORTING_GROUPS)) is not None:
|
||||
cg.add_define("USE_WEBSERVER_SORTING")
|
||||
add_sorting_groups(var, sorting_group_config)
|
||||
|
@ -184,6 +184,7 @@ void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUp
|
||||
std::string message = ws->get_config_json();
|
||||
source->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
|
||||
|
||||
#ifdef USE_WEBSERVER_SORTING
|
||||
for (auto &group : ws->sorting_groups_) {
|
||||
message = json::build_json([group](JsonObject root) {
|
||||
root["name"] = group.second.name;
|
||||
@ -193,6 +194,7 @@ void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUp
|
||||
// up to 31 groups should be able to be queued initially without defer
|
||||
source->try_send_nodefer(message.c_str(), "sorting_group");
|
||||
}
|
||||
#endif
|
||||
|
||||
source->entities_iterator_.begin(ws->include_internal_);
|
||||
|
||||
@ -413,12 +415,7 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
|
||||
}
|
||||
set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
if (!obj->get_unit_of_measurement().empty())
|
||||
root["uom"] = obj->get_unit_of_measurement();
|
||||
}
|
||||
@ -458,12 +455,7 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std:
|
||||
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||
set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -511,12 +503,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail
|
||||
set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
|
||||
if (start_config == DETAIL_ALL) {
|
||||
root["assumed_state"] = obj->assumed_state();
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -552,12 +539,7 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config)
|
||||
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||
set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -595,12 +577,7 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool
|
||||
set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
|
||||
start_config);
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -681,12 +658,7 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
|
||||
if (obj->get_traits().supports_oscillation())
|
||||
root["oscillation"] = obj->oscillating;
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -802,12 +774,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
|
||||
for (auto const &option : obj->get_effects()) {
|
||||
opt.add(option->get_name());
|
||||
}
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -888,12 +855,7 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
||||
if (obj->get_traits().get_supports_tilt())
|
||||
root["tilt"] = obj->tilt;
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -954,12 +916,7 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
||||
root["mode"] = (int) obj->traits.get_mode();
|
||||
if (!obj->traits.get_unit_of_measurement().empty())
|
||||
root["uom"] = obj->traits.get_unit_of_measurement();
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
if (std::isnan(value)) {
|
||||
root["value"] = "\"NaN\"";
|
||||
@ -1028,12 +985,7 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con
|
||||
root["value"] = value;
|
||||
root["state"] = value;
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1091,12 +1043,7 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con
|
||||
root["value"] = value;
|
||||
root["state"] = value;
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1155,12 +1102,7 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s
|
||||
root["value"] = value;
|
||||
root["state"] = value;
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1221,12 +1163,7 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
|
||||
root["value"] = value;
|
||||
if (start_config == DETAIL_ALL) {
|
||||
root["mode"] = (int) obj->traits.get_mode();
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1282,12 +1219,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
|
||||
for (auto &option : obj->traits.get_options()) {
|
||||
opt.add(option);
|
||||
}
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1404,12 +1336,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
||||
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
||||
opt.add(custom_preset);
|
||||
}
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
|
||||
bool has_state = false;
|
||||
@ -1502,12 +1429,7 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet
|
||||
set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
|
||||
start_config);
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1579,12 +1501,7 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
|
||||
if (obj->get_traits().get_supports_position())
|
||||
root["position"] = obj->position;
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1652,12 +1569,7 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro
|
||||
set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
|
||||
PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
|
||||
if (start_config == DETAIL_ALL) {
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1705,12 +1617,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
|
||||
event_types.add(event_type);
|
||||
}
|
||||
root["device_class"] = obj->get_device_class();
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1774,12 +1681,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
|
||||
root["title"] = obj->update_info.title;
|
||||
root["summary"] = obj->update_info.summary;
|
||||
root["release_url"] = obj->update_info.release_url;
|
||||
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
|
||||
}
|
||||
}
|
||||
this->add_sorting_info_(root, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -2093,6 +1995,18 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||
|
||||
bool WebServer::isRequestHandlerTrivial() const { return false; }
|
||||
|
||||
void WebServer::add_sorting_info_(JsonObject &root, EntityBase *entity) {
|
||||
#ifdef USE_WEBSERVER_SORTING
|
||||
if (this->sorting_entitys_.find(entity) != this->sorting_entitys_.end()) {
|
||||
root["sorting_weight"] = this->sorting_entitys_[entity].weight;
|
||||
if (this->sorting_groups_.find(this->sorting_entitys_[entity].group_id) != this->sorting_groups_.end()) {
|
||||
root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[entity].group_id].name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_WEBSERVER_SORTING
|
||||
void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) {
|
||||
this->sorting_entitys_[entity] = SortingComponents{weight, group};
|
||||
}
|
||||
@ -2100,6 +2014,7 @@ void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t gro
|
||||
void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_name, float weight) {
|
||||
this->sorting_groups_[group_id] = SortingGroup{group_name, weight};
|
||||
}
|
||||
#endif
|
||||
|
||||
void WebServer::schedule_(std::function<void()> &&f) {
|
||||
#ifdef USE_ESP32
|
||||
|
@ -46,6 +46,7 @@ struct UrlMatch {
|
||||
bool valid; ///< Whether this match is valid
|
||||
};
|
||||
|
||||
#ifdef USE_WEBSERVER_SORTING
|
||||
struct SortingComponents {
|
||||
float weight;
|
||||
uint64_t group_id;
|
||||
@ -55,6 +56,7 @@ struct SortingGroup {
|
||||
std::string name;
|
||||
float weight;
|
||||
};
|
||||
#endif
|
||||
|
||||
enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
|
||||
|
||||
@ -474,14 +476,18 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
/// This web handle is not trivial.
|
||||
bool isRequestHandlerTrivial() const override; // NOLINT(readability-identifier-naming)
|
||||
|
||||
#ifdef USE_WEBSERVER_SORTING
|
||||
void add_entity_config(EntityBase *entity, float weight, uint64_t group);
|
||||
void add_sorting_group(uint64_t group_id, const std::string &group_name, float weight);
|
||||
|
||||
std::map<EntityBase *, SortingComponents> sorting_entitys_;
|
||||
std::map<uint64_t, SortingGroup> sorting_groups_;
|
||||
#endif
|
||||
|
||||
bool include_internal_{false};
|
||||
|
||||
protected:
|
||||
void add_sorting_info_(JsonObject &root, EntityBase *entity);
|
||||
void schedule_(std::function<void()> &&f);
|
||||
web_server_base::WebServerBase *base_;
|
||||
#ifdef USE_ARDUINO
|
||||
|
@ -530,6 +530,7 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *
|
||||
std::string message = ws->get_config_json();
|
||||
this->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
|
||||
|
||||
#ifdef USE_WEBSERVER_SORTING
|
||||
for (auto &group : ws->sorting_groups_) {
|
||||
message = json::build_json([group](JsonObject root) {
|
||||
root["name"] = group.second.name;
|
||||
@ -540,6 +541,7 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *
|
||||
// since the only thing in the send buffer at this point is the initial ping/config
|
||||
this->try_send_nodefer(message.c_str(), "sorting_group");
|
||||
}
|
||||
#endif
|
||||
|
||||
this->entities_iterator_->begin(ws->include_internal_);
|
||||
|
||||
|
@ -152,6 +152,7 @@
|
||||
#define USE_WEBSERVER
|
||||
#define USE_WEBSERVER_OTA
|
||||
#define USE_WEBSERVER_PORT 80 // NOLINT
|
||||
#define USE_WEBSERVER_SORTING
|
||||
#define USE_WIFI_11KV_SUPPORT
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
@ -1034,7 +1034,7 @@ SOURCE_BOTH = 0
|
||||
SOURCE_SERVER = 1
|
||||
SOURCE_CLIENT = 2
|
||||
|
||||
RECEIVE_CASES: dict[int, str] = {}
|
||||
RECEIVE_CASES: dict[int, tuple[str, str | None]] = {}
|
||||
|
||||
ifdefs: dict[str, str] = {}
|
||||
|
||||
@ -1208,8 +1208,6 @@ def build_service_message_type(
|
||||
func = f"on_{snake}"
|
||||
hout += f"virtual void {func}(const {mt.name} &value){{}};\n"
|
||||
case = ""
|
||||
if ifdef is not None:
|
||||
case += f"#ifdef {ifdef}\n"
|
||||
case += f"{mt.name} msg;\n"
|
||||
case += "msg.decode(msg_data, msg_size);\n"
|
||||
if log:
|
||||
@ -1217,10 +1215,9 @@ def build_service_message_type(
|
||||
case += f'ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n'
|
||||
case += "#endif\n"
|
||||
case += f"this->{func}(msg);\n"
|
||||
if ifdef is not None:
|
||||
case += "#endif\n"
|
||||
case += "break;"
|
||||
RECEIVE_CASES[id_] = case
|
||||
# Store the ifdef with the case for later use
|
||||
RECEIVE_CASES[id_] = (case, ifdef)
|
||||
|
||||
# Only close ifdef if we opened it
|
||||
if ifdef is not None:
|
||||
@ -1379,18 +1376,21 @@ def main() -> None:
|
||||
cases = list(RECEIVE_CASES.items())
|
||||
cases.sort()
|
||||
hpp += " protected:\n"
|
||||
hpp += " bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n"
|
||||
out = f"bool {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n"
|
||||
hpp += " void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n"
|
||||
out = f"void {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n"
|
||||
out += " switch (msg_type) {\n"
|
||||
for i, case in cases:
|
||||
c = f"case {i}: {{\n"
|
||||
c += indent(case) + "\n"
|
||||
c += "}"
|
||||
out += indent(c, " ") + "\n"
|
||||
for i, (case, ifdef) in cases:
|
||||
if ifdef is not None:
|
||||
out += f"#ifdef {ifdef}\n"
|
||||
c = f" case {i}: {{\n"
|
||||
c += indent(case, " ") + "\n"
|
||||
c += " }"
|
||||
out += c + "\n"
|
||||
if ifdef is not None:
|
||||
out += "#endif\n"
|
||||
out += " default:\n"
|
||||
out += " return false;\n"
|
||||
out += " break;\n"
|
||||
out += " }\n"
|
||||
out += " return true;\n"
|
||||
out += "}\n"
|
||||
cpp += out
|
||||
hpp += "};\n"
|
||||
|
22
tests/components/pi4ioe5v6408/common.yaml
Normal file
22
tests/components/pi4ioe5v6408/common.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
i2c:
|
||||
id: i2c_pi4ioe5v6408
|
||||
sda: ${i2c_sda}
|
||||
scl: ${i2c_scl}
|
||||
|
||||
pi4ioe5v6408:
|
||||
id: pi4ioe1
|
||||
address: 0x44
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
id: switch1
|
||||
pin:
|
||||
pi4ioe5v6408: pi4ioe1
|
||||
number: 0
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
id: sensor1
|
||||
pin:
|
||||
pi4ioe5v6408: pi4ioe1
|
||||
number: 1
|
5
tests/components/pi4ioe5v6408/test.esp32-ard.yaml
Normal file
5
tests/components/pi4ioe5v6408/test.esp32-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
i2c_sda: GPIO21
|
||||
i2c_scl: GPIO22
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/pi4ioe5v6408/test.esp32-idf.yaml
Normal file
5
tests/components/pi4ioe5v6408/test.esp32-idf.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
i2c_sda: GPIO21
|
||||
i2c_scl: GPIO22
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/pi4ioe5v6408/test.rp2040-ard.yaml
Normal file
5
tests/components/pi4ioe5v6408/test.rp2040-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
i2c_sda: GPIO4
|
||||
i2c_scl: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
Loading…
x
Reference in New Issue
Block a user