mirror of
https://github.com/esphome/esphome.git
synced 2025-07-27 05:36:38 +00:00
Merge branch 'dev' into protobuf_magic_numbers
This commit is contained in:
commit
8652db0796
@ -732,7 +732,6 @@ message SubscribeLogsResponse {
|
|||||||
|
|
||||||
LogLevel level = 1;
|
LogLevel level = 1;
|
||||||
bytes message = 3;
|
bytes message = 3;
|
||||||
bool send_failed = 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== NOISE ENCRYPTION ====================
|
// ==================== NOISE ENCRYPTION ====================
|
||||||
@ -1464,19 +1463,19 @@ message BluetoothGATTGetServicesRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message BluetoothGATTDescriptor {
|
message BluetoothGATTDescriptor {
|
||||||
repeated uint64 uuid = 1;
|
repeated uint64 uuid = 1 [(fixed_array_size) = 2];
|
||||||
uint32 handle = 2;
|
uint32 handle = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BluetoothGATTCharacteristic {
|
message BluetoothGATTCharacteristic {
|
||||||
repeated uint64 uuid = 1;
|
repeated uint64 uuid = 1 [(fixed_array_size) = 2];
|
||||||
uint32 handle = 2;
|
uint32 handle = 2;
|
||||||
uint32 properties = 3;
|
uint32 properties = 3;
|
||||||
repeated BluetoothGATTDescriptor descriptors = 4;
|
repeated BluetoothGATTDescriptor descriptors = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BluetoothGATTService {
|
message BluetoothGATTService {
|
||||||
repeated uint64 uuid = 1;
|
repeated uint64 uuid = 1 [(fixed_array_size) = 2];
|
||||||
uint32 handle = 2;
|
uint32 handle = 2;
|
||||||
repeated BluetoothGATTCharacteristic characteristics = 3;
|
repeated BluetoothGATTCharacteristic characteristics = 3;
|
||||||
}
|
}
|
||||||
@ -1487,7 +1486,7 @@ message BluetoothGATTGetServicesResponse {
|
|||||||
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
||||||
|
|
||||||
uint64 address = 1;
|
uint64 address = 1;
|
||||||
repeated BluetoothGATTService services = 2;
|
repeated BluetoothGATTService services = 2 [(fixed_array_size) = 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
message BluetoothGATTGetServicesDoneResponse {
|
message BluetoothGATTGetServicesDoneResponse {
|
||||||
|
@ -225,24 +225,16 @@ void APIConnection::loop() {
|
|||||||
if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
|
if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
|
||||||
uint32_t to_send = std::min((size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
|
uint32_t to_send = std::min((size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
|
||||||
bool done = this->image_reader_->available() == to_send;
|
bool done = this->image_reader_->available() == to_send;
|
||||||
uint32_t msg_size = 0;
|
|
||||||
ProtoSize::add_fixed_field<4>(msg_size, 1, true);
|
|
||||||
// partial message size calculated manually since its a special case
|
|
||||||
// 1 for the data field, varint for the data size, and the data itself
|
|
||||||
msg_size += 1 + ProtoSize::varint(to_send) + to_send;
|
|
||||||
ProtoSize::add_bool_field(msg_size, 1, done);
|
|
||||||
|
|
||||||
auto buffer = this->create_buffer(msg_size);
|
CameraImageResponse msg;
|
||||||
// fixed32 key = 1;
|
msg.key = camera::Camera::instance()->get_object_id_hash();
|
||||||
buffer.encode_fixed32(1, camera::Camera::instance()->get_object_id_hash());
|
msg.set_data(this->image_reader_->peek_data_buffer(), to_send);
|
||||||
// bytes data = 2;
|
msg.done = done;
|
||||||
buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send);
|
#ifdef USE_DEVICES
|
||||||
// bool done = 3;
|
msg.device_id = camera::Camera::instance()->get_device_id();
|
||||||
buffer.encode_bool(3, done);
|
#endif
|
||||||
|
|
||||||
bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
|
if (this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) {
|
||||||
|
|
||||||
if (success) {
|
|
||||||
this->image_reader_->consume_data(to_send);
|
this->image_reader_->consume_data(to_send);
|
||||||
if (done) {
|
if (done) {
|
||||||
this->image_reader_->return_image();
|
this->image_reader_->return_image();
|
||||||
@ -256,8 +248,10 @@ void APIConnection::loop() {
|
|||||||
if (state_subs_at_ < static_cast<int>(subs.size())) {
|
if (state_subs_at_ < static_cast<int>(subs.size())) {
|
||||||
auto &it = subs[state_subs_at_];
|
auto &it = subs[state_subs_at_];
|
||||||
SubscribeHomeAssistantStateResponse resp;
|
SubscribeHomeAssistantStateResponse resp;
|
||||||
resp.entity_id = it.entity_id;
|
resp.set_entity_id(StringRef(it.entity_id));
|
||||||
resp.attribute = it.attribute.value();
|
// attribute.value() returns temporary - must store it
|
||||||
|
std::string attribute_value = it.attribute.value();
|
||||||
|
resp.set_attribute(StringRef(attribute_value));
|
||||||
resp.once = it.once;
|
resp.once = it.once;
|
||||||
if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
|
if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
|
||||||
state_subs_at_++;
|
state_subs_at_++;
|
||||||
@ -268,14 +262,14 @@ void APIConnection::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
bool APIConnection::send_disconnect_response(const DisconnectRequest &msg) {
|
||||||
// remote initiated disconnect_client
|
// remote initiated disconnect_client
|
||||||
// don't close yet, we still need to send the disconnect response
|
// don't close yet, we still need to send the disconnect response
|
||||||
// close will happen on next loop
|
// close will happen on next loop
|
||||||
ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
|
ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
|
||||||
this->flags_.next_close = true;
|
this->flags_.next_close = true;
|
||||||
DisconnectResponse resp;
|
DisconnectResponse resp;
|
||||||
return resp;
|
return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
||||||
this->helper_->close();
|
this->helper_->close();
|
||||||
@ -352,7 +346,7 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
|
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
|
||||||
ListEntitiesBinarySensorResponse msg;
|
ListEntitiesBinarySensorResponse msg;
|
||||||
msg.device_class = binary_sensor->get_device_class();
|
msg.set_device_class(binary_sensor->get_device_class_ref());
|
||||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||||
return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size, is_single);
|
remaining_size, is_single);
|
||||||
@ -384,7 +378,7 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
|
|||||||
msg.supports_position = traits.get_supports_position();
|
msg.supports_position = traits.get_supports_position();
|
||||||
msg.supports_tilt = traits.get_supports_tilt();
|
msg.supports_tilt = traits.get_supports_tilt();
|
||||||
msg.supports_stop = traits.get_supports_stop();
|
msg.supports_stop = traits.get_supports_stop();
|
||||||
msg.device_class = cover->get_device_class();
|
msg.set_device_class(cover->get_device_class_ref());
|
||||||
return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
is_single);
|
is_single);
|
||||||
}
|
}
|
||||||
@ -419,7 +413,7 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co
|
|||||||
if (traits.supports_direction())
|
if (traits.supports_direction())
|
||||||
msg.direction = static_cast<enums::FanDirection>(fan->direction);
|
msg.direction = static_cast<enums::FanDirection>(fan->direction);
|
||||||
if (traits.supports_preset_modes())
|
if (traits.supports_preset_modes())
|
||||||
msg.preset_mode = fan->preset_mode;
|
msg.set_preset_mode(StringRef(fan->preset_mode));
|
||||||
return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@ -476,8 +470,11 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *
|
|||||||
resp.color_temperature = values.get_color_temperature();
|
resp.color_temperature = values.get_color_temperature();
|
||||||
resp.cold_white = values.get_cold_white();
|
resp.cold_white = values.get_cold_white();
|
||||||
resp.warm_white = values.get_warm_white();
|
resp.warm_white = values.get_warm_white();
|
||||||
if (light->supports_effects())
|
if (light->supports_effects()) {
|
||||||
resp.effect = light->get_effect_name();
|
// get_effect_name() returns temporary std::string - must store it
|
||||||
|
std::string effect_name = light->get_effect_name();
|
||||||
|
resp.set_effect(StringRef(effect_name));
|
||||||
|
}
|
||||||
return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@ -553,10 +550,10 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *sensor = static_cast<sensor::Sensor *>(entity);
|
auto *sensor = static_cast<sensor::Sensor *>(entity);
|
||||||
ListEntitiesSensorResponse msg;
|
ListEntitiesSensorResponse msg;
|
||||||
msg.unit_of_measurement = sensor->get_unit_of_measurement();
|
msg.set_unit_of_measurement(sensor->get_unit_of_measurement_ref());
|
||||||
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
||||||
msg.force_update = sensor->get_force_update();
|
msg.force_update = sensor->get_force_update();
|
||||||
msg.device_class = sensor->get_device_class();
|
msg.set_device_class(sensor->get_device_class_ref());
|
||||||
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
|
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
|
||||||
return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
is_single);
|
is_single);
|
||||||
@ -583,7 +580,7 @@ uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *
|
|||||||
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
||||||
ListEntitiesSwitchResponse msg;
|
ListEntitiesSwitchResponse msg;
|
||||||
msg.assumed_state = a_switch->assumed_state();
|
msg.assumed_state = a_switch->assumed_state();
|
||||||
msg.device_class = a_switch->get_device_class();
|
msg.set_device_class(a_switch->get_device_class_ref());
|
||||||
return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
is_single);
|
is_single);
|
||||||
}
|
}
|
||||||
@ -608,7 +605,7 @@ uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnec
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
||||||
TextSensorStateResponse resp;
|
TextSensorStateResponse resp;
|
||||||
resp.state = text_sensor->state;
|
resp.set_state(StringRef(text_sensor->state));
|
||||||
resp.missing_state = !text_sensor->has_state();
|
resp.missing_state = !text_sensor->has_state();
|
||||||
return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
is_single);
|
is_single);
|
||||||
@ -617,7 +614,7 @@ uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnect
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
|
||||||
ListEntitiesTextSensorResponse msg;
|
ListEntitiesTextSensorResponse msg;
|
||||||
msg.device_class = text_sensor->get_device_class();
|
msg.set_device_class(text_sensor->get_device_class_ref());
|
||||||
return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size, is_single);
|
remaining_size, is_single);
|
||||||
}
|
}
|
||||||
@ -645,13 +642,19 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
|
|||||||
}
|
}
|
||||||
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
|
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
|
||||||
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
|
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
|
||||||
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
|
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) {
|
||||||
resp.custom_fan_mode = climate->custom_fan_mode.value();
|
// custom_fan_mode.value() returns temporary - must store it
|
||||||
|
std::string custom_fan_mode = climate->custom_fan_mode.value();
|
||||||
|
resp.set_custom_fan_mode(StringRef(custom_fan_mode));
|
||||||
|
}
|
||||||
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
||||||
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
||||||
}
|
}
|
||||||
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
|
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) {
|
||||||
resp.custom_preset = climate->custom_preset.value();
|
// custom_preset.value() returns temporary - must store it
|
||||||
|
std::string custom_preset = climate->custom_preset.value();
|
||||||
|
resp.set_custom_preset(StringRef(custom_preset));
|
||||||
|
}
|
||||||
if (traits.get_supports_swing_modes())
|
if (traits.get_supports_swing_modes())
|
||||||
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
||||||
if (traits.get_supports_current_humidity())
|
if (traits.get_supports_current_humidity())
|
||||||
@ -737,9 +740,9 @@ uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *number = static_cast<number::Number *>(entity);
|
auto *number = static_cast<number::Number *>(entity);
|
||||||
ListEntitiesNumberResponse msg;
|
ListEntitiesNumberResponse msg;
|
||||||
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
|
msg.set_unit_of_measurement(number->traits.get_unit_of_measurement_ref());
|
||||||
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
|
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
|
||||||
msg.device_class = number->traits.get_device_class();
|
msg.set_device_class(number->traits.get_device_class_ref());
|
||||||
msg.min_value = number->traits.get_min_value();
|
msg.min_value = number->traits.get_min_value();
|
||||||
msg.max_value = number->traits.get_max_value();
|
msg.max_value = number->traits.get_max_value();
|
||||||
msg.step = number->traits.get_step();
|
msg.step = number->traits.get_step();
|
||||||
@ -852,7 +855,7 @@ uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *c
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *text = static_cast<text::Text *>(entity);
|
auto *text = static_cast<text::Text *>(entity);
|
||||||
TextStateResponse resp;
|
TextStateResponse resp;
|
||||||
resp.state = text->state;
|
resp.set_state(StringRef(text->state));
|
||||||
resp.missing_state = !text->has_state();
|
resp.missing_state = !text->has_state();
|
||||||
return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
@ -864,7 +867,7 @@ uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *co
|
|||||||
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
|
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
|
||||||
msg.min_length = text->traits.get_min_length();
|
msg.min_length = text->traits.get_min_length();
|
||||||
msg.max_length = text->traits.get_max_length();
|
msg.max_length = text->traits.get_max_length();
|
||||||
msg.pattern = text->traits.get_pattern();
|
msg.set_pattern(text->traits.get_pattern_ref());
|
||||||
return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
is_single);
|
is_single);
|
||||||
}
|
}
|
||||||
@ -885,7 +888,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *select = static_cast<select::Select *>(entity);
|
auto *select = static_cast<select::Select *>(entity);
|
||||||
SelectStateResponse resp;
|
SelectStateResponse resp;
|
||||||
resp.state = select->state;
|
resp.set_state(StringRef(select->state));
|
||||||
resp.missing_state = !select->has_state();
|
resp.missing_state = !select->has_state();
|
||||||
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
@ -911,7 +914,7 @@ uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *button = static_cast<button::Button *>(entity);
|
auto *button = static_cast<button::Button *>(entity);
|
||||||
ListEntitiesButtonResponse msg;
|
ListEntitiesButtonResponse msg;
|
||||||
msg.device_class = button->get_device_class();
|
msg.set_device_class(button->get_device_class_ref());
|
||||||
return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
is_single);
|
is_single);
|
||||||
}
|
}
|
||||||
@ -980,7 +983,7 @@ uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *c
|
|||||||
auto *valve = static_cast<valve::Valve *>(entity);
|
auto *valve = static_cast<valve::Valve *>(entity);
|
||||||
ListEntitiesValveResponse msg;
|
ListEntitiesValveResponse msg;
|
||||||
auto traits = valve->get_traits();
|
auto traits = valve->get_traits();
|
||||||
msg.device_class = valve->get_device_class();
|
msg.set_device_class(valve->get_device_class_ref());
|
||||||
msg.assumed_state = traits.get_is_assumed_state();
|
msg.assumed_state = traits.get_is_assumed_state();
|
||||||
msg.supports_position = traits.get_supports_position();
|
msg.supports_position = traits.get_supports_position();
|
||||||
msg.supports_stop = traits.get_supports_stop();
|
msg.supports_stop = traits.get_supports_stop();
|
||||||
@ -1022,13 +1025,13 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
|
|||||||
auto traits = media_player->get_traits();
|
auto traits = media_player->get_traits();
|
||||||
msg.supports_pause = traits.get_supports_pause();
|
msg.supports_pause = traits.get_supports_pause();
|
||||||
for (auto &supported_format : traits.get_supported_formats()) {
|
for (auto &supported_format : traits.get_supported_formats()) {
|
||||||
MediaPlayerSupportedFormat media_format;
|
msg.supported_formats.emplace_back();
|
||||||
media_format.format = supported_format.format;
|
auto &media_format = msg.supported_formats.back();
|
||||||
|
media_format.set_format(StringRef(supported_format.format));
|
||||||
media_format.sample_rate = supported_format.sample_rate;
|
media_format.sample_rate = supported_format.sample_rate;
|
||||||
media_format.num_channels = supported_format.num_channels;
|
media_format.num_channels = supported_format.num_channels;
|
||||||
media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose);
|
media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose);
|
||||||
media_format.sample_bytes = supported_format.sample_bytes;
|
media_format.sample_bytes = supported_format.sample_bytes;
|
||||||
msg.supported_formats.push_back(media_format);
|
|
||||||
}
|
}
|
||||||
return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
|
return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
|
||||||
remaining_size, is_single);
|
remaining_size, is_single);
|
||||||
@ -1091,6 +1094,12 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool APIConnection::send_get_time_response(const GetTimeRequest &msg) {
|
||||||
|
GetTimeResponse resp;
|
||||||
|
resp.epoch_seconds = ::time(nullptr);
|
||||||
|
return this->send_message(resp, GetTimeResponse::MESSAGE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
|
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
|
||||||
@ -1121,12 +1130,12 @@ void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg)
|
|||||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg);
|
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_free(
|
bool APIConnection::send_subscribe_bluetooth_connections_free_response(
|
||||||
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
||||||
BluetoothConnectionsFreeResponse resp;
|
BluetoothConnectionsFreeResponse resp;
|
||||||
resp.free = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_free();
|
resp.free = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_free();
|
||||||
resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit();
|
resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit();
|
||||||
return resp;
|
return this->send_message(resp, BluetoothConnectionsFreeResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) {
|
void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) {
|
||||||
@ -1187,28 +1196,27 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration(
|
bool APIConnection::send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) {
|
||||||
const VoiceAssistantConfigurationRequest &msg) {
|
|
||||||
VoiceAssistantConfigurationResponse resp;
|
VoiceAssistantConfigurationResponse resp;
|
||||||
if (!this->check_voice_assistant_api_connection_()) {
|
if (!this->check_voice_assistant_api_connection_()) {
|
||||||
return resp;
|
return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &config = voice_assistant::global_voice_assistant->get_configuration();
|
auto &config = voice_assistant::global_voice_assistant->get_configuration();
|
||||||
for (auto &wake_word : config.available_wake_words) {
|
for (auto &wake_word : config.available_wake_words) {
|
||||||
VoiceAssistantWakeWord resp_wake_word;
|
resp.available_wake_words.emplace_back();
|
||||||
resp_wake_word.id = wake_word.id;
|
auto &resp_wake_word = resp.available_wake_words.back();
|
||||||
resp_wake_word.wake_word = wake_word.wake_word;
|
resp_wake_word.set_id(StringRef(wake_word.id));
|
||||||
|
resp_wake_word.set_wake_word(StringRef(wake_word.wake_word));
|
||||||
for (const auto &lang : wake_word.trained_languages) {
|
for (const auto &lang : wake_word.trained_languages) {
|
||||||
resp_wake_word.trained_languages.push_back(lang);
|
resp_wake_word.trained_languages.push_back(lang);
|
||||||
}
|
}
|
||||||
resp.available_wake_words.push_back(std::move(resp_wake_word));
|
|
||||||
}
|
}
|
||||||
for (auto &wake_word_id : config.active_wake_words) {
|
for (auto &wake_word_id : config.active_wake_words) {
|
||||||
resp.active_wake_words.push_back(wake_word_id);
|
resp.active_wake_words.push_back(wake_word_id);
|
||||||
}
|
}
|
||||||
resp.max_active_wake_words = config.max_active_wake_words;
|
resp.max_active_wake_words = config.max_active_wake_words;
|
||||||
return resp;
|
return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
|
void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
|
||||||
@ -1281,7 +1289,7 @@ void APIConnection::send_event(event::Event *event, const std::string &event_typ
|
|||||||
uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
|
uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
|
||||||
uint32_t remaining_size, bool is_single) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
EventResponse resp;
|
EventResponse resp;
|
||||||
resp.event_type = event_type;
|
resp.set_event_type(StringRef(event_type));
|
||||||
return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1289,7 +1297,7 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *event = static_cast<event::Event *>(entity);
|
auto *event = static_cast<event::Event *>(entity);
|
||||||
ListEntitiesEventResponse msg;
|
ListEntitiesEventResponse msg;
|
||||||
msg.device_class = event->get_device_class();
|
msg.set_device_class(event->get_device_class_ref());
|
||||||
for (const auto &event_type : event->get_event_types())
|
for (const auto &event_type : event->get_event_types())
|
||||||
msg.event_types.push_back(event_type);
|
msg.event_types.push_back(event_type);
|
||||||
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
@ -1313,11 +1321,11 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection
|
|||||||
resp.has_progress = true;
|
resp.has_progress = true;
|
||||||
resp.progress = update->update_info.progress;
|
resp.progress = update->update_info.progress;
|
||||||
}
|
}
|
||||||
resp.current_version = update->update_info.current_version;
|
resp.set_current_version(StringRef(update->update_info.current_version));
|
||||||
resp.latest_version = update->update_info.latest_version;
|
resp.set_latest_version(StringRef(update->update_info.latest_version));
|
||||||
resp.title = update->update_info.title;
|
resp.set_title(StringRef(update->update_info.title));
|
||||||
resp.release_summary = update->update_info.summary;
|
resp.set_release_summary(StringRef(update->update_info.summary));
|
||||||
resp.release_url = update->update_info.release_url;
|
resp.set_release_url(StringRef(update->update_info.release_url));
|
||||||
}
|
}
|
||||||
return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
@ -1325,7 +1333,7 @@ uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *update = static_cast<update::UpdateEntity *>(entity);
|
auto *update = static_cast<update::UpdateEntity *>(entity);
|
||||||
ListEntitiesUpdateResponse msg;
|
ListEntitiesUpdateResponse msg;
|
||||||
msg.device_class = update->get_device_class();
|
msg.set_device_class(update->get_device_class_ref());
|
||||||
return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size,
|
return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||||
is_single);
|
is_single);
|
||||||
}
|
}
|
||||||
@ -1350,26 +1358,10 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool APIConnection::try_send_log_message(int level, const char *tag, const char *line, size_t message_len) {
|
bool APIConnection::try_send_log_message(int level, const char *tag, const char *line, size_t message_len) {
|
||||||
// Pre-calculate message size to avoid reallocations
|
SubscribeLogsResponse msg;
|
||||||
uint32_t msg_size = 0;
|
msg.level = static_cast<enums::LogLevel>(level);
|
||||||
|
msg.set_message(reinterpret_cast<const uint8_t *>(line), message_len);
|
||||||
// Add size for level field (field ID 1, varint type)
|
return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE);
|
||||||
// 1 byte for field tag + size of the level varint
|
|
||||||
msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(level));
|
|
||||||
|
|
||||||
// Add size for string field (field ID 3, string type)
|
|
||||||
// 1 byte for field tag + size of length varint + string length
|
|
||||||
msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(message_len)) + message_len;
|
|
||||||
|
|
||||||
// Create a pre-sized buffer
|
|
||||||
auto buffer = this->create_buffer(msg_size);
|
|
||||||
|
|
||||||
// Encode the message (SubscribeLogsResponse)
|
|
||||||
buffer.encode_uint32(1, static_cast<uint32_t>(level)); // LogLevel level = 1
|
|
||||||
buffer.encode_string(3, line, message_len); // string message = 3
|
|
||||||
|
|
||||||
// SubscribeLogsResponse - 29
|
|
||||||
return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIConnection::complete_authentication_() {
|
void APIConnection::complete_authentication_() {
|
||||||
@ -1390,7 +1382,7 @@ void APIConnection::complete_authentication_() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
bool APIConnection::send_hello_response(const HelloRequest &msg) {
|
||||||
this->client_info_.name = msg.client_info;
|
this->client_info_.name = msg.client_info;
|
||||||
this->client_info_.peername = this->helper_->getpeername();
|
this->client_info_.peername = this->helper_->getpeername();
|
||||||
this->client_api_version_major_ = msg.api_version_major;
|
this->client_api_version_major_ = msg.api_version_major;
|
||||||
@ -1401,8 +1393,10 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
|||||||
HelloResponse resp;
|
HelloResponse resp;
|
||||||
resp.api_version_major = 1;
|
resp.api_version_major = 1;
|
||||||
resp.api_version_minor = 10;
|
resp.api_version_minor = 10;
|
||||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
// Temporary string for concatenation - will be valid during send_message call
|
||||||
resp.name = App.get_name();
|
std::string server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||||
|
resp.set_server_info(StringRef(server_info));
|
||||||
|
resp.set_name(StringRef(App.get_name()));
|
||||||
|
|
||||||
#ifdef USE_API_PASSWORD
|
#ifdef USE_API_PASSWORD
|
||||||
// Password required - wait for authentication
|
// Password required - wait for authentication
|
||||||
@ -1412,9 +1406,9 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
|||||||
this->complete_authentication_();
|
this->complete_authentication_();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return resp;
|
return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
|
bool APIConnection::send_connect_response(const ConnectRequest &msg) {
|
||||||
bool correct = true;
|
bool correct = true;
|
||||||
#ifdef USE_API_PASSWORD
|
#ifdef USE_API_PASSWORD
|
||||||
correct = this->parent_->check_password(msg.password);
|
correct = this->parent_->check_password(msg.password);
|
||||||
@ -1426,48 +1420,71 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
|
|||||||
if (correct) {
|
if (correct) {
|
||||||
this->complete_authentication_();
|
this->complete_authentication_();
|
||||||
}
|
}
|
||||||
return resp;
|
return this->send_message(resp, ConnectResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
|
||||||
|
bool APIConnection::send_ping_response(const PingRequest &msg) {
|
||||||
|
PingResponse resp;
|
||||||
|
return this->send_message(resp, PingResponse::MESSAGE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
|
||||||
DeviceInfoResponse resp{};
|
DeviceInfoResponse resp{};
|
||||||
#ifdef USE_API_PASSWORD
|
#ifdef USE_API_PASSWORD
|
||||||
resp.uses_password = true;
|
resp.uses_password = true;
|
||||||
#endif
|
#endif
|
||||||
resp.name = App.get_name();
|
resp.set_name(StringRef(App.get_name()));
|
||||||
resp.friendly_name = App.get_friendly_name();
|
resp.set_friendly_name(StringRef(App.get_friendly_name()));
|
||||||
#ifdef USE_AREAS
|
#ifdef USE_AREAS
|
||||||
resp.suggested_area = App.get_area();
|
resp.set_suggested_area(StringRef(App.get_area()));
|
||||||
#endif
|
#endif
|
||||||
resp.mac_address = get_mac_address_pretty();
|
// mac_address must store temporary string - will be valid during send_message call
|
||||||
resp.esphome_version = ESPHOME_VERSION;
|
std::string mac_address = get_mac_address_pretty();
|
||||||
resp.compilation_time = App.get_compilation_time();
|
resp.set_mac_address(StringRef(mac_address));
|
||||||
|
|
||||||
|
// Compile-time StringRef constants
|
||||||
|
static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION);
|
||||||
|
resp.set_esphome_version(ESPHOME_VERSION_REF);
|
||||||
|
|
||||||
|
// get_compilation_time() returns temporary std::string - must store it
|
||||||
|
std::string compilation_time = App.get_compilation_time();
|
||||||
|
resp.set_compilation_time(StringRef(compilation_time));
|
||||||
|
|
||||||
|
// Compile-time StringRef constants for manufacturers
|
||||||
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
||||||
resp.manufacturer = "Espressif";
|
static constexpr auto MANUFACTURER = StringRef::from_lit("Espressif");
|
||||||
#elif defined(USE_RP2040)
|
#elif defined(USE_RP2040)
|
||||||
resp.manufacturer = "Raspberry Pi";
|
static constexpr auto MANUFACTURER = StringRef::from_lit("Raspberry Pi");
|
||||||
#elif defined(USE_BK72XX)
|
#elif defined(USE_BK72XX)
|
||||||
resp.manufacturer = "Beken";
|
static constexpr auto MANUFACTURER = StringRef::from_lit("Beken");
|
||||||
#elif defined(USE_LN882X)
|
#elif defined(USE_LN882X)
|
||||||
resp.manufacturer = "Lightning";
|
static constexpr auto MANUFACTURER = StringRef::from_lit("Lightning");
|
||||||
#elif defined(USE_RTL87XX)
|
#elif defined(USE_RTL87XX)
|
||||||
resp.manufacturer = "Realtek";
|
static constexpr auto MANUFACTURER = StringRef::from_lit("Realtek");
|
||||||
#elif defined(USE_HOST)
|
#elif defined(USE_HOST)
|
||||||
resp.manufacturer = "Host";
|
static constexpr auto MANUFACTURER = StringRef::from_lit("Host");
|
||||||
#endif
|
#endif
|
||||||
resp.model = ESPHOME_BOARD;
|
resp.set_manufacturer(MANUFACTURER);
|
||||||
|
|
||||||
|
static constexpr auto MODEL = StringRef::from_lit(ESPHOME_BOARD);
|
||||||
|
resp.set_model(MODEL);
|
||||||
#ifdef USE_DEEP_SLEEP
|
#ifdef USE_DEEP_SLEEP
|
||||||
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
|
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
|
||||||
#endif
|
#endif
|
||||||
#ifdef ESPHOME_PROJECT_NAME
|
#ifdef ESPHOME_PROJECT_NAME
|
||||||
resp.project_name = ESPHOME_PROJECT_NAME;
|
static constexpr auto PROJECT_NAME = StringRef::from_lit(ESPHOME_PROJECT_NAME);
|
||||||
resp.project_version = ESPHOME_PROJECT_VERSION;
|
static constexpr auto PROJECT_VERSION = StringRef::from_lit(ESPHOME_PROJECT_VERSION);
|
||||||
|
resp.set_project_name(PROJECT_NAME);
|
||||||
|
resp.set_project_version(PROJECT_VERSION);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_WEBSERVER
|
#ifdef USE_WEBSERVER
|
||||||
resp.webserver_port = USE_WEBSERVER_PORT;
|
resp.webserver_port = USE_WEBSERVER_PORT;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
||||||
resp.bluetooth_mac_address = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty();
|
// bt_mac must store temporary string - will be valid during send_message call
|
||||||
|
std::string bluetooth_mac = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty();
|
||||||
|
resp.set_bluetooth_mac_address(StringRef(bluetooth_mac));
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
|
resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
|
||||||
@ -1477,23 +1494,25 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
for (auto const &device : App.get_devices()) {
|
for (auto const &device : App.get_devices()) {
|
||||||
DeviceInfo device_info;
|
resp.devices.emplace_back();
|
||||||
|
auto &device_info = resp.devices.back();
|
||||||
device_info.device_id = device->get_device_id();
|
device_info.device_id = device->get_device_id();
|
||||||
device_info.name = device->get_name();
|
device_info.set_name(StringRef(device->get_name()));
|
||||||
device_info.area_id = device->get_area_id();
|
device_info.area_id = device->get_area_id();
|
||||||
resp.devices.push_back(device_info);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_AREAS
|
#ifdef USE_AREAS
|
||||||
for (auto const &area : App.get_areas()) {
|
for (auto const &area : App.get_areas()) {
|
||||||
AreaInfo area_info;
|
resp.areas.emplace_back();
|
||||||
|
auto &area_info = resp.areas.back();
|
||||||
area_info.area_id = area->get_area_id();
|
area_info.area_id = area->get_area_id();
|
||||||
area_info.name = area->get_name();
|
area_info.set_name(StringRef(area->get_name()));
|
||||||
resp.areas.push_back(area_info);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return resp;
|
|
||||||
|
return this->send_message(resp, DeviceInfoResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
||||||
for (auto &it : this->parent_->get_state_subs()) {
|
for (auto &it : this->parent_->get_state_subs()) {
|
||||||
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
|
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
|
||||||
@ -1515,23 +1534,21 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) {
|
bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
|
||||||
psk_t psk{};
|
psk_t psk{};
|
||||||
NoiseEncryptionSetKeyResponse resp;
|
NoiseEncryptionSetKeyResponse resp;
|
||||||
if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
|
if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
|
||||||
ESP_LOGW(TAG, "Invalid encryption key length");
|
ESP_LOGW(TAG, "Invalid encryption key length");
|
||||||
resp.success = false;
|
resp.success = false;
|
||||||
return resp;
|
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->parent_->save_noise_psk(psk, true)) {
|
if (!this->parent_->save_noise_psk(psk, true)) {
|
||||||
ESP_LOGW(TAG, "Failed to save encryption key");
|
ESP_LOGW(TAG, "Failed to save encryption key");
|
||||||
resp.success = false;
|
resp.success = false;
|
||||||
return resp;
|
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.success = true;
|
resp.success = true;
|
||||||
return resp;
|
return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
|
void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
|
||||||
|
@ -148,8 +148,7 @@ class APIConnection : public APIServerConnection {
|
|||||||
void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
|
void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
|
||||||
void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
|
void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
|
||||||
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
|
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
|
||||||
BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
|
bool send_subscribe_bluetooth_connections_free_response(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
|
||||||
const SubscribeBluetoothConnectionsFreeRequest &msg) override;
|
|
||||||
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
|
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -167,8 +166,7 @@ class APIConnection : public APIServerConnection {
|
|||||||
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
||||||
void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
|
void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
|
||||||
void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override;
|
void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override;
|
||||||
VoiceAssistantConfigurationResponse voice_assistant_get_configuration(
|
bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) override;
|
||||||
const VoiceAssistantConfigurationRequest &msg) override;
|
|
||||||
void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
|
void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -195,11 +193,11 @@ class APIConnection : public APIServerConnection {
|
|||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
void on_get_time_response(const GetTimeResponse &value) override;
|
void on_get_time_response(const GetTimeResponse &value) override;
|
||||||
#endif
|
#endif
|
||||||
HelloResponse hello(const HelloRequest &msg) override;
|
bool send_hello_response(const HelloRequest &msg) override;
|
||||||
ConnectResponse connect(const ConnectRequest &msg) override;
|
bool send_connect_response(const ConnectRequest &msg) override;
|
||||||
DisconnectResponse disconnect(const DisconnectRequest &msg) override;
|
bool send_disconnect_response(const DisconnectRequest &msg) override;
|
||||||
PingResponse ping(const PingRequest &msg) override { return {}; }
|
bool send_ping_response(const PingRequest &msg) override;
|
||||||
DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override;
|
bool send_device_info_response(const DeviceInfoRequest &msg) override;
|
||||||
void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); }
|
void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); }
|
||||||
void subscribe_states(const SubscribeStatesRequest &msg) override {
|
void subscribe_states(const SubscribeStatesRequest &msg) override {
|
||||||
this->flags_.state_subscription = true;
|
this->flags_.state_subscription = true;
|
||||||
@ -214,15 +212,12 @@ class APIConnection : public APIServerConnection {
|
|||||||
this->flags_.service_call_subscription = true;
|
this->flags_.service_call_subscription = true;
|
||||||
}
|
}
|
||||||
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
|
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||||
GetTimeResponse get_time(const GetTimeRequest &msg) override {
|
bool send_get_time_response(const GetTimeRequest &msg) override;
|
||||||
// TODO
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
#ifdef USE_API_SERVICES
|
#ifdef USE_API_SERVICES
|
||||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override;
|
bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool is_authenticated() override {
|
bool is_authenticated() override {
|
||||||
@ -313,14 +308,17 @@ class APIConnection : public APIServerConnection {
|
|||||||
APIConnection *conn, uint32_t remaining_size, bool is_single) {
|
APIConnection *conn, uint32_t remaining_size, bool is_single) {
|
||||||
// Set common fields that are shared by all entity types
|
// Set common fields that are shared by all entity types
|
||||||
msg.key = entity->get_object_id_hash();
|
msg.key = entity->get_object_id_hash();
|
||||||
msg.object_id = entity->get_object_id();
|
// IMPORTANT: get_object_id() may return a temporary std::string
|
||||||
|
std::string object_id = entity->get_object_id();
|
||||||
|
msg.set_object_id(StringRef(object_id));
|
||||||
|
|
||||||
if (entity->has_own_name())
|
if (entity->has_own_name()) {
|
||||||
msg.name = entity->get_name();
|
msg.set_name(entity->get_name());
|
||||||
|
}
|
||||||
|
|
||||||
// Set common EntityBase properties
|
// Set common EntityBase properties
|
||||||
#ifdef USE_ENTITY_ICON
|
#ifdef USE_ENTITY_ICON
|
||||||
msg.icon = entity->get_icon();
|
msg.set_icon(entity->get_icon_ref());
|
||||||
#endif
|
#endif
|
||||||
msg.disabled_by_default = entity->is_disabled_by_default();
|
msg.disabled_by_default = entity->is_disabled_by_default();
|
||||||
msg.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
|
msg.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
|
||||||
|
@ -15,6 +15,7 @@ namespace api {
|
|||||||
|
|
||||||
static const char *const TAG = "api.noise";
|
static const char *const TAG = "api.noise";
|
||||||
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
|
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
|
||||||
|
static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit")
|
||||||
|
|
||||||
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->client_info_->get_combined_info().c_str(), ##__VA_ARGS__)
|
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->client_info_->get_combined_info().c_str(), ##__VA_ARGS__)
|
||||||
|
|
||||||
@ -73,7 +74,9 @@ APIError APINoiseFrameHelper::init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init prologue
|
// init prologue
|
||||||
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
size_t old_size = prologue_.size();
|
||||||
|
prologue_.resize(old_size + PROLOGUE_INIT_LEN);
|
||||||
|
std::memcpy(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
|
||||||
|
|
||||||
state_ = State::CLIENT_HELLO;
|
state_ = State::CLIENT_HELLO;
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
@ -223,11 +226,12 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
return handle_handshake_frame_error_(aerr);
|
return handle_handshake_frame_error_(aerr);
|
||||||
}
|
}
|
||||||
// ignore contents, may be used in future for flags
|
// ignore contents, may be used in future for flags
|
||||||
// Reserve space for: existing prologue + 2 size bytes + frame data
|
// Resize for: existing prologue + 2 size bytes + frame data
|
||||||
prologue_.reserve(prologue_.size() + 2 + frame.size());
|
size_t old_size = prologue_.size();
|
||||||
prologue_.push_back((uint8_t) (frame.size() >> 8));
|
prologue_.resize(old_size + 2 + frame.size());
|
||||||
prologue_.push_back((uint8_t) frame.size());
|
prologue_[old_size] = (uint8_t) (frame.size() >> 8);
|
||||||
prologue_.insert(prologue_.end(), frame.begin(), frame.end());
|
prologue_[old_size + 1] = (uint8_t) frame.size();
|
||||||
|
std::memcpy(prologue_.data() + old_size + 2, frame.data(), frame.size());
|
||||||
|
|
||||||
state_ = State::SERVER_HELLO;
|
state_ = State::SERVER_HELLO;
|
||||||
}
|
}
|
||||||
@ -237,18 +241,22 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
const std::string &mac = get_mac_address();
|
const std::string &mac = get_mac_address();
|
||||||
|
|
||||||
std::vector<uint8_t> msg;
|
std::vector<uint8_t> msg;
|
||||||
// Reserve space for: 1 byte proto + name + null + mac + null
|
// Calculate positions and sizes
|
||||||
msg.reserve(1 + name.size() + 1 + mac.size() + 1);
|
size_t name_len = name.size() + 1; // including null terminator
|
||||||
|
size_t mac_len = mac.size() + 1; // including null terminator
|
||||||
|
size_t name_offset = 1;
|
||||||
|
size_t mac_offset = name_offset + name_len;
|
||||||
|
size_t total_size = 1 + name_len + mac_len;
|
||||||
|
|
||||||
|
msg.resize(total_size);
|
||||||
|
|
||||||
// chosen proto
|
// chosen proto
|
||||||
msg.push_back(0x01);
|
msg[0] = 0x01;
|
||||||
|
|
||||||
// node name, terminated by null byte
|
// node name, terminated by null byte
|
||||||
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
|
std::memcpy(msg.data() + name_offset, name.c_str(), name_len);
|
||||||
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
|
|
||||||
// node mac, terminated by null byte
|
// node mac, terminated by null byte
|
||||||
const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
|
std::memcpy(msg.data() + mac_offset, mac.c_str(), mac_len);
|
||||||
msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
|
|
||||||
|
|
||||||
aerr = write_frame_(msg.data(), msg.size());
|
aerr = write_frame_(msg.data(), msg.size());
|
||||||
if (aerr != APIError::OK)
|
if (aerr != APIError::OK)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
|
||||||
@ -269,12 +270,15 @@ enum UpdateCommand : uint32_t {
|
|||||||
class InfoResponseProtoMessage : public ProtoMessage {
|
class InfoResponseProtoMessage : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
~InfoResponseProtoMessage() override = default;
|
~InfoResponseProtoMessage() override = default;
|
||||||
std::string object_id{};
|
StringRef object_id_ref_{};
|
||||||
|
void set_object_id(const StringRef &ref) { this->object_id_ref_ = ref; }
|
||||||
uint32_t key{0};
|
uint32_t key{0};
|
||||||
std::string name{};
|
StringRef name_ref_{};
|
||||||
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
bool disabled_by_default{false};
|
bool disabled_by_default{false};
|
||||||
#ifdef USE_ENTITY_ICON
|
#ifdef USE_ENTITY_ICON
|
||||||
std::string icon{};
|
StringRef icon_ref_{};
|
||||||
|
void set_icon(const StringRef &ref) { this->icon_ref_ = ref; }
|
||||||
#endif
|
#endif
|
||||||
enums::EntityCategory entity_category{};
|
enums::EntityCategory entity_category{};
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
@ -332,8 +336,10 @@ class HelloResponse : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
uint32_t api_version_major{0};
|
uint32_t api_version_major{0};
|
||||||
uint32_t api_version_minor{0};
|
uint32_t api_version_minor{0};
|
||||||
std::string server_info{};
|
StringRef server_info_ref_{};
|
||||||
std::string name{};
|
void set_server_info(const StringRef &ref) { this->server_info_ref_ = ref; }
|
||||||
|
StringRef name_ref_{};
|
||||||
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -442,7 +448,8 @@ class DeviceInfoRequest : public ProtoDecodableMessage {
|
|||||||
class AreaInfo : public ProtoMessage {
|
class AreaInfo : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t area_id{0};
|
uint32_t area_id{0};
|
||||||
std::string name{};
|
StringRef name_ref_{};
|
||||||
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -456,7 +463,8 @@ class AreaInfo : public ProtoMessage {
|
|||||||
class DeviceInfo : public ProtoMessage {
|
class DeviceInfo : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t device_id{0};
|
uint32_t device_id{0};
|
||||||
std::string name{};
|
StringRef name_ref_{};
|
||||||
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
uint32_t area_id{0};
|
uint32_t area_id{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -477,19 +485,26 @@ class DeviceInfoResponse : public ProtoMessage {
|
|||||||
#ifdef USE_API_PASSWORD
|
#ifdef USE_API_PASSWORD
|
||||||
bool uses_password{false};
|
bool uses_password{false};
|
||||||
#endif
|
#endif
|
||||||
std::string name{};
|
StringRef name_ref_{};
|
||||||
std::string mac_address{};
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
std::string esphome_version{};
|
StringRef mac_address_ref_{};
|
||||||
std::string compilation_time{};
|
void set_mac_address(const StringRef &ref) { this->mac_address_ref_ = ref; }
|
||||||
std::string model{};
|
StringRef esphome_version_ref_{};
|
||||||
|
void set_esphome_version(const StringRef &ref) { this->esphome_version_ref_ = ref; }
|
||||||
|
StringRef compilation_time_ref_{};
|
||||||
|
void set_compilation_time(const StringRef &ref) { this->compilation_time_ref_ = ref; }
|
||||||
|
StringRef model_ref_{};
|
||||||
|
void set_model(const StringRef &ref) { this->model_ref_ = ref; }
|
||||||
#ifdef USE_DEEP_SLEEP
|
#ifdef USE_DEEP_SLEEP
|
||||||
bool has_deep_sleep{false};
|
bool has_deep_sleep{false};
|
||||||
#endif
|
#endif
|
||||||
#ifdef ESPHOME_PROJECT_NAME
|
#ifdef ESPHOME_PROJECT_NAME
|
||||||
std::string project_name{};
|
StringRef project_name_ref_{};
|
||||||
|
void set_project_name(const StringRef &ref) { this->project_name_ref_ = ref; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef ESPHOME_PROJECT_NAME
|
#ifdef ESPHOME_PROJECT_NAME
|
||||||
std::string project_version{};
|
StringRef project_version_ref_{};
|
||||||
|
void set_project_version(const StringRef &ref) { this->project_version_ref_ = ref; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_WEBSERVER
|
#ifdef USE_WEBSERVER
|
||||||
uint32_t webserver_port{0};
|
uint32_t webserver_port{0};
|
||||||
@ -497,16 +512,20 @@ class DeviceInfoResponse : public ProtoMessage {
|
|||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
uint32_t bluetooth_proxy_feature_flags{0};
|
uint32_t bluetooth_proxy_feature_flags{0};
|
||||||
#endif
|
#endif
|
||||||
std::string manufacturer{};
|
StringRef manufacturer_ref_{};
|
||||||
std::string friendly_name{};
|
void set_manufacturer(const StringRef &ref) { this->manufacturer_ref_ = ref; }
|
||||||
|
StringRef friendly_name_ref_{};
|
||||||
|
void set_friendly_name(const StringRef &ref) { this->friendly_name_ref_ = ref; }
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
uint32_t voice_assistant_feature_flags{0};
|
uint32_t voice_assistant_feature_flags{0};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_AREAS
|
#ifdef USE_AREAS
|
||||||
std::string suggested_area{};
|
StringRef suggested_area_ref_{};
|
||||||
|
void set_suggested_area(const StringRef &ref) { this->suggested_area_ref_ = ref; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
std::string bluetooth_mac_address{};
|
StringRef bluetooth_mac_address_ref_{};
|
||||||
|
void set_bluetooth_mac_address(const StringRef &ref) { this->bluetooth_mac_address_ref_ = ref; }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
bool api_encryption_supported{false};
|
bool api_encryption_supported{false};
|
||||||
@ -575,7 +594,8 @@ class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_binary_sensor_response"; }
|
const char *message_name() const override { return "list_entities_binary_sensor_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
bool is_status_binary_sensor{false};
|
bool is_status_binary_sensor{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -614,7 +634,8 @@ class ListEntitiesCoverResponse : public InfoResponseProtoMessage {
|
|||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
bool supports_position{false};
|
bool supports_position{false};
|
||||||
bool supports_tilt{false};
|
bool supports_tilt{false};
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
bool supports_stop{false};
|
bool supports_stop{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -695,7 +716,8 @@ class FanStateResponse : public StateResponseProtoMessage {
|
|||||||
bool oscillating{false};
|
bool oscillating{false};
|
||||||
enums::FanDirection direction{};
|
enums::FanDirection direction{};
|
||||||
int32_t speed_level{0};
|
int32_t speed_level{0};
|
||||||
std::string preset_mode{};
|
StringRef preset_mode_ref_{};
|
||||||
|
void set_preset_mode(const StringRef &ref) { this->preset_mode_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -769,7 +791,8 @@ class LightStateResponse : public StateResponseProtoMessage {
|
|||||||
float color_temperature{0.0f};
|
float color_temperature{0.0f};
|
||||||
float cold_white{0.0f};
|
float cold_white{0.0f};
|
||||||
float warm_white{0.0f};
|
float warm_white{0.0f};
|
||||||
std::string effect{};
|
StringRef effect_ref_{};
|
||||||
|
void set_effect(const StringRef &ref) { this->effect_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -829,10 +852,12 @@ class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_sensor_response"; }
|
const char *message_name() const override { return "list_entities_sensor_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string unit_of_measurement{};
|
StringRef unit_of_measurement_ref_{};
|
||||||
|
void set_unit_of_measurement(const StringRef &ref) { this->unit_of_measurement_ref_ = ref; }
|
||||||
int32_t accuracy_decimals{0};
|
int32_t accuracy_decimals{0};
|
||||||
bool force_update{false};
|
bool force_update{false};
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
enums::SensorStateClass state_class{};
|
enums::SensorStateClass state_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -869,7 +894,8 @@ class ListEntitiesSwitchResponse : public InfoResponseProtoMessage {
|
|||||||
const char *message_name() const override { return "list_entities_switch_response"; }
|
const char *message_name() const override { return "list_entities_switch_response"; }
|
||||||
#endif
|
#endif
|
||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -919,7 +945,8 @@ class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_text_sensor_response"; }
|
const char *message_name() const override { return "list_entities_text_sensor_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -935,7 +962,8 @@ class TextSensorStateResponse : public StateResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "text_sensor_state_response"; }
|
const char *message_name() const override { return "text_sensor_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string state{};
|
StringRef state_ref_{};
|
||||||
|
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -965,13 +993,17 @@ class SubscribeLogsRequest : public ProtoDecodableMessage {
|
|||||||
class SubscribeLogsResponse : public ProtoMessage {
|
class SubscribeLogsResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 29;
|
static constexpr uint8_t MESSAGE_TYPE = 29;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 13;
|
static constexpr uint8_t ESTIMATED_SIZE = 11;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "subscribe_logs_response"; }
|
const char *message_name() const override { return "subscribe_logs_response"; }
|
||||||
#endif
|
#endif
|
||||||
enums::LogLevel level{};
|
enums::LogLevel level{};
|
||||||
std::string message{};
|
const uint8_t *message_ptr_{nullptr};
|
||||||
bool send_failed{false};
|
size_t message_len_{0};
|
||||||
|
void set_message(const uint8_t *data, size_t len) {
|
||||||
|
this->message_ptr_ = data;
|
||||||
|
this->message_len_ = len;
|
||||||
|
}
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1028,8 +1060,10 @@ class SubscribeHomeassistantServicesRequest : public ProtoDecodableMessage {
|
|||||||
};
|
};
|
||||||
class HomeassistantServiceMap : public ProtoMessage {
|
class HomeassistantServiceMap : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string key{};
|
StringRef key_ref_{};
|
||||||
std::string value{};
|
void set_key(const StringRef &ref) { this->key_ref_ = ref; }
|
||||||
|
StringRef value_ref_{};
|
||||||
|
void set_value(const StringRef &ref) { this->value_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1045,7 +1079,8 @@ class HomeassistantServiceResponse : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "homeassistant_service_response"; }
|
const char *message_name() const override { return "homeassistant_service_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string service{};
|
StringRef service_ref_{};
|
||||||
|
void set_service(const StringRef &ref) { this->service_ref_ = ref; }
|
||||||
std::vector<HomeassistantServiceMap> data{};
|
std::vector<HomeassistantServiceMap> data{};
|
||||||
std::vector<HomeassistantServiceMap> data_template{};
|
std::vector<HomeassistantServiceMap> data_template{};
|
||||||
std::vector<HomeassistantServiceMap> variables{};
|
std::vector<HomeassistantServiceMap> variables{};
|
||||||
@ -1078,8 +1113,10 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "subscribe_home_assistant_state_response"; }
|
const char *message_name() const override { return "subscribe_home_assistant_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string entity_id{};
|
StringRef entity_id_ref_{};
|
||||||
std::string attribute{};
|
void set_entity_id(const StringRef &ref) { this->entity_id_ref_ = ref; }
|
||||||
|
StringRef attribute_ref_{};
|
||||||
|
void set_attribute(const StringRef &ref) { this->attribute_ref_ = ref; }
|
||||||
bool once{false};
|
bool once{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -1139,7 +1176,8 @@ class GetTimeResponse : public ProtoDecodableMessage {
|
|||||||
#ifdef USE_API_SERVICES
|
#ifdef USE_API_SERVICES
|
||||||
class ListEntitiesServicesArgument : public ProtoMessage {
|
class ListEntitiesServicesArgument : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string name{};
|
StringRef name_ref_{};
|
||||||
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
enums::ServiceArgType type{};
|
enums::ServiceArgType type{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -1156,7 +1194,8 @@ class ListEntitiesServicesResponse : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_services_response"; }
|
const char *message_name() const override { return "list_entities_services_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string name{};
|
StringRef name_ref_{};
|
||||||
|
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
|
||||||
uint32_t key{0};
|
uint32_t key{0};
|
||||||
std::vector<ListEntitiesServicesArgument> args{};
|
std::vector<ListEntitiesServicesArgument> args{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@ -1228,7 +1267,12 @@ class CameraImageResponse : public StateResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "camera_image_response"; }
|
const char *message_name() const override { return "camera_image_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string data{};
|
const uint8_t *data_ptr_{nullptr};
|
||||||
|
size_t data_len_{0};
|
||||||
|
void set_data(const uint8_t *data, size_t len) {
|
||||||
|
this->data_ptr_ = data;
|
||||||
|
this->data_len_ = len;
|
||||||
|
}
|
||||||
bool done{false};
|
bool done{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -1303,9 +1347,11 @@ class ClimateStateResponse : public StateResponseProtoMessage {
|
|||||||
enums::ClimateAction action{};
|
enums::ClimateAction action{};
|
||||||
enums::ClimateFanMode fan_mode{};
|
enums::ClimateFanMode fan_mode{};
|
||||||
enums::ClimateSwingMode swing_mode{};
|
enums::ClimateSwingMode swing_mode{};
|
||||||
std::string custom_fan_mode{};
|
StringRef custom_fan_mode_ref_{};
|
||||||
|
void set_custom_fan_mode(const StringRef &ref) { this->custom_fan_mode_ref_ = ref; }
|
||||||
enums::ClimatePreset preset{};
|
enums::ClimatePreset preset{};
|
||||||
std::string custom_preset{};
|
StringRef custom_preset_ref_{};
|
||||||
|
void set_custom_preset(const StringRef &ref) { this->custom_preset_ref_ = ref; }
|
||||||
float current_humidity{0.0f};
|
float current_humidity{0.0f};
|
||||||
float target_humidity{0.0f};
|
float target_humidity{0.0f};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@ -1364,9 +1410,11 @@ class ListEntitiesNumberResponse : public InfoResponseProtoMessage {
|
|||||||
float min_value{0.0f};
|
float min_value{0.0f};
|
||||||
float max_value{0.0f};
|
float max_value{0.0f};
|
||||||
float step{0.0f};
|
float step{0.0f};
|
||||||
std::string unit_of_measurement{};
|
StringRef unit_of_measurement_ref_{};
|
||||||
|
void set_unit_of_measurement(const StringRef &ref) { this->unit_of_measurement_ref_ = ref; }
|
||||||
enums::NumberMode mode{};
|
enums::NumberMode mode{};
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1433,7 +1481,8 @@ class SelectStateResponse : public StateResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "select_state_response"; }
|
const char *message_name() const override { return "select_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string state{};
|
StringRef state_ref_{};
|
||||||
|
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -1532,7 +1581,8 @@ class ListEntitiesLockResponse : public InfoResponseProtoMessage {
|
|||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
bool supports_open{false};
|
bool supports_open{false};
|
||||||
bool requires_code{false};
|
bool requires_code{false};
|
||||||
std::string code_format{};
|
StringRef code_format_ref_{};
|
||||||
|
void set_code_format(const StringRef &ref) { this->code_format_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1585,7 +1635,8 @@ class ListEntitiesButtonResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_button_response"; }
|
const char *message_name() const override { return "list_entities_button_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1613,7 +1664,8 @@ class ButtonCommandRequest : public CommandProtoMessage {
|
|||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
class MediaPlayerSupportedFormat : public ProtoMessage {
|
class MediaPlayerSupportedFormat : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string format{};
|
StringRef format_ref_{};
|
||||||
|
void set_format(const StringRef &ref) { this->format_ref_ = ref; }
|
||||||
uint32_t sample_rate{0};
|
uint32_t sample_rate{0};
|
||||||
uint32_t num_channels{0};
|
uint32_t num_channels{0};
|
||||||
enums::MediaPlayerFormatPurpose purpose{};
|
enums::MediaPlayerFormatPurpose purpose{};
|
||||||
@ -1787,7 +1839,7 @@ class BluetoothGATTGetServicesRequest : public ProtoDecodableMessage {
|
|||||||
};
|
};
|
||||||
class BluetoothGATTDescriptor : public ProtoMessage {
|
class BluetoothGATTDescriptor : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::vector<uint64_t> uuid{};
|
std::array<uint64_t, 2> uuid{};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -1799,7 +1851,7 @@ class BluetoothGATTDescriptor : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class BluetoothGATTCharacteristic : public ProtoMessage {
|
class BluetoothGATTCharacteristic : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::vector<uint64_t> uuid{};
|
std::array<uint64_t, 2> uuid{};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
uint32_t properties{0};
|
uint32_t properties{0};
|
||||||
std::vector<BluetoothGATTDescriptor> descriptors{};
|
std::vector<BluetoothGATTDescriptor> descriptors{};
|
||||||
@ -1813,7 +1865,7 @@ class BluetoothGATTCharacteristic : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class BluetoothGATTService : public ProtoMessage {
|
class BluetoothGATTService : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::vector<uint64_t> uuid{};
|
std::array<uint64_t, 2> uuid{};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
std::vector<BluetoothGATTCharacteristic> characteristics{};
|
std::vector<BluetoothGATTCharacteristic> characteristics{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@ -1827,12 +1879,12 @@ class BluetoothGATTService : public ProtoMessage {
|
|||||||
class BluetoothGATTGetServicesResponse : public ProtoMessage {
|
class BluetoothGATTGetServicesResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 71;
|
static constexpr uint8_t MESSAGE_TYPE = 71;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 38;
|
static constexpr uint8_t ESTIMATED_SIZE = 21;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "bluetooth_gatt_get_services_response"; }
|
const char *message_name() const override { return "bluetooth_gatt_get_services_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
std::vector<BluetoothGATTService> services{};
|
std::array<BluetoothGATTService, 1> services{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1882,7 +1934,12 @@ class BluetoothGATTReadResponse : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
std::string data{};
|
const uint8_t *data_ptr_{nullptr};
|
||||||
|
size_t data_len_{0};
|
||||||
|
void set_data(const uint8_t *data, size_t len) {
|
||||||
|
this->data_ptr_ = data;
|
||||||
|
this->data_len_ = len;
|
||||||
|
}
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1970,7 +2027,12 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
uint32_t handle{0};
|
uint32_t handle{0};
|
||||||
std::string data{};
|
const uint8_t *data_ptr_{nullptr};
|
||||||
|
size_t data_len_{0};
|
||||||
|
void set_data(const uint8_t *data, size_t len) {
|
||||||
|
this->data_ptr_ = data;
|
||||||
|
this->data_len_ = len;
|
||||||
|
}
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -2200,10 +2262,12 @@ class VoiceAssistantRequest : public ProtoMessage {
|
|||||||
const char *message_name() const override { return "voice_assistant_request"; }
|
const char *message_name() const override { return "voice_assistant_request"; }
|
||||||
#endif
|
#endif
|
||||||
bool start{false};
|
bool start{false};
|
||||||
std::string conversation_id{};
|
StringRef conversation_id_ref_{};
|
||||||
|
void set_conversation_id(const StringRef &ref) { this->conversation_id_ref_ = ref; }
|
||||||
uint32_t flags{0};
|
uint32_t flags{0};
|
||||||
VoiceAssistantAudioSettings audio_settings{};
|
VoiceAssistantAudioSettings audio_settings{};
|
||||||
std::string wake_word_phrase{};
|
StringRef wake_word_phrase_ref_{};
|
||||||
|
void set_wake_word_phrase(const StringRef &ref) { this->wake_word_phrase_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -2264,6 +2328,12 @@ class VoiceAssistantAudio : public ProtoDecodableMessage {
|
|||||||
const char *message_name() const override { return "voice_assistant_audio"; }
|
const char *message_name() const override { return "voice_assistant_audio"; }
|
||||||
#endif
|
#endif
|
||||||
std::string data{};
|
std::string data{};
|
||||||
|
const uint8_t *data_ptr_{nullptr};
|
||||||
|
size_t data_len_{0};
|
||||||
|
void set_data(const uint8_t *data, size_t len) {
|
||||||
|
this->data_ptr_ = data;
|
||||||
|
this->data_len_ = len;
|
||||||
|
}
|
||||||
bool end{false};
|
bool end{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -2333,8 +2403,10 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class VoiceAssistantWakeWord : public ProtoMessage {
|
class VoiceAssistantWakeWord : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string id{};
|
StringRef id_ref_{};
|
||||||
std::string wake_word{};
|
void set_id(const StringRef &ref) { this->id_ref_ = ref; }
|
||||||
|
StringRef wake_word_ref_{};
|
||||||
|
void set_wake_word(const StringRef &ref) { this->wake_word_ref_ = ref; }
|
||||||
std::vector<std::string> trained_languages{};
|
std::vector<std::string> trained_languages{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -2455,7 +2527,8 @@ class ListEntitiesTextResponse : public InfoResponseProtoMessage {
|
|||||||
#endif
|
#endif
|
||||||
uint32_t min_length{0};
|
uint32_t min_length{0};
|
||||||
uint32_t max_length{0};
|
uint32_t max_length{0};
|
||||||
std::string pattern{};
|
StringRef pattern_ref_{};
|
||||||
|
void set_pattern(const StringRef &ref) { this->pattern_ref_ = ref; }
|
||||||
enums::TextMode mode{};
|
enums::TextMode mode{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -2472,7 +2545,8 @@ class TextStateResponse : public StateResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "text_state_response"; }
|
const char *message_name() const override { return "text_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string state{};
|
StringRef state_ref_{};
|
||||||
|
void set_state(const StringRef &ref) { this->state_ref_ = ref; }
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -2616,7 +2690,8 @@ class ListEntitiesEventResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_event_response"; }
|
const char *message_name() const override { return "list_entities_event_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
std::vector<std::string> event_types{};
|
std::vector<std::string> event_types{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@ -2633,7 +2708,8 @@ class EventResponse : public StateResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "event_response"; }
|
const char *message_name() const override { return "event_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string event_type{};
|
StringRef event_type_ref_{};
|
||||||
|
void set_event_type(const StringRef &ref) { this->event_type_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -2651,7 +2727,8 @@ class ListEntitiesValveResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_valve_response"; }
|
const char *message_name() const override { return "list_entities_valve_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
bool supports_position{false};
|
bool supports_position{false};
|
||||||
bool supports_stop{false};
|
bool supports_stop{false};
|
||||||
@ -2757,7 +2834,8 @@ class ListEntitiesUpdateResponse : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_update_response"; }
|
const char *message_name() const override { return "list_entities_update_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string device_class{};
|
StringRef device_class_ref_{};
|
||||||
|
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -2777,11 +2855,16 @@ class UpdateStateResponse : public StateResponseProtoMessage {
|
|||||||
bool in_progress{false};
|
bool in_progress{false};
|
||||||
bool has_progress{false};
|
bool has_progress{false};
|
||||||
float progress{0.0f};
|
float progress{0.0f};
|
||||||
std::string current_version{};
|
StringRef current_version_ref_{};
|
||||||
std::string latest_version{};
|
void set_current_version(const StringRef &ref) { this->current_version_ref_ = ref; }
|
||||||
std::string title{};
|
StringRef latest_version_ref_{};
|
||||||
std::string release_summary{};
|
void set_latest_version(const StringRef &ref) { this->latest_version_ref_ = ref; }
|
||||||
std::string release_url{};
|
StringRef title_ref_{};
|
||||||
|
void set_title(const StringRef &ref) { this->title_ref_ = ref; }
|
||||||
|
StringRef release_summary_ref_{};
|
||||||
|
void set_release_summary(const StringRef &ref) { this->release_summary_ref_ = ref; }
|
||||||
|
StringRef release_url_ref_{};
|
||||||
|
void set_release_url(const StringRef &ref) { this->release_url_ref_ = ref; }
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -597,33 +597,28 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
|
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
|
||||||
HelloResponse ret = this->hello(msg);
|
if (!this->send_hello_response(msg)) {
|
||||||
if (!this->send_message(ret, HelloResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_connect_request(const ConnectRequest &msg) {
|
void APIServerConnection::on_connect_request(const ConnectRequest &msg) {
|
||||||
ConnectResponse ret = this->connect(msg);
|
if (!this->send_connect_response(msg)) {
|
||||||
if (!this->send_message(ret, ConnectResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
|
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
|
||||||
DisconnectResponse ret = this->disconnect(msg);
|
if (!this->send_disconnect_response(msg)) {
|
||||||
if (!this->send_message(ret, DisconnectResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_ping_request(const PingRequest &msg) {
|
void APIServerConnection::on_ping_request(const PingRequest &msg) {
|
||||||
PingResponse ret = this->ping(msg);
|
if (!this->send_ping_response(msg)) {
|
||||||
if (!this->send_message(ret, PingResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
|
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
|
||||||
if (this->check_connection_setup_()) {
|
if (this->check_connection_setup_()) {
|
||||||
DeviceInfoResponse ret = this->device_info(msg);
|
if (!this->send_device_info_response(msg)) {
|
||||||
if (!this->send_message(ret, DeviceInfoResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -656,8 +651,7 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc
|
|||||||
}
|
}
|
||||||
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
|
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
|
||||||
if (this->check_connection_setup_()) {
|
if (this->check_connection_setup_()) {
|
||||||
GetTimeResponse ret = this->get_time(msg);
|
if (!this->send_get_time_response(msg)) {
|
||||||
if (!this->send_message(ret, GetTimeResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,8 +666,7 @@ void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest
|
|||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
|
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
|
||||||
if (this->check_authenticated_()) {
|
if (this->check_authenticated_()) {
|
||||||
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
|
if (!this->send_noise_encryption_set_key_response(msg)) {
|
||||||
if (!this->send_message(ret, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -866,8 +859,7 @@ void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNo
|
|||||||
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
|
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
|
||||||
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
||||||
if (this->check_authenticated_()) {
|
if (this->check_authenticated_()) {
|
||||||
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
|
if (!this->send_subscribe_bluetooth_connections_free_response(msg)) {
|
||||||
if (!this->send_message(ret, BluetoothConnectionsFreeResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -898,8 +890,7 @@ void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVo
|
|||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
|
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
|
||||||
if (this->check_authenticated_()) {
|
if (this->check_authenticated_()) {
|
||||||
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
|
if (!this->send_voice_assistant_get_configuration_response(msg)) {
|
||||||
if (!this->send_message(ret, VoiceAssistantConfigurationResponse::MESSAGE_TYPE)) {
|
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,22 +207,22 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
|
|
||||||
class APIServerConnection : public APIServerConnectionBase {
|
class APIServerConnection : public APIServerConnectionBase {
|
||||||
public:
|
public:
|
||||||
virtual HelloResponse hello(const HelloRequest &msg) = 0;
|
virtual bool send_hello_response(const HelloRequest &msg) = 0;
|
||||||
virtual ConnectResponse connect(const ConnectRequest &msg) = 0;
|
virtual bool send_connect_response(const ConnectRequest &msg) = 0;
|
||||||
virtual DisconnectResponse disconnect(const DisconnectRequest &msg) = 0;
|
virtual bool send_disconnect_response(const DisconnectRequest &msg) = 0;
|
||||||
virtual PingResponse ping(const PingRequest &msg) = 0;
|
virtual bool send_ping_response(const PingRequest &msg) = 0;
|
||||||
virtual DeviceInfoResponse device_info(const DeviceInfoRequest &msg) = 0;
|
virtual bool send_device_info_response(const DeviceInfoRequest &msg) = 0;
|
||||||
virtual void list_entities(const ListEntitiesRequest &msg) = 0;
|
virtual void list_entities(const ListEntitiesRequest &msg) = 0;
|
||||||
virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0;
|
virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0;
|
||||||
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
|
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
|
||||||
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
|
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
|
||||||
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
||||||
virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0;
|
virtual bool send_get_time_response(const GetTimeRequest &msg) = 0;
|
||||||
#ifdef USE_API_SERVICES
|
#ifdef USE_API_SERVICES
|
||||||
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
virtual NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) = 0;
|
virtual bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
||||||
@ -303,7 +303,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
|
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
|
virtual bool send_subscribe_bluetooth_connections_free_response(
|
||||||
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
|
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
@ -316,8 +316,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual VoiceAssistantConfigurationResponse voice_assistant_get_configuration(
|
virtual bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) = 0;
|
||||||
const VoiceAssistantConfigurationRequest &msg) = 0;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0;
|
virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0;
|
||||||
|
@ -148,7 +148,7 @@ class CustomAPIDevice {
|
|||||||
*/
|
*/
|
||||||
void call_homeassistant_service(const std::string &service_name) {
|
void call_homeassistant_service(const std::string &service_name) {
|
||||||
HomeassistantServiceResponse resp;
|
HomeassistantServiceResponse resp;
|
||||||
resp.service = service_name;
|
resp.set_service(StringRef(service_name));
|
||||||
global_api_server->send_homeassistant_service_call(resp);
|
global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,12 +168,12 @@ class CustomAPIDevice {
|
|||||||
*/
|
*/
|
||||||
void call_homeassistant_service(const std::string &service_name, const std::map<std::string, std::string> &data) {
|
void call_homeassistant_service(const std::string &service_name, const std::map<std::string, std::string> &data) {
|
||||||
HomeassistantServiceResponse resp;
|
HomeassistantServiceResponse resp;
|
||||||
resp.service = service_name;
|
resp.set_service(StringRef(service_name));
|
||||||
for (auto &it : data) {
|
for (auto &it : data) {
|
||||||
HomeassistantServiceMap kv;
|
resp.data.emplace_back();
|
||||||
kv.key = it.first;
|
auto &kv = resp.data.back();
|
||||||
kv.value = it.second;
|
kv.set_key(StringRef(it.first));
|
||||||
resp.data.push_back(kv);
|
kv.set_value(StringRef(it.second));
|
||||||
}
|
}
|
||||||
global_api_server->send_homeassistant_service_call(resp);
|
global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ class CustomAPIDevice {
|
|||||||
*/
|
*/
|
||||||
void fire_homeassistant_event(const std::string &event_name) {
|
void fire_homeassistant_event(const std::string &event_name) {
|
||||||
HomeassistantServiceResponse resp;
|
HomeassistantServiceResponse resp;
|
||||||
resp.service = event_name;
|
resp.set_service(StringRef(event_name));
|
||||||
resp.is_event = true;
|
resp.is_event = true;
|
||||||
global_api_server->send_homeassistant_service_call(resp);
|
global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
@ -210,13 +210,13 @@ class CustomAPIDevice {
|
|||||||
*/
|
*/
|
||||||
void fire_homeassistant_event(const std::string &service_name, const std::map<std::string, std::string> &data) {
|
void fire_homeassistant_event(const std::string &service_name, const std::map<std::string, std::string> &data) {
|
||||||
HomeassistantServiceResponse resp;
|
HomeassistantServiceResponse resp;
|
||||||
resp.service = service_name;
|
resp.set_service(StringRef(service_name));
|
||||||
resp.is_event = true;
|
resp.is_event = true;
|
||||||
for (auto &it : data) {
|
for (auto &it : data) {
|
||||||
HomeassistantServiceMap kv;
|
resp.data.emplace_back();
|
||||||
kv.key = it.first;
|
auto &kv = resp.data.back();
|
||||||
kv.value = it.second;
|
kv.set_key(StringRef(it.first));
|
||||||
resp.data.push_back(kv);
|
kv.set_value(StringRef(it.second));
|
||||||
}
|
}
|
||||||
global_api_server->send_homeassistant_service_call(resp);
|
global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
@ -59,25 +59,29 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
|||||||
|
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
HomeassistantServiceResponse resp;
|
HomeassistantServiceResponse resp;
|
||||||
resp.service = this->service_.value(x...);
|
std::string service_value = this->service_.value(x...);
|
||||||
|
resp.set_service(StringRef(service_value));
|
||||||
resp.is_event = this->is_event_;
|
resp.is_event = this->is_event_;
|
||||||
for (auto &it : this->data_) {
|
for (auto &it : this->data_) {
|
||||||
HomeassistantServiceMap kv;
|
resp.data.emplace_back();
|
||||||
kv.key = it.key;
|
auto &kv = resp.data.back();
|
||||||
kv.value = it.value.value(x...);
|
kv.set_key(StringRef(it.key));
|
||||||
resp.data.push_back(kv);
|
std::string value = it.value.value(x...);
|
||||||
|
kv.set_value(StringRef(value));
|
||||||
}
|
}
|
||||||
for (auto &it : this->data_template_) {
|
for (auto &it : this->data_template_) {
|
||||||
HomeassistantServiceMap kv;
|
resp.data_template.emplace_back();
|
||||||
kv.key = it.key;
|
auto &kv = resp.data_template.back();
|
||||||
kv.value = it.value.value(x...);
|
kv.set_key(StringRef(it.key));
|
||||||
resp.data_template.push_back(kv);
|
std::string value = it.value.value(x...);
|
||||||
|
kv.set_value(StringRef(value));
|
||||||
}
|
}
|
||||||
for (auto &it : this->variables_) {
|
for (auto &it : this->variables_) {
|
||||||
HomeassistantServiceMap kv;
|
resp.variables.emplace_back();
|
||||||
kv.key = it.key;
|
auto &kv = resp.variables.back();
|
||||||
kv.value = it.value.value(x...);
|
kv.set_key(StringRef(it.key));
|
||||||
resp.variables.push_back(kv);
|
std::string value = it.value.value(x...);
|
||||||
|
kv.set_value(StringRef(value));
|
||||||
}
|
}
|
||||||
this->parent_->send_homeassistant_service_call(resp);
|
this->parent_->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||||
@ -14,6 +16,37 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* StringRef Ownership Model for API Protocol Messages
|
||||||
|
* ===================================================
|
||||||
|
*
|
||||||
|
* StringRef is used for zero-copy string handling in outgoing (SOURCE_SERVER) messages.
|
||||||
|
* It holds a pointer and length to existing string data without copying.
|
||||||
|
*
|
||||||
|
* CRITICAL: The referenced string data MUST remain valid until message encoding completes.
|
||||||
|
*
|
||||||
|
* Safe StringRef Patterns:
|
||||||
|
* 1. String literals: StringRef("literal") - Always safe (static storage duration)
|
||||||
|
* 2. Member variables: StringRef(this->member_string_) - Safe if object outlives encoding
|
||||||
|
* 3. Global/static strings: StringRef(GLOBAL_CONSTANT) - Always safe
|
||||||
|
* 4. Local variables: Safe ONLY if encoding happens before function returns:
|
||||||
|
* std::string temp = compute_value();
|
||||||
|
* msg.set_field(StringRef(temp));
|
||||||
|
* return this->send_message(msg); // temp is valid during encoding
|
||||||
|
*
|
||||||
|
* Unsafe Patterns (WILL cause crashes/corruption):
|
||||||
|
* 1. Temporaries: msg.set_field(StringRef(obj.get_string())) // get_string() returns by value
|
||||||
|
* 2. Optional values: msg.set_field(StringRef(optional.value())) // value() returns a copy
|
||||||
|
* 3. Concatenation: msg.set_field(StringRef(str1 + str2)) // Result is temporary
|
||||||
|
*
|
||||||
|
* For unsafe patterns, store in a local variable first:
|
||||||
|
* std::string temp = optional.value(); // or get_string() or str1 + str2
|
||||||
|
* msg.set_field(StringRef(temp));
|
||||||
|
*
|
||||||
|
* The send_*_response pattern ensures proper lifetime management by encoding
|
||||||
|
* within the same function scope where temporaries are created.
|
||||||
|
*/
|
||||||
|
|
||||||
/// Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit
|
/// Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit
|
||||||
class ProtoVarInt {
|
class ProtoVarInt {
|
||||||
public:
|
public:
|
||||||
@ -206,12 +239,20 @@ class ProtoWriteBuffer {
|
|||||||
|
|
||||||
this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
|
this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
|
||||||
this->encode_varint_raw(len);
|
this->encode_varint_raw(len);
|
||||||
auto *data = reinterpret_cast<const uint8_t *>(string);
|
|
||||||
this->buffer_->insert(this->buffer_->end(), data, data + len);
|
// Using resize + memcpy instead of insert provides significant performance improvement:
|
||||||
|
// ~10-11x faster for 16-32 byte strings, ~3x faster for 64-byte strings
|
||||||
|
// as it avoids iterator checks and potential element moves that insert performs
|
||||||
|
size_t old_size = this->buffer_->size();
|
||||||
|
this->buffer_->resize(old_size + len);
|
||||||
|
std::memcpy(this->buffer_->data() + old_size, string, len);
|
||||||
}
|
}
|
||||||
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
|
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
|
||||||
this->encode_string(field_id, value.data(), value.size(), force);
|
this->encode_string(field_id, value.data(), value.size(), force);
|
||||||
}
|
}
|
||||||
|
void encode_string(uint32_t field_id, const StringRef &ref, bool force = false) {
|
||||||
|
this->encode_string(field_id, ref.c_str(), ref.size(), force);
|
||||||
|
}
|
||||||
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
|
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
|
||||||
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
|
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
|
||||||
}
|
}
|
||||||
@ -527,25 +568,6 @@ class ProtoSize {
|
|||||||
total_size += field_id_size + 1;
|
total_size += field_id_size + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Calculates and adds the size of a fixed field to the total message size
|
|
||||||
*
|
|
||||||
* Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double).
|
|
||||||
*
|
|
||||||
* @tparam NumBytes The number of bytes for this fixed field (4 or 8)
|
|
||||||
* @param is_nonzero Whether the value is non-zero
|
|
||||||
*/
|
|
||||||
template<uint32_t NumBytes>
|
|
||||||
static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero) {
|
|
||||||
// Skip calculation if value is zero
|
|
||||||
if (!is_nonzero) {
|
|
||||||
return; // No need to update total_size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixed fields always take exactly NumBytes
|
|
||||||
total_size += field_id_size + NumBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a float field to the total message size
|
* @brief Calculates and adds the size of a float field to the total message size
|
||||||
*/
|
*/
|
||||||
@ -682,17 +704,16 @@ class ProtoSize {
|
|||||||
// sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
|
// sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a string/bytes field to the total message size
|
* @brief Calculates and adds the size of a string field using length
|
||||||
*/
|
*/
|
||||||
static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str) {
|
static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, size_t len) {
|
||||||
// Skip calculation if string is empty
|
// Skip calculation if string is empty
|
||||||
if (str.empty()) {
|
if (len == 0) {
|
||||||
return; // No need to update total_size
|
return; // No need to update total_size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate and directly add to total_size
|
// Field ID + length varint + string bytes
|
||||||
const uint32_t str_size = static_cast<uint32_t>(str.size());
|
total_size += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
|
||||||
total_size += field_id_size + varint(str_size) + str_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -704,6 +725,19 @@ class ProtoSize {
|
|||||||
total_size += field_id_size + varint(str_size) + str_size;
|
total_size += field_id_size + varint(str_size) + str_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a bytes field to the total message size
|
||||||
|
*/
|
||||||
|
static inline void add_bytes_field(uint32_t &total_size, uint32_t field_id_size, size_t len) {
|
||||||
|
// Skip calculation if bytes is empty
|
||||||
|
if (len == 0) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field ID + length varint + data bytes
|
||||||
|
total_size += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size
|
* @brief Calculates and adds the size of a nested message field to the total message size
|
||||||
*
|
*
|
||||||
|
@ -33,14 +33,14 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
|||||||
|
|
||||||
ListEntitiesServicesResponse encode_list_service_response() override {
|
ListEntitiesServicesResponse encode_list_service_response() override {
|
||||||
ListEntitiesServicesResponse msg;
|
ListEntitiesServicesResponse msg;
|
||||||
msg.name = this->name_;
|
msg.set_name(StringRef(this->name_));
|
||||||
msg.key = this->key_;
|
msg.key = this->key_;
|
||||||
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
||||||
for (int i = 0; i < sizeof...(Ts); i++) {
|
for (int i = 0; i < sizeof...(Ts); i++) {
|
||||||
ListEntitiesServicesArgument arg;
|
msg.args.emplace_back();
|
||||||
|
auto &arg = msg.args.back();
|
||||||
arg.type = arg_types[i];
|
arg.type = arg_types[i];
|
||||||
arg.name = this->arg_names_[i];
|
arg.set_name(StringRef(this->arg_names_[i]));
|
||||||
msg.args.push_back(arg);
|
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
@ -13,16 +13,16 @@ namespace bluetooth_proxy {
|
|||||||
|
|
||||||
static const char *const TAG = "bluetooth_proxy.connection";
|
static const char *const TAG = "bluetooth_proxy.connection";
|
||||||
|
|
||||||
static std::vector<uint64_t> get_128bit_uuid_vec(esp_bt_uuid_t uuid_source) {
|
static void fill_128bit_uuid_array(std::array<uint64_t, 2> &out, esp_bt_uuid_t uuid_source) {
|
||||||
esp_bt_uuid_t uuid = espbt::ESPBTUUID::from_uuid(uuid_source).as_128bit().get_uuid();
|
esp_bt_uuid_t uuid = espbt::ESPBTUUID::from_uuid(uuid_source).as_128bit().get_uuid();
|
||||||
return std::vector<uint64_t>{((uint64_t) uuid.uuid.uuid128[15] << 56) | ((uint64_t) uuid.uuid.uuid128[14] << 48) |
|
out[0] = ((uint64_t) uuid.uuid.uuid128[15] << 56) | ((uint64_t) uuid.uuid.uuid128[14] << 48) |
|
||||||
((uint64_t) uuid.uuid.uuid128[13] << 40) | ((uint64_t) uuid.uuid.uuid128[12] << 32) |
|
((uint64_t) uuid.uuid.uuid128[13] << 40) | ((uint64_t) uuid.uuid.uuid128[12] << 32) |
|
||||||
((uint64_t) uuid.uuid.uuid128[11] << 24) | ((uint64_t) uuid.uuid.uuid128[10] << 16) |
|
((uint64_t) uuid.uuid.uuid128[11] << 24) | ((uint64_t) uuid.uuid.uuid128[10] << 16) |
|
||||||
((uint64_t) uuid.uuid.uuid128[9] << 8) | ((uint64_t) uuid.uuid.uuid128[8]),
|
((uint64_t) uuid.uuid.uuid128[9] << 8) | ((uint64_t) uuid.uuid.uuid128[8]);
|
||||||
((uint64_t) uuid.uuid.uuid128[7] << 56) | ((uint64_t) uuid.uuid.uuid128[6] << 48) |
|
out[1] = ((uint64_t) uuid.uuid.uuid128[7] << 56) | ((uint64_t) uuid.uuid.uuid128[6] << 48) |
|
||||||
((uint64_t) uuid.uuid.uuid128[5] << 40) | ((uint64_t) uuid.uuid.uuid128[4] << 32) |
|
((uint64_t) uuid.uuid.uuid128[5] << 40) | ((uint64_t) uuid.uuid.uuid128[4] << 32) |
|
||||||
((uint64_t) uuid.uuid.uuid128[3] << 24) | ((uint64_t) uuid.uuid.uuid128[2] << 16) |
|
((uint64_t) uuid.uuid.uuid128[3] << 24) | ((uint64_t) uuid.uuid.uuid128[2] << 16) |
|
||||||
((uint64_t) uuid.uuid.uuid128[1] << 8) | ((uint64_t) uuid.uuid.uuid128[0])};
|
((uint64_t) uuid.uuid.uuid128[1] << 8) | ((uint64_t) uuid.uuid.uuid128[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothConnection::dump_config() {
|
void BluetoothConnection::dump_config() {
|
||||||
@ -95,9 +95,8 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
|
|
||||||
api::BluetoothGATTGetServicesResponse resp;
|
api::BluetoothGATTGetServicesResponse resp;
|
||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.services.emplace_back();
|
auto &service_resp = resp.services[0];
|
||||||
auto &service_resp = resp.services.back();
|
fill_128bit_uuid_array(service_resp.uuid, service_result.uuid);
|
||||||
service_resp.uuid = get_128bit_uuid_vec(service_result.uuid);
|
|
||||||
service_resp.handle = service_result.start_handle;
|
service_resp.handle = service_result.start_handle;
|
||||||
|
|
||||||
// Get the number of characteristics directly with one call
|
// Get the number of characteristics directly with one call
|
||||||
@ -136,7 +135,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
|
|
||||||
service_resp.characteristics.emplace_back();
|
service_resp.characteristics.emplace_back();
|
||||||
auto &characteristic_resp = service_resp.characteristics.back();
|
auto &characteristic_resp = service_resp.characteristics.back();
|
||||||
characteristic_resp.uuid = get_128bit_uuid_vec(char_result.uuid);
|
fill_128bit_uuid_array(characteristic_resp.uuid, char_result.uuid);
|
||||||
characteristic_resp.handle = char_result.char_handle;
|
characteristic_resp.handle = char_result.char_handle;
|
||||||
characteristic_resp.properties = char_result.properties;
|
characteristic_resp.properties = char_result.properties;
|
||||||
char_offset++;
|
char_offset++;
|
||||||
@ -176,7 +175,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
|
|
||||||
characteristic_resp.descriptors.emplace_back();
|
characteristic_resp.descriptors.emplace_back();
|
||||||
auto &descriptor_resp = characteristic_resp.descriptors.back();
|
auto &descriptor_resp = characteristic_resp.descriptors.back();
|
||||||
descriptor_resp.uuid = get_128bit_uuid_vec(desc_result.uuid);
|
fill_128bit_uuid_array(descriptor_resp.uuid, desc_result.uuid);
|
||||||
descriptor_resp.handle = desc_result.handle;
|
descriptor_resp.handle = desc_result.handle;
|
||||||
desc_offset++;
|
desc_offset++;
|
||||||
}
|
}
|
||||||
@ -234,9 +233,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|||||||
api::BluetoothGATTReadResponse resp;
|
api::BluetoothGATTReadResponse resp;
|
||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.handle = param->read.handle;
|
resp.handle = param->read.handle;
|
||||||
resp.data.reserve(param->read.value_len);
|
resp.set_data(param->read.value, param->read.value_len);
|
||||||
// Use bulk insert instead of individual push_backs
|
|
||||||
resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len);
|
|
||||||
this->proxy_->get_api_connection()->send_message(resp, api::BluetoothGATTReadResponse::MESSAGE_TYPE);
|
this->proxy_->get_api_connection()->send_message(resp, api::BluetoothGATTReadResponse::MESSAGE_TYPE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -287,9 +284,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|||||||
api::BluetoothGATTNotifyDataResponse resp;
|
api::BluetoothGATTNotifyDataResponse resp;
|
||||||
resp.address = this->address_;
|
resp.address = this->address_;
|
||||||
resp.handle = param->notify.handle;
|
resp.handle = param->notify.handle;
|
||||||
resp.data.reserve(param->notify.value_len);
|
resp.set_data(param->notify.value, param->notify.value_len);
|
||||||
// Use bulk insert instead of individual push_backs
|
|
||||||
resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len);
|
|
||||||
this->proxy_->get_api_connection()->send_message(resp, api::BluetoothGATTNotifyDataResponse::MESSAGE_TYPE);
|
this->proxy_->get_api_connection()->send_message(resp, api::BluetoothGATTNotifyDataResponse::MESSAGE_TYPE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import esp32, i2c
|
from esphome.components import esp32, i2c
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_SAMPLE_RATE, CONF_TEMPERATURE_OFFSET
|
from esphome.const import CONF_ID, CONF_SAMPLE_RATE, CONF_TEMPERATURE_OFFSET, Framework
|
||||||
|
|
||||||
CODEOWNERS = ["@trvrnrth"]
|
CODEOWNERS = ["@trvrnrth"]
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
@ -56,7 +56,15 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
): cv.positive_time_period_minutes,
|
): cv.positive_time_period_minutes,
|
||||||
}
|
}
|
||||||
).extend(i2c.i2c_device_schema(0x76)),
|
).extend(i2c.i2c_device_schema(0x76)),
|
||||||
cv.only_with_arduino,
|
cv.only_with_framework(
|
||||||
|
frameworks=Framework.ARDUINO,
|
||||||
|
suggestions={
|
||||||
|
Framework.ESP_IDF: (
|
||||||
|
"bme68x_bsec2_i2c",
|
||||||
|
"sensor/bme68x_bsec2",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
cv.Any(
|
cv.Any(
|
||||||
cv.only_on_esp8266,
|
cv.only_on_esp8266,
|
||||||
cv.All(
|
cv.All(
|
||||||
|
@ -30,7 +30,7 @@ from esphome.const import (
|
|||||||
CONF_SERVICE_UUID,
|
CONF_SERVICE_UUID,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
from esphome.enum import StrEnum
|
from esphome.enum import StrEnum
|
||||||
from esphome.types import ConfigType
|
from esphome.types import ConfigType
|
||||||
|
|
||||||
@ -365,14 +365,22 @@ async def to_code(config):
|
|||||||
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
|
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
|
||||||
cg.add_define("USE_ESP32_BLE_CLIENT")
|
cg.add_define("USE_ESP32_BLE_CLIENT")
|
||||||
|
|
||||||
# Add feature-specific defines based on what's needed
|
CORE.add_job(_add_ble_features)
|
||||||
if BLEFeatures.ESP_BT_DEVICE in _required_features:
|
|
||||||
cg.add_define("USE_ESP32_BLE_DEVICE")
|
|
||||||
|
|
||||||
if config.get(CONF_SOFTWARE_COEXISTENCE):
|
if config.get(CONF_SOFTWARE_COEXISTENCE):
|
||||||
cg.add_define("USE_ESP32_BLE_SOFTWARE_COEXISTENCE")
|
cg.add_define("USE_ESP32_BLE_SOFTWARE_COEXISTENCE")
|
||||||
|
|
||||||
|
|
||||||
|
# This needs to be run as a job with very low priority so that all components have
|
||||||
|
# chance to call register_ble_tracker and register_client before the list is checked
|
||||||
|
# and added to the global defines list.
|
||||||
|
@coroutine_with_priority(-1000)
|
||||||
|
async def _add_ble_features():
|
||||||
|
# Add feature-specific defines based on what's needed
|
||||||
|
if BLEFeatures.ESP_BT_DEVICE in _required_features:
|
||||||
|
cg.add_define("USE_ESP32_BLE_DEVICE")
|
||||||
|
|
||||||
|
|
||||||
ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema(
|
ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(ESP32BLETracker),
|
cv.GenerateID(): cv.use_id(ESP32BLETracker),
|
||||||
|
@ -16,6 +16,8 @@ namespace esp32_touch {
|
|||||||
|
|
||||||
static const char *const TAG = "esp32_touch";
|
static const char *const TAG = "esp32_touch";
|
||||||
|
|
||||||
|
static const uint32_t SETUP_MODE_THRESHOLD = 0xFFFF;
|
||||||
|
|
||||||
void ESP32TouchComponent::setup() {
|
void ESP32TouchComponent::setup() {
|
||||||
// Create queue for touch events
|
// Create queue for touch events
|
||||||
// Queue size calculation: children * 4 allows for burst scenarios where ISR
|
// Queue size calculation: children * 4 allows for burst scenarios where ISR
|
||||||
@ -44,8 +46,12 @@ void ESP32TouchComponent::setup() {
|
|||||||
|
|
||||||
// Configure each touch pad
|
// Configure each touch pad
|
||||||
for (auto *child : this->children_) {
|
for (auto *child : this->children_) {
|
||||||
|
if (this->setup_mode_) {
|
||||||
|
touch_pad_config(child->get_touch_pad(), SETUP_MODE_THRESHOLD);
|
||||||
|
} else {
|
||||||
touch_pad_config(child->get_touch_pad(), child->get_threshold());
|
touch_pad_config(child->get_touch_pad(), child->get_threshold());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Register ISR handler
|
// Register ISR handler
|
||||||
esp_err_t err = touch_pad_isr_register(touch_isr_handler, this);
|
esp_err_t err = touch_pad_isr_register(touch_isr_handler, this);
|
||||||
@ -114,8 +120,8 @@ void ESP32TouchComponent::loop() {
|
|||||||
child->publish_state(new_state);
|
child->publish_state(new_state);
|
||||||
// Original ESP32: ISR only fires when touched, release is detected by timeout
|
// Original ESP32: ISR only fires when touched, release is detected by timeout
|
||||||
// Note: ESP32 v1 uses inverted logic - touched when value < threshold
|
// Note: ESP32 v1 uses inverted logic - touched when value < threshold
|
||||||
ESP_LOGV(TAG, "Touch Pad '%s' state: ON (value: %" PRIu32 " < threshold: %" PRIu32 ")",
|
ESP_LOGV(TAG, "Touch Pad '%s' state: %s (value: %" PRIu32 " < threshold: %" PRIu32 ")",
|
||||||
child->get_name().c_str(), event.value, child->get_threshold());
|
child->get_name().c_str(), ONOFF(new_state), event.value, child->get_threshold());
|
||||||
}
|
}
|
||||||
break; // Exit inner loop after processing matching pad
|
break; // Exit inner loop after processing matching pad
|
||||||
}
|
}
|
||||||
@ -188,11 +194,6 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
|
|||||||
// as any pad remains touched. This allows us to detect both new touches and
|
// as any pad remains touched. This allows us to detect both new touches and
|
||||||
// continued touches, but releases must be detected by timeout in the main loop.
|
// continued touches, but releases must be detected by timeout in the main loop.
|
||||||
|
|
||||||
// IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2!
|
|
||||||
// ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE
|
|
||||||
// Therefore: touched = (value < threshold)
|
|
||||||
// This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold)
|
|
||||||
|
|
||||||
// Process all configured pads to check their current state
|
// Process all configured pads to check their current state
|
||||||
// Note: ESP32 v1 doesn't tell us which specific pad triggered the interrupt,
|
// Note: ESP32 v1 doesn't tell us which specific pad triggered the interrupt,
|
||||||
// so we must scan all configured pads to find which ones were touched
|
// so we must scan all configured pads to find which ones were touched
|
||||||
@ -211,11 +212,16 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip pads that aren’t in the trigger mask
|
// Skip pads that aren’t in the trigger mask
|
||||||
bool is_touched = (mask >> pad) & 1;
|
if (((mask >> pad) & 1) == 0) {
|
||||||
if (!is_touched) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2!
|
||||||
|
// ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE
|
||||||
|
// Therefore: touched = (value < threshold)
|
||||||
|
// This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold)
|
||||||
|
bool is_touched = value < child->get_threshold();
|
||||||
|
|
||||||
// Always send the current state - the main loop will filter for changes
|
// Always send the current state - the main loop will filter for changes
|
||||||
// We send both touched and untouched states because the ISR doesn't
|
// We send both touched and untouched states because the ISR doesn't
|
||||||
// track previous state (to keep ISR fast and simple)
|
// track previous state (to keep ISR fast and simple)
|
||||||
|
@ -2,7 +2,13 @@ from esphome import pins
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fastled_base
|
from esphome.components import fastled_base
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER
|
from esphome.const import (
|
||||||
|
CONF_CHIPSET,
|
||||||
|
CONF_NUM_LEDS,
|
||||||
|
CONF_PIN,
|
||||||
|
CONF_RGB_ORDER,
|
||||||
|
Framework,
|
||||||
|
)
|
||||||
|
|
||||||
AUTO_LOAD = ["fastled_base"]
|
AUTO_LOAD = ["fastled_base"]
|
||||||
|
|
||||||
@ -48,13 +54,22 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
_validate,
|
cv.only_with_framework(
|
||||||
|
frameworks=Framework.ARDUINO,
|
||||||
|
suggestions={
|
||||||
|
Framework.ESP_IDF: (
|
||||||
|
"esp32_rmt_led_strip",
|
||||||
|
"light/esp32_rmt_led_strip",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
cv.require_framework_version(
|
cv.require_framework_version(
|
||||||
esp8266_arduino=cv.Version(2, 7, 4),
|
esp8266_arduino=cv.Version(2, 7, 4),
|
||||||
esp32_arduino=cv.Version(99, 0, 0),
|
esp32_arduino=cv.Version(99, 0, 0),
|
||||||
max_version=True,
|
max_version=True,
|
||||||
extra_message="Please see note on documentation for FastLED",
|
extra_message="Please see note on documentation for FastLED",
|
||||||
),
|
),
|
||||||
|
_validate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ from esphome.const import (
|
|||||||
CONF_DATA_RATE,
|
CONF_DATA_RATE,
|
||||||
CONF_NUM_LEDS,
|
CONF_NUM_LEDS,
|
||||||
CONF_RGB_ORDER,
|
CONF_RGB_ORDER,
|
||||||
|
Framework,
|
||||||
)
|
)
|
||||||
|
|
||||||
AUTO_LOAD = ["fastled_base"]
|
AUTO_LOAD = ["fastled_base"]
|
||||||
@ -33,6 +34,15 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Optional(CONF_DATA_RATE): cv.frequency,
|
cv.Optional(CONF_DATA_RATE): cv.frequency,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
cv.only_with_framework(
|
||||||
|
frameworks=Framework.ARDUINO,
|
||||||
|
suggestions={
|
||||||
|
Framework.ESP_IDF: (
|
||||||
|
"spi_led_strip",
|
||||||
|
"light/spi_led_strip",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
cv.require_framework_version(
|
cv.require_framework_version(
|
||||||
esp8266_arduino=cv.Version(2, 7, 4),
|
esp8266_arduino=cv.Version(2, 7, 4),
|
||||||
esp32_arduino=cv.Version(99, 0, 0),
|
esp32_arduino=cv.Version(99, 0, 0),
|
||||||
|
@ -83,18 +83,24 @@ void HomeassistantNumber::control(float value) {
|
|||||||
|
|
||||||
this->publish_state(value);
|
this->publish_state(value);
|
||||||
|
|
||||||
|
static constexpr auto SERVICE_NAME = StringRef::from_lit("number.set_value");
|
||||||
|
static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id");
|
||||||
|
static constexpr auto VALUE_KEY = StringRef::from_lit("value");
|
||||||
|
|
||||||
api::HomeassistantServiceResponse resp;
|
api::HomeassistantServiceResponse resp;
|
||||||
resp.service = "number.set_value";
|
resp.set_service(SERVICE_NAME);
|
||||||
|
|
||||||
api::HomeassistantServiceMap entity_id;
|
resp.data.emplace_back();
|
||||||
entity_id.key = "entity_id";
|
auto &entity_id = resp.data.back();
|
||||||
entity_id.value = this->entity_id_;
|
entity_id.set_key(ENTITY_ID_KEY);
|
||||||
resp.data.push_back(entity_id);
|
entity_id.set_value(StringRef(this->entity_id_));
|
||||||
|
|
||||||
api::HomeassistantServiceMap entity_value;
|
resp.data.emplace_back();
|
||||||
entity_value.key = "value";
|
auto &entity_value = resp.data.back();
|
||||||
entity_value.value = to_string(value);
|
entity_value.set_key(VALUE_KEY);
|
||||||
resp.data.push_back(entity_value);
|
// to_string() returns a temporary - must store it to avoid dangling reference
|
||||||
|
std::string value_str = to_string(value);
|
||||||
|
entity_value.set_value(StringRef(value_str));
|
||||||
|
|
||||||
api::global_api_server->send_homeassistant_service_call(resp);
|
api::global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
@ -40,17 +40,21 @@ void HomeassistantSwitch::write_state(bool state) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr auto SERVICE_ON = StringRef::from_lit("homeassistant.turn_on");
|
||||||
|
static constexpr auto SERVICE_OFF = StringRef::from_lit("homeassistant.turn_off");
|
||||||
|
static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id");
|
||||||
|
|
||||||
api::HomeassistantServiceResponse resp;
|
api::HomeassistantServiceResponse resp;
|
||||||
if (state) {
|
if (state) {
|
||||||
resp.service = "homeassistant.turn_on";
|
resp.set_service(SERVICE_ON);
|
||||||
} else {
|
} else {
|
||||||
resp.service = "homeassistant.turn_off";
|
resp.set_service(SERVICE_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
api::HomeassistantServiceMap entity_id_kv;
|
resp.data.emplace_back();
|
||||||
entity_id_kv.key = "entity_id";
|
auto &entity_id_kv = resp.data.back();
|
||||||
entity_id_kv.value = this->entity_id_;
|
entity_id_kv.set_key(ENTITY_ID_KEY);
|
||||||
resp.data.push_back(entity_id_kv);
|
entity_id_kv.set_value(StringRef(this->entity_id_));
|
||||||
|
|
||||||
api::global_api_server->send_homeassistant_service_call(resp);
|
api::global_api_server->send_homeassistant_service_call(resp);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ from esphome.const import (
|
|||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
CONF_VARIANT,
|
CONF_VARIANT,
|
||||||
|
Framework,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
|
|
||||||
@ -162,7 +163,15 @@ def _validate_method(value):
|
|||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.only_with_arduino,
|
cv.only_with_framework(
|
||||||
|
frameworks=Framework.ARDUINO,
|
||||||
|
suggestions={
|
||||||
|
Framework.ESP_IDF: (
|
||||||
|
"esp32_rmt_led_strip",
|
||||||
|
"light/esp32_rmt_led_strip",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
cv.require_framework_version(
|
cv.require_framework_version(
|
||||||
esp8266_arduino=cv.Version(2, 4, 0),
|
esp8266_arduino=cv.Version(2, 4, 0),
|
||||||
esp32_arduino=cv.Version(0, 0, 0),
|
esp32_arduino=cv.Version(0, 0, 0),
|
||||||
|
@ -23,6 +23,7 @@ from esphome.const import (
|
|||||||
KEY_TARGET_FRAMEWORK,
|
KEY_TARGET_FRAMEWORK,
|
||||||
KEY_TARGET_PLATFORM,
|
KEY_TARGET_PLATFORM,
|
||||||
PLATFORM_NRF52,
|
PLATFORM_NRF52,
|
||||||
|
CoreModel,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, EsphomeError, coroutine_with_priority
|
from esphome.core import CORE, EsphomeError, coroutine_with_priority
|
||||||
from esphome.storage_json import StorageJSON
|
from esphome.storage_json import StorageJSON
|
||||||
@ -108,6 +109,8 @@ async def to_code(config: ConfigType) -> None:
|
|||||||
cg.add_build_flag("-DUSE_NRF52")
|
cg.add_build_flag("-DUSE_NRF52")
|
||||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||||
cg.add_define("ESPHOME_VARIANT", "NRF52")
|
cg.add_define("ESPHOME_VARIANT", "NRF52")
|
||||||
|
# nRF52 processors are single-core
|
||||||
|
cg.add_define(CoreModel.SINGLE)
|
||||||
cg.add_platformio_option(CONF_FRAMEWORK, CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK])
|
cg.add_platformio_option(CONF_FRAMEWORK, CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK])
|
||||||
cg.add_platformio_option(
|
cg.add_platformio_option(
|
||||||
"platform",
|
"platform",
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace text {
|
namespace text {
|
||||||
@ -23,6 +24,7 @@ class TextTraits {
|
|||||||
// Set/get the pattern.
|
// Set/get the pattern.
|
||||||
void set_pattern(std::string pattern) { this->pattern_ = std::move(pattern); }
|
void set_pattern(std::string pattern) { this->pattern_ = std::move(pattern); }
|
||||||
std::string get_pattern() const { return this->pattern_; }
|
std::string get_pattern() const { return this->pattern_; }
|
||||||
|
StringRef get_pattern_ref() const { return StringRef(this->pattern_); }
|
||||||
|
|
||||||
// Set/get the frontend mode.
|
// Set/get the frontend mode.
|
||||||
void set_mode(TextMode mode) { this->mode_ = mode; }
|
void set_mode(TextMode mode) { this->mode_ = mode; }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fan
|
from esphome.components import fan
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_OUTPUT_ID, CONF_SPEED_COUNT, CONF_SWITCH_DATAPOINT
|
from esphome.const import CONF_ID, CONF_SPEED_COUNT, CONF_SWITCH_DATAPOINT
|
||||||
|
|
||||||
from .. import CONF_TUYA_ID, Tuya, tuya_ns
|
from .. import CONF_TUYA_ID, Tuya, tuya_ns
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ CONF_DIRECTION_DATAPOINT = "direction_datapoint"
|
|||||||
TuyaFan = tuya_ns.class_("TuyaFan", cg.Component, fan.Fan)
|
TuyaFan = tuya_ns.class_("TuyaFan", cg.Component, fan.Fan)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
fan.FAN_SCHEMA.extend(
|
fan.fan_schema(TuyaFan)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaFan),
|
|
||||||
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
|
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
|
||||||
cv.Optional(CONF_OSCILLATION_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_OSCILLATION_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_SPEED_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_SPEED_DATAPOINT): cv.uint8_t,
|
||||||
@ -24,7 +24,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Optional(CONF_DIRECTION_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_DIRECTION_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_SPEED_COUNT, default=3): cv.int_range(min=1, max=256),
|
cv.Optional(CONF_SPEED_COUNT, default=3): cv.int_range(min=1, max=256),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.has_at_least_one_key(CONF_SPEED_DATAPOINT, CONF_SWITCH_DATAPOINT),
|
cv.has_at_least_one_key(CONF_SPEED_DATAPOINT, CONF_SWITCH_DATAPOINT),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
parent = await cg.get_variable(config[CONF_TUYA_ID])
|
parent = await cg.get_variable(config[CONF_TUYA_ID])
|
||||||
|
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], parent, config[CONF_SPEED_COUNT])
|
var = cg.new_Pvariable(config[CONF_ID], parent, config[CONF_SPEED_COUNT])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
await fan.register_fan(var, config)
|
||||||
|
|
||||||
|
@ -238,10 +238,10 @@ void VoiceAssistant::loop() {
|
|||||||
|
|
||||||
api::VoiceAssistantRequest msg;
|
api::VoiceAssistantRequest msg;
|
||||||
msg.start = true;
|
msg.start = true;
|
||||||
msg.conversation_id = this->conversation_id_;
|
msg.set_conversation_id(StringRef(this->conversation_id_));
|
||||||
msg.flags = flags;
|
msg.flags = flags;
|
||||||
msg.audio_settings = audio_settings;
|
msg.audio_settings = audio_settings;
|
||||||
msg.wake_word_phrase = this->wake_word_;
|
msg.set_wake_word_phrase(StringRef(this->wake_word_));
|
||||||
this->wake_word_ = "";
|
this->wake_word_ = "";
|
||||||
|
|
||||||
// Reset media player state tracking
|
// Reset media player state tracking
|
||||||
@ -273,7 +273,7 @@ void VoiceAssistant::loop() {
|
|||||||
size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
|
size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
|
||||||
if (this->audio_mode_ == AUDIO_MODE_API) {
|
if (this->audio_mode_ == AUDIO_MODE_API) {
|
||||||
api::VoiceAssistantAudio msg;
|
api::VoiceAssistantAudio msg;
|
||||||
msg.data.assign((char *) this->send_buffer_, read_bytes);
|
msg.set_data(this->send_buffer_, read_bytes);
|
||||||
this->api_client_->send_message(msg, api::VoiceAssistantAudio::MESSAGE_TYPE);
|
this->api_client_->send_message(msg, api::VoiceAssistantAudio::MESSAGE_TYPE);
|
||||||
} else {
|
} else {
|
||||||
if (!this->udp_socket_running_) {
|
if (!this->udp_socket_running_) {
|
||||||
|
@ -73,6 +73,7 @@ from esphome.const import (
|
|||||||
TYPE_GIT,
|
TYPE_GIT,
|
||||||
TYPE_LOCAL,
|
TYPE_LOCAL,
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS,
|
VALID_SUBSTITUTIONS_CHARACTERS,
|
||||||
|
Framework,
|
||||||
__version__ as ESPHOME_VERSION,
|
__version__ as ESPHOME_VERSION,
|
||||||
)
|
)
|
||||||
from esphome.core import (
|
from esphome.core import (
|
||||||
@ -282,6 +283,38 @@ class FinalExternalInvalid(Invalid):
|
|||||||
"""Represents an invalid value in the final validation phase where the path should not be prepended."""
|
"""Represents an invalid value in the final validation phase where the path should not be prepended."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, order=True)
|
||||||
|
class Version:
|
||||||
|
major: int
|
||||||
|
minor: int
|
||||||
|
patch: int
|
||||||
|
extra: str = ""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.major}.{self.minor}.{self.patch}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, value: str) -> Version:
|
||||||
|
match = re.match(r"^(\d+).(\d+).(\d+)-?(\w*)$", value)
|
||||||
|
if match is None:
|
||||||
|
raise ValueError(f"Not a valid version number {value}")
|
||||||
|
major = int(match[1])
|
||||||
|
minor = int(match[2])
|
||||||
|
patch = int(match[3])
|
||||||
|
extra = match[4] or ""
|
||||||
|
return Version(major=major, minor=minor, patch=patch, extra=extra)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_beta(self) -> bool:
|
||||||
|
"""Check if this version is a beta version."""
|
||||||
|
return self.extra.startswith("b")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_dev(self) -> bool:
|
||||||
|
"""Check if this version is a development version."""
|
||||||
|
return self.extra.startswith("dev")
|
||||||
|
|
||||||
|
|
||||||
def check_not_templatable(value):
|
def check_not_templatable(value):
|
||||||
if isinstance(value, Lambda):
|
if isinstance(value, Lambda):
|
||||||
raise Invalid("This option is not templatable!")
|
raise Invalid("This option is not templatable!")
|
||||||
@ -619,16 +652,35 @@ def only_on(platforms):
|
|||||||
return validator_
|
return validator_
|
||||||
|
|
||||||
|
|
||||||
def only_with_framework(frameworks):
|
def only_with_framework(
|
||||||
|
frameworks: Framework | str | list[Framework | str], suggestions=None
|
||||||
|
):
|
||||||
"""Validate that this option can only be specified on the given frameworks."""
|
"""Validate that this option can only be specified on the given frameworks."""
|
||||||
if not isinstance(frameworks, list):
|
if not isinstance(frameworks, list):
|
||||||
frameworks = [frameworks]
|
frameworks = [frameworks]
|
||||||
|
|
||||||
|
frameworks = [Framework(framework) for framework in frameworks]
|
||||||
|
|
||||||
|
if suggestions is None:
|
||||||
|
suggestions = {}
|
||||||
|
|
||||||
|
version = Version.parse(ESPHOME_VERSION)
|
||||||
|
if version.is_beta:
|
||||||
|
docs_format = "https://beta.esphome.io/components/{path}"
|
||||||
|
elif version.is_dev:
|
||||||
|
docs_format = "https://next.esphome.io/components/{path}"
|
||||||
|
else:
|
||||||
|
docs_format = "https://esphome.io/components/{path}"
|
||||||
|
|
||||||
def validator_(obj):
|
def validator_(obj):
|
||||||
if CORE.target_framework not in frameworks:
|
if CORE.target_framework not in frameworks:
|
||||||
raise Invalid(
|
err_str = f"This feature is only available with framework(s) {', '.join([framework.value for framework in frameworks])}"
|
||||||
f"This feature is only available with frameworks {frameworks}"
|
if suggestion := suggestions.get(CORE.target_framework, None):
|
||||||
)
|
(component, docs_path) = suggestion
|
||||||
|
err_str += f"\nPlease use '{component}'"
|
||||||
|
if docs_path:
|
||||||
|
err_str += f": {docs_format.format(path=docs_path)}"
|
||||||
|
raise Invalid(err_str)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
return validator_
|
return validator_
|
||||||
@ -637,8 +689,8 @@ def only_with_framework(frameworks):
|
|||||||
only_on_esp32 = only_on(PLATFORM_ESP32)
|
only_on_esp32 = only_on(PLATFORM_ESP32)
|
||||||
only_on_esp8266 = only_on(PLATFORM_ESP8266)
|
only_on_esp8266 = only_on(PLATFORM_ESP8266)
|
||||||
only_on_rp2040 = only_on(PLATFORM_RP2040)
|
only_on_rp2040 = only_on(PLATFORM_RP2040)
|
||||||
only_with_arduino = only_with_framework("arduino")
|
only_with_arduino = only_with_framework(Framework.ARDUINO)
|
||||||
only_with_esp_idf = only_with_framework("esp-idf")
|
only_with_esp_idf = only_with_framework(Framework.ESP_IDF)
|
||||||
|
|
||||||
|
|
||||||
# Adapted from:
|
# Adapted from:
|
||||||
@ -1966,26 +2018,6 @@ def source_refresh(value: str):
|
|||||||
return positive_time_period_seconds(value)
|
return positive_time_period_seconds(value)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, order=True)
|
|
||||||
class Version:
|
|
||||||
major: int
|
|
||||||
minor: int
|
|
||||||
patch: int
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.major}.{self.minor}.{self.patch}"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse(cls, value: str) -> Version:
|
|
||||||
match = re.match(r"^(\d+).(\d+).(\d+)-?\w*$", value)
|
|
||||||
if match is None:
|
|
||||||
raise ValueError(f"Not a valid version number {value}")
|
|
||||||
major = int(match[1])
|
|
||||||
minor = int(match[2])
|
|
||||||
patch = int(match[3])
|
|
||||||
return Version(major=major, minor=minor, patch=patch)
|
|
||||||
|
|
||||||
|
|
||||||
def version_number(value):
|
def version_number(value):
|
||||||
value = string_strict(value)
|
value = string_strict(value)
|
||||||
try:
|
try:
|
||||||
|
@ -71,8 +71,11 @@ void Application::setup() {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
uint8_t new_app_state = STATUS_LED_WARNING;
|
uint8_t new_app_state = STATUS_LED_WARNING;
|
||||||
this->scheduler.call(millis());
|
uint32_t now = millis();
|
||||||
this->feed_wdt();
|
|
||||||
|
// Process pending loop enables to handle GPIO interrupts during setup
|
||||||
|
this->before_loop_tasks_(now);
|
||||||
|
|
||||||
for (uint32_t j = 0; j <= i; j++) {
|
for (uint32_t j = 0; j <= i; j++) {
|
||||||
// Update loop_component_start_time_ right before calling each component
|
// Update loop_component_start_time_ right before calling each component
|
||||||
this->loop_component_start_time_ = millis();
|
this->loop_component_start_time_ = millis();
|
||||||
@ -81,6 +84,8 @@ void Application::setup() {
|
|||||||
this->app_state_ |= new_app_state;
|
this->app_state_ |= new_app_state;
|
||||||
this->feed_wdt();
|
this->feed_wdt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->after_loop_tasks_();
|
||||||
this->app_state_ = new_app_state;
|
this->app_state_ = new_app_state;
|
||||||
yield();
|
yield();
|
||||||
} while (!component->can_proceed());
|
} while (!component->can_proceed());
|
||||||
@ -100,27 +105,7 @@ void Application::loop() {
|
|||||||
// Get the initial loop time at the start
|
// Get the initial loop time at the start
|
||||||
uint32_t last_op_end_time = millis();
|
uint32_t last_op_end_time = millis();
|
||||||
|
|
||||||
this->scheduler.call(last_op_end_time);
|
this->before_loop_tasks_(last_op_end_time);
|
||||||
|
|
||||||
// Feed WDT with time
|
|
||||||
this->feed_wdt(last_op_end_time);
|
|
||||||
|
|
||||||
// Process any pending enable_loop requests from ISRs
|
|
||||||
// This must be done before marking in_loop_ = true to avoid race conditions
|
|
||||||
if (this->has_pending_enable_loop_requests_) {
|
|
||||||
// Clear flag BEFORE processing to avoid race condition
|
|
||||||
// If ISR sets it during processing, we'll catch it next loop iteration
|
|
||||||
// This is safe because:
|
|
||||||
// 1. Each component has its own pending_enable_loop_ flag that we check
|
|
||||||
// 2. If we can't process a component (wrong state), enable_pending_loops_()
|
|
||||||
// will set this flag back to true
|
|
||||||
// 3. Any new ISR requests during processing will set the flag again
|
|
||||||
this->has_pending_enable_loop_requests_ = false;
|
|
||||||
this->enable_pending_loops_();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark that we're in the loop for safe reentrant modifications
|
|
||||||
this->in_loop_ = true;
|
|
||||||
|
|
||||||
for (this->current_loop_index_ = 0; this->current_loop_index_ < this->looping_components_active_end_;
|
for (this->current_loop_index_ = 0; this->current_loop_index_ < this->looping_components_active_end_;
|
||||||
this->current_loop_index_++) {
|
this->current_loop_index_++) {
|
||||||
@ -141,7 +126,7 @@ void Application::loop() {
|
|||||||
this->feed_wdt(last_op_end_time);
|
this->feed_wdt(last_op_end_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->in_loop_ = false;
|
this->after_loop_tasks_();
|
||||||
this->app_state_ = new_app_state;
|
this->app_state_ = new_app_state;
|
||||||
|
|
||||||
#ifdef USE_RUNTIME_STATS
|
#ifdef USE_RUNTIME_STATS
|
||||||
@ -411,6 +396,36 @@ void Application::enable_pending_loops_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::before_loop_tasks_(uint32_t loop_start_time) {
|
||||||
|
// Process scheduled tasks
|
||||||
|
this->scheduler.call(loop_start_time);
|
||||||
|
|
||||||
|
// Feed the watchdog timer
|
||||||
|
this->feed_wdt(loop_start_time);
|
||||||
|
|
||||||
|
// Process any pending enable_loop requests from ISRs
|
||||||
|
// This must be done before marking in_loop_ = true to avoid race conditions
|
||||||
|
if (this->has_pending_enable_loop_requests_) {
|
||||||
|
// Clear flag BEFORE processing to avoid race condition
|
||||||
|
// If ISR sets it during processing, we'll catch it next loop iteration
|
||||||
|
// This is safe because:
|
||||||
|
// 1. Each component has its own pending_enable_loop_ flag that we check
|
||||||
|
// 2. If we can't process a component (wrong state), enable_pending_loops_()
|
||||||
|
// will set this flag back to true
|
||||||
|
// 3. Any new ISR requests during processing will set the flag again
|
||||||
|
this->has_pending_enable_loop_requests_ = false;
|
||||||
|
this->enable_pending_loops_();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark that we're in the loop for safe reentrant modifications
|
||||||
|
this->in_loop_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::after_loop_tasks_() {
|
||||||
|
// Clear the in_loop_ flag to indicate we're done processing components
|
||||||
|
this->in_loop_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||||
bool Application::register_socket_fd(int fd) {
|
bool Application::register_socket_fd(int fd) {
|
||||||
// WARNING: This function is NOT thread-safe and must only be called from the main loop
|
// WARNING: This function is NOT thread-safe and must only be called from the main loop
|
||||||
|
@ -512,6 +512,8 @@ class Application {
|
|||||||
void enable_component_loop_(Component *component);
|
void enable_component_loop_(Component *component);
|
||||||
void enable_pending_loops_();
|
void enable_pending_loops_();
|
||||||
void activate_looping_component_(uint16_t index);
|
void activate_looping_component_(uint16_t index);
|
||||||
|
void before_loop_tasks_(uint32_t loop_start_time);
|
||||||
|
void after_loop_tasks_();
|
||||||
|
|
||||||
void feed_wdt_arch_();
|
void feed_wdt_arch_();
|
||||||
|
|
||||||
|
@ -54,6 +54,14 @@ class EntityBase {
|
|||||||
// Get/set this entity's icon
|
// Get/set this entity's icon
|
||||||
std::string get_icon() const;
|
std::string get_icon() const;
|
||||||
void set_icon(const char *icon);
|
void set_icon(const char *icon);
|
||||||
|
StringRef get_icon_ref() const {
|
||||||
|
static constexpr auto EMPTY_STRING = StringRef::from_lit("");
|
||||||
|
#ifdef USE_ENTITY_ICON
|
||||||
|
return this->icon_c_str_ == nullptr ? EMPTY_STRING : StringRef(this->icon_c_str_);
|
||||||
|
#else
|
||||||
|
return EMPTY_STRING;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
// Get/set this entity's device id
|
// Get/set this entity's device id
|
||||||
@ -105,6 +113,11 @@ class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming)
|
|||||||
std::string get_device_class();
|
std::string get_device_class();
|
||||||
/// Manually set the device class.
|
/// Manually set the device class.
|
||||||
void set_device_class(const char *device_class);
|
void set_device_class(const char *device_class);
|
||||||
|
/// Get the device class as StringRef
|
||||||
|
StringRef get_device_class_ref() const {
|
||||||
|
static constexpr auto EMPTY_STRING = StringRef::from_lit("");
|
||||||
|
return this->device_class_ == nullptr ? EMPTY_STRING : StringRef(this->device_class_);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char *device_class_{nullptr}; ///< Device class override
|
const char *device_class_{nullptr}; ///< Device class override
|
||||||
@ -116,6 +129,11 @@ class EntityBase_UnitOfMeasurement { // NOLINT(readability-identifier-naming)
|
|||||||
std::string get_unit_of_measurement();
|
std::string get_unit_of_measurement();
|
||||||
/// Manually set the unit of measurement.
|
/// Manually set the unit of measurement.
|
||||||
void set_unit_of_measurement(const char *unit_of_measurement);
|
void set_unit_of_measurement(const char *unit_of_measurement);
|
||||||
|
/// Get the unit of measurement as StringRef
|
||||||
|
StringRef get_unit_of_measurement_ref() const {
|
||||||
|
static constexpr auto EMPTY_STRING = StringRef::from_lit("");
|
||||||
|
return this->unit_of_measurement_ == nullptr ? EMPTY_STRING : StringRef(this->unit_of_measurement_);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char *unit_of_measurement_{nullptr}; ///< Unit of measurement override
|
const char *unit_of_measurement_{nullptr}; ///< Unit of measurement override
|
||||||
|
@ -113,8 +113,15 @@ def force_str(force: bool) -> str:
|
|||||||
class TypeInfo(ABC):
|
class TypeInfo(ABC):
|
||||||
"""Base class for all type information."""
|
"""Base class for all type information."""
|
||||||
|
|
||||||
def __init__(self, field: descriptor.FieldDescriptorProto) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
field: descriptor.FieldDescriptorProto,
|
||||||
|
needs_decode: bool = True,
|
||||||
|
needs_encode: bool = True,
|
||||||
|
) -> None:
|
||||||
self._field = field
|
self._field = field
|
||||||
|
self._needs_decode = needs_decode
|
||||||
|
self._needs_encode = needs_encode
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_value(self) -> str:
|
def default_value(self) -> str:
|
||||||
@ -313,9 +320,16 @@ def validate_field_type(field_type: int, field_name: str = "") -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_field_type_info(field: descriptor.FieldDescriptorProto) -> TypeInfo:
|
def create_field_type_info(
|
||||||
|
field: descriptor.FieldDescriptorProto,
|
||||||
|
needs_decode: bool = True,
|
||||||
|
needs_encode: bool = True,
|
||||||
|
) -> TypeInfo:
|
||||||
"""Create the appropriate TypeInfo instance for a field, handling repeated fields and custom options."""
|
"""Create the appropriate TypeInfo instance for a field, handling repeated fields and custom options."""
|
||||||
if field.label == 3: # repeated
|
if field.label == 3: # repeated
|
||||||
|
# Check if this repeated field has fixed_array_size option
|
||||||
|
if (fixed_size := get_field_opt(field, pb.fixed_array_size)) is not None:
|
||||||
|
return FixedArrayRepeatedType(field, fixed_size)
|
||||||
return RepeatedTypeInfo(field)
|
return RepeatedTypeInfo(field)
|
||||||
|
|
||||||
# Check for fixed_array_size option on bytes fields
|
# Check for fixed_array_size option on bytes fields
|
||||||
@ -325,6 +339,14 @@ def create_field_type_info(field: descriptor.FieldDescriptorProto) -> TypeInfo:
|
|||||||
):
|
):
|
||||||
return FixedArrayBytesType(field, fixed_size)
|
return FixedArrayBytesType(field, fixed_size)
|
||||||
|
|
||||||
|
# Special handling for bytes fields
|
||||||
|
if field.type == 12:
|
||||||
|
return BytesType(field, needs_decode, needs_encode)
|
||||||
|
|
||||||
|
# Special handling for string fields
|
||||||
|
if field.type == 9:
|
||||||
|
return StringType(field, needs_decode, needs_encode)
|
||||||
|
|
||||||
validate_field_type(field.type, field.name)
|
validate_field_type(field.type, field.name)
|
||||||
return TYPE_INFO[field.type](field)
|
return TYPE_INFO[field.type](field)
|
||||||
|
|
||||||
@ -525,13 +547,68 @@ class StringType(TypeInfo):
|
|||||||
encode_func = "encode_string"
|
encode_func = "encode_string"
|
||||||
wire_type = WireType.LENGTH_DELIMITED # Uses wire type 2
|
wire_type = WireType.LENGTH_DELIMITED # Uses wire type 2
|
||||||
|
|
||||||
|
@property
|
||||||
|
def public_content(self) -> list[str]:
|
||||||
|
content: list[str] = []
|
||||||
|
# Add std::string storage if message needs decoding
|
||||||
|
if self._needs_decode:
|
||||||
|
content.append(f"std::string {self.field_name}{{}};")
|
||||||
|
|
||||||
|
if self._needs_encode:
|
||||||
|
content.extend(
|
||||||
|
[
|
||||||
|
# Add StringRef field if message needs encoding
|
||||||
|
f"StringRef {self.field_name}_ref_{{}};",
|
||||||
|
# Add setter method if message needs encoding
|
||||||
|
f"void set_{self.field_name}(const StringRef &ref) {{",
|
||||||
|
f" this->{self.field_name}_ref_ = ref;",
|
||||||
|
"}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return content
|
||||||
|
|
||||||
|
@property
|
||||||
|
def encode_content(self) -> str:
|
||||||
|
return f"buffer.encode_string({self.number}, this->{self.field_name}_ref_);"
|
||||||
|
|
||||||
def dump(self, name):
|
def dump(self, name):
|
||||||
o = f'out.append("\'").append({name}).append("\'");'
|
# If name is 'it', this is a repeated field element - always use string
|
||||||
return o
|
if name == "it":
|
||||||
|
return "append_quoted_string(out, StringRef(it));"
|
||||||
|
|
||||||
|
# For SOURCE_CLIENT only, always use std::string
|
||||||
|
if not self._needs_encode:
|
||||||
|
return f'out.append("\'").append(this->{self.field_name}).append("\'");'
|
||||||
|
|
||||||
|
# For SOURCE_SERVER, always use StringRef
|
||||||
|
if not self._needs_decode:
|
||||||
|
return f"append_quoted_string(out, this->{self.field_name}_ref_);"
|
||||||
|
|
||||||
|
# For SOURCE_BOTH, check if StringRef is set (sending) or use string (received)
|
||||||
|
return (
|
||||||
|
f"if (!this->{self.field_name}_ref_.empty()) {{"
|
||||||
|
f' out.append("\'").append(this->{self.field_name}_ref_.c_str()).append("\'");'
|
||||||
|
f"}} else {{"
|
||||||
|
f' out.append("\'").append(this->{self.field_name}).append("\'");'
|
||||||
|
f"}}"
|
||||||
|
)
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
|
# For SOURCE_CLIENT only messages, use the string field directly
|
||||||
|
if not self._needs_encode:
|
||||||
return self._get_simple_size_calculation(name, force, "add_string_field")
|
return self._get_simple_size_calculation(name, force, "add_string_field")
|
||||||
|
|
||||||
|
# Check if this is being called from a repeated field context
|
||||||
|
# In that case, 'name' will be 'it' and we need to use the repeated version
|
||||||
|
if name == "it":
|
||||||
|
# For repeated fields, we need to use add_string_field_repeated which includes field ID
|
||||||
|
field_id_size = self.calculate_field_id_size()
|
||||||
|
return f"ProtoSize::add_string_field_repeated(total_size, {field_id_size}, it);"
|
||||||
|
|
||||||
|
# For messages that need encoding, use the StringRef size
|
||||||
|
field_id_size = self.calculate_field_id_size()
|
||||||
|
return f"ProtoSize::add_string_field(total_size, {field_id_size}, this->{self.field_name}_ref_.size());"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string
|
||||||
|
|
||||||
@ -578,6 +655,8 @@ class MessageType(TypeInfo):
|
|||||||
return self._get_simple_size_calculation(name, force, "add_message_object")
|
return self._get_simple_size_calculation(name, force, "add_message_object")
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
|
# For message types, we can't easily estimate the submessage size without
|
||||||
|
# access to the actual message definition. This is just a rough estimate.
|
||||||
return (
|
return (
|
||||||
self.calculate_field_id_size() + 16
|
self.calculate_field_id_size() + 16
|
||||||
) # field ID + 16 bytes estimated submessage
|
) # field ID + 16 bytes estimated submessage
|
||||||
@ -589,20 +668,59 @@ class BytesType(TypeInfo):
|
|||||||
default_value = ""
|
default_value = ""
|
||||||
reference_type = "std::string &"
|
reference_type = "std::string &"
|
||||||
const_reference_type = "const std::string &"
|
const_reference_type = "const std::string &"
|
||||||
decode_length = "value.as_string()"
|
|
||||||
encode_func = "encode_bytes"
|
encode_func = "encode_bytes"
|
||||||
|
decode_length = "value.as_string()"
|
||||||
wire_type = WireType.LENGTH_DELIMITED # Uses wire type 2
|
wire_type = WireType.LENGTH_DELIMITED # Uses wire type 2
|
||||||
|
|
||||||
|
@property
|
||||||
|
def public_content(self) -> list[str]:
|
||||||
|
content: list[str] = []
|
||||||
|
# Add std::string storage if message needs decoding
|
||||||
|
if self._needs_decode:
|
||||||
|
content.append(f"std::string {self.field_name}{{}};")
|
||||||
|
|
||||||
|
if self._needs_encode:
|
||||||
|
content.extend(
|
||||||
|
[
|
||||||
|
# Add pointer/length fields if message needs encoding
|
||||||
|
f"const uint8_t* {self.field_name}_ptr_{{nullptr}};",
|
||||||
|
f"size_t {self.field_name}_len_{{0}};",
|
||||||
|
# Add setter method if message needs encoding
|
||||||
|
f"void set_{self.field_name}(const uint8_t* data, size_t len) {{",
|
||||||
|
f" this->{self.field_name}_ptr_ = data;",
|
||||||
|
f" this->{self.field_name}_len_ = len;",
|
||||||
|
"}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return content
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def encode_content(self) -> str:
|
def encode_content(self) -> str:
|
||||||
return f"buffer.encode_bytes({self.number}, reinterpret_cast<const uint8_t*>(this->{self.field_name}.data()), this->{self.field_name}.size());"
|
return f"buffer.encode_bytes({self.number}, this->{self.field_name}_ptr_, this->{self.field_name}_len_);"
|
||||||
|
|
||||||
def dump(self, name: str) -> str:
|
def dump(self, name: str) -> str:
|
||||||
o = f"out.append(format_hex_pretty({name}));"
|
ptr_dump = f"format_hex_pretty(this->{self.field_name}_ptr_, this->{self.field_name}_len_)"
|
||||||
return o
|
str_dump = f"format_hex_pretty(reinterpret_cast<const uint8_t*>(this->{self.field_name}.data()), this->{self.field_name}.size())"
|
||||||
|
|
||||||
|
# For SOURCE_CLIENT only, always use std::string
|
||||||
|
if not self._needs_encode:
|
||||||
|
return f"out.append({str_dump});"
|
||||||
|
|
||||||
|
# For SOURCE_SERVER, always use pointer/length
|
||||||
|
if not self._needs_decode:
|
||||||
|
return f"out.append({ptr_dump});"
|
||||||
|
|
||||||
|
# For SOURCE_BOTH, check if pointer is set (sending) or use string (received)
|
||||||
|
return (
|
||||||
|
f"if (this->{self.field_name}_ptr_ != nullptr) {{\n"
|
||||||
|
f" out.append({ptr_dump});\n"
|
||||||
|
f" }} else {{\n"
|
||||||
|
f" out.append({str_dump});\n"
|
||||||
|
f" }}"
|
||||||
|
)
|
||||||
|
|
||||||
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
return self._get_simple_size_calculation(name, force, "add_string_field")
|
return f"ProtoSize::add_bytes_field(total_size, {self.calculate_field_id_size()}, this->{self.field_name}_len_);"
|
||||||
|
|
||||||
def get_estimated_size(self) -> int:
|
def get_estimated_size(self) -> int:
|
||||||
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes
|
return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes
|
||||||
@ -829,6 +947,111 @@ class SInt64Type(TypeInfo):
|
|||||||
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint
|
||||||
|
|
||||||
|
|
||||||
|
class FixedArrayRepeatedType(TypeInfo):
|
||||||
|
"""Special type for fixed-size repeated fields using std::array.
|
||||||
|
|
||||||
|
Fixed arrays are only supported for encoding (SOURCE_SERVER) since we cannot
|
||||||
|
control how many items we receive when decoding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, field: descriptor.FieldDescriptorProto, size: int) -> None:
|
||||||
|
super().__init__(field)
|
||||||
|
self.array_size = size
|
||||||
|
# Create the element type info
|
||||||
|
validate_field_type(field.type, field.name)
|
||||||
|
self._ti: TypeInfo = TYPE_INFO[field.type](field)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cpp_type(self) -> str:
|
||||||
|
return f"std::array<{self._ti.cpp_type}, {self.array_size}>"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reference_type(self) -> str:
|
||||||
|
return f"{self.cpp_type} &"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def const_reference_type(self) -> str:
|
||||||
|
return f"const {self.cpp_type} &"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wire_type(self) -> WireType:
|
||||||
|
"""Get the wire type for this fixed array field."""
|
||||||
|
return self._ti.wire_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def public_content(self) -> list[str]:
|
||||||
|
# Just the array member, no index needed since we don't decode
|
||||||
|
return [f"{self.cpp_type} {self.field_name}{{}};"]
|
||||||
|
|
||||||
|
# No decode methods needed - fixed arrays don't support decoding
|
||||||
|
# The base class TypeInfo already returns None for all decode properties
|
||||||
|
|
||||||
|
@property
|
||||||
|
def encode_content(self) -> str:
|
||||||
|
# Helper to generate encode statement for a single element
|
||||||
|
def encode_element(element: str) -> str:
|
||||||
|
if isinstance(self._ti, EnumType):
|
||||||
|
return f"buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>({element}), true);"
|
||||||
|
else:
|
||||||
|
return f"buffer.{self._ti.encode_func}({self.number}, {element}, true);"
|
||||||
|
|
||||||
|
# Unroll small arrays for efficiency
|
||||||
|
if self.array_size == 1:
|
||||||
|
return encode_element(f"this->{self.field_name}[0]")
|
||||||
|
elif self.array_size == 2:
|
||||||
|
return (
|
||||||
|
encode_element(f"this->{self.field_name}[0]")
|
||||||
|
+ "\n "
|
||||||
|
+ encode_element(f"this->{self.field_name}[1]")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use loops for larger arrays
|
||||||
|
o = f"for (const auto &it : this->{self.field_name}) {{\n"
|
||||||
|
o += f" {encode_element('it')}\n"
|
||||||
|
o += "}"
|
||||||
|
return o
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dump_content(self) -> str:
|
||||||
|
o = f"for (const auto &it : this->{self.field_name}) {{\n"
|
||||||
|
o += f' out.append(" {self.name}: ");\n'
|
||||||
|
o += indent(self._ti.dump("it")) + "\n"
|
||||||
|
o += ' out.append("\\n");\n'
|
||||||
|
o += "}\n"
|
||||||
|
return o
|
||||||
|
|
||||||
|
def dump(self, name: str) -> str:
|
||||||
|
# This is used when dumping the array itself (not its elements)
|
||||||
|
# Since dump_content handles the iteration, this is not used directly
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_size_calculation(self, name: str, force: bool = False) -> str:
|
||||||
|
# For fixed arrays, we always encode all elements
|
||||||
|
|
||||||
|
# Special case for single-element arrays - no loop needed
|
||||||
|
if self.array_size == 1:
|
||||||
|
return self._ti.get_size_calculation(f"{name}[0]", True)
|
||||||
|
|
||||||
|
# Special case for 2-element arrays - unroll the calculation
|
||||||
|
if self.array_size == 2:
|
||||||
|
return (
|
||||||
|
self._ti.get_size_calculation(f"{name}[0]", True)
|
||||||
|
+ "\n "
|
||||||
|
+ self._ti.get_size_calculation(f"{name}[1]", True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use loops for larger arrays
|
||||||
|
o = f"for (const auto &it : {name}) {{\n"
|
||||||
|
o += f" {self._ti.get_size_calculation('it', True)}\n"
|
||||||
|
o += "}"
|
||||||
|
return o
|
||||||
|
|
||||||
|
def get_estimated_size(self) -> int:
|
||||||
|
# For fixed arrays, estimate underlying type size * array size
|
||||||
|
underlying_size = self._ti.get_estimated_size()
|
||||||
|
return underlying_size * self.array_size
|
||||||
|
|
||||||
|
|
||||||
class RepeatedTypeInfo(TypeInfo):
|
class RepeatedTypeInfo(TypeInfo):
|
||||||
def __init__(self, field: descriptor.FieldDescriptorProto) -> None:
|
def __init__(self, field: descriptor.FieldDescriptorProto) -> None:
|
||||||
super().__init__(field)
|
super().__init__(field)
|
||||||
@ -1257,7 +1480,20 @@ def build_message_type(
|
|||||||
if field.options.deprecated:
|
if field.options.deprecated:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ti = create_field_type_info(field)
|
# Validate that fixed_array_size is only used in encode-only messages
|
||||||
|
if (
|
||||||
|
needs_decode
|
||||||
|
and field.label == 3
|
||||||
|
and get_field_opt(field, pb.fixed_array_size) is not None
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"Message '{desc.name}' uses fixed_array_size on field '{field.name}' "
|
||||||
|
f"but has source={SOURCE_NAMES[source]}. "
|
||||||
|
f"Fixed arrays are only supported for SOURCE_SERVER (encode-only) messages "
|
||||||
|
f"since we cannot trust or control the number of items received from clients."
|
||||||
|
)
|
||||||
|
|
||||||
|
ti = create_field_type_info(field, needs_decode, needs_encode)
|
||||||
|
|
||||||
# Skip field declarations for fields that are in the base class
|
# Skip field declarations for fields that are in the base class
|
||||||
# but include their encode/decode logic
|
# but include their encode/decode logic
|
||||||
@ -1446,6 +1682,12 @@ SOURCE_BOTH = 0
|
|||||||
SOURCE_SERVER = 1
|
SOURCE_SERVER = 1
|
||||||
SOURCE_CLIENT = 2
|
SOURCE_CLIENT = 2
|
||||||
|
|
||||||
|
SOURCE_NAMES = {
|
||||||
|
SOURCE_BOTH: "SOURCE_BOTH",
|
||||||
|
SOURCE_SERVER: "SOURCE_SERVER",
|
||||||
|
SOURCE_CLIENT: "SOURCE_CLIENT",
|
||||||
|
}
|
||||||
|
|
||||||
RECEIVE_CASES: dict[int, tuple[str, str | None]] = {}
|
RECEIVE_CASES: dict[int, tuple[str, str | None]] = {}
|
||||||
|
|
||||||
ifdefs: dict[str, str] = {}
|
ifdefs: dict[str, str] = {}
|
||||||
@ -1572,10 +1814,20 @@ def build_base_class(
|
|||||||
public_content = []
|
public_content = []
|
||||||
protected_content = []
|
protected_content = []
|
||||||
|
|
||||||
|
# Determine if any message using this base class needs decoding/encoding
|
||||||
|
needs_decode = any(
|
||||||
|
message_source_map.get(msg.name, SOURCE_BOTH) in (SOURCE_BOTH, SOURCE_CLIENT)
|
||||||
|
for msg in messages
|
||||||
|
)
|
||||||
|
needs_encode = any(
|
||||||
|
message_source_map.get(msg.name, SOURCE_BOTH) in (SOURCE_BOTH, SOURCE_SERVER)
|
||||||
|
for msg in messages
|
||||||
|
)
|
||||||
|
|
||||||
# For base classes, we only declare the fields but don't handle encode/decode
|
# For base classes, we only declare the fields but don't handle encode/decode
|
||||||
# The derived classes will handle encoding/decoding with their specific field numbers
|
# The derived classes will handle encoding/decoding with their specific field numbers
|
||||||
for field in common_fields:
|
for field in common_fields:
|
||||||
ti = create_field_type_info(field)
|
ti = create_field_type_info(field, needs_decode, needs_encode)
|
||||||
|
|
||||||
# Get field_ifdef if it's consistent across all messages
|
# Get field_ifdef if it's consistent across all messages
|
||||||
field_ifdef = get_common_field_ifdef(field.name, messages)
|
field_ifdef = get_common_field_ifdef(field.name, messages)
|
||||||
@ -1586,12 +1838,6 @@ def build_base_class(
|
|||||||
if ti.public_content:
|
if ti.public_content:
|
||||||
public_content.extend(wrap_with_ifdef(ti.public_content, field_ifdef))
|
public_content.extend(wrap_with_ifdef(ti.public_content, field_ifdef))
|
||||||
|
|
||||||
# Determine if any message using this base class needs decoding
|
|
||||||
needs_decode = any(
|
|
||||||
message_source_map.get(msg.name, SOURCE_BOTH) in (SOURCE_BOTH, SOURCE_CLIENT)
|
|
||||||
for msg in messages
|
|
||||||
)
|
|
||||||
|
|
||||||
# Build header
|
# Build header
|
||||||
parent_class = "ProtoDecodableMessage" if needs_decode else "ProtoMessage"
|
parent_class = "ProtoDecodableMessage" if needs_decode else "ProtoMessage"
|
||||||
out = f"class {base_class_name} : public {parent_class} {{\n"
|
out = f"class {base_class_name} : public {parent_class} {{\n"
|
||||||
@ -1715,6 +1961,7 @@ def main() -> None:
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
|
||||||
@ -1748,6 +1995,15 @@ namespace api {
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
// Helper function to append a quoted string, handling empty StringRef
|
||||||
|
static inline void append_quoted_string(std::string &out, const StringRef &ref) {
|
||||||
|
out.append("'");
|
||||||
|
if (!ref.empty()) {
|
||||||
|
out.append(ref.c_str());
|
||||||
|
}
|
||||||
|
out.append("'");
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
content += "namespace enums {\n\n"
|
content += "namespace enums {\n\n"
|
||||||
@ -1988,7 +2244,13 @@ static const char *const TAG = "api.service";
|
|||||||
cpp += f"#ifdef {ifdef}\n"
|
cpp += f"#ifdef {ifdef}\n"
|
||||||
|
|
||||||
hpp_protected += f" void {on_func}(const {inp} &msg) override;\n"
|
hpp_protected += f" void {on_func}(const {inp} &msg) override;\n"
|
||||||
hpp += f" virtual {ret} {func}(const {inp} &msg) = 0;\n"
|
|
||||||
|
# For non-void methods, generate a send_ method instead of return-by-value
|
||||||
|
if is_void:
|
||||||
|
hpp += f" virtual void {func}(const {inp} &msg) = 0;\n"
|
||||||
|
else:
|
||||||
|
hpp += f" virtual bool send_{func}_response(const {inp} &msg) = 0;\n"
|
||||||
|
|
||||||
cpp += f"void {class_name}::{on_func}(const {inp} &msg) {{\n"
|
cpp += f"void {class_name}::{on_func}(const {inp} &msg) {{\n"
|
||||||
|
|
||||||
# Start with authentication/connection check if needed
|
# Start with authentication/connection check if needed
|
||||||
@ -2006,10 +2268,7 @@ static const char *const TAG = "api.service";
|
|||||||
if is_void:
|
if is_void:
|
||||||
handler_body = f"this->{func}(msg);\n"
|
handler_body = f"this->{func}(msg);\n"
|
||||||
else:
|
else:
|
||||||
handler_body = f"{ret} ret = this->{func}(msg);\n"
|
handler_body = f"if (!this->send_{func}_response(msg)) {{\n"
|
||||||
handler_body += (
|
|
||||||
f"if (!this->send_message(ret, {ret}::MESSAGE_TYPE)) {{\n"
|
|
||||||
)
|
|
||||||
handler_body += " this->on_fatal_error();\n"
|
handler_body += " this->on_fatal_error();\n"
|
||||||
handler_body += "}\n"
|
handler_body += "}\n"
|
||||||
|
|
||||||
@ -2021,8 +2280,7 @@ static const char *const TAG = "api.service";
|
|||||||
if is_void:
|
if is_void:
|
||||||
body += f"this->{func}(msg);\n"
|
body += f"this->{func}(msg);\n"
|
||||||
else:
|
else:
|
||||||
body += f"{ret} ret = this->{func}(msg);\n"
|
body += f"if (!this->send_{func}_response(msg)) {{\n"
|
||||||
body += f"if (!this->send_message(ret, {ret}::MESSAGE_TYPE)) {{\n"
|
|
||||||
body += " this->on_fatal_error();\n"
|
body += " this->on_fatal_error();\n"
|
||||||
body += "}\n"
|
body += "}\n"
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ def test_framework_specific_errors(
|
|||||||
|
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
cv.Invalid,
|
cv.Invalid,
|
||||||
match=r"This feature is only available with frameworks \['esp-idf'\]",
|
match=r"This feature is only available with framework\(s\) esp-idf",
|
||||||
):
|
):
|
||||||
run_schema_validation({"model": "wt32-sc01-plus"})
|
run_schema_validation({"model": "wt32-sc01-plus"})
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user