mirror of
https://github.com/esphome/esphome.git
synced 2026-01-13 13:37:39 -07:00
Compare commits
23 Commits
vbus
...
integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ae9aad168 | ||
|
|
2c803873b7 | ||
|
|
77307016af | ||
|
|
fb3d287267 | ||
|
|
ddd43f466a | ||
|
|
d1ff959f4c | ||
|
|
30cb8336f2 | ||
|
|
31f4e0ee48 | ||
|
|
174b68712e | ||
|
|
16ba98d213 | ||
|
|
91d52710e4 | ||
|
|
e5936e92a1 | ||
|
|
3610c96177 | ||
|
|
9542882464 | ||
|
|
748a6d79a3 | ||
|
|
c48ebe1ebb | ||
|
|
b51d9622cb | ||
|
|
af5ea02978 | ||
|
|
48eaaa8be0 | ||
|
|
2f3ee0e15a | ||
|
|
57afa8be6a | ||
|
|
e468d5afd0 | ||
|
|
516ab539e1 |
@@ -1 +1 @@
|
||||
94557f94be073390342833aff12ef8676a8b597db5fa770a5a1232e9425cb48f
|
||||
4268ab0b5150f79ab1c317e8f3834c8bb0b4c8122da4f6b1fd67c49d0f2098c9
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include LICENSE
|
||||
include README.md
|
||||
include requirements.txt
|
||||
recursive-include esphome *.yaml
|
||||
recursive-include esphome *.cpp *.h *.tcc *.c
|
||||
recursive-include esphome *.py.script
|
||||
recursive-include esphome LICENSE.txt
|
||||
|
||||
@@ -11,16 +11,6 @@ FROM base-source-${BUILD_TYPE} AS base
|
||||
|
||||
RUN git config --system --add safe.directory "*"
|
||||
|
||||
# Install build tools for Python packages that require compilation
|
||||
# (e.g., ruamel.yaml.clibz used by ESP-IDF's idf-component-manager)
|
||||
RUN if command -v apk > /dev/null; then \
|
||||
apk add --no-cache build-base; \
|
||||
else \
|
||||
apt-get update \
|
||||
&& apt-get install -y --no-install-recommends build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
RUN pip install --no-cache-dir -U pip uv==0.6.14
|
||||
|
||||
@@ -431,33 +431,25 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
|
||||
while tries < 5:
|
||||
try:
|
||||
with ser:
|
||||
buffer = b""
|
||||
ser.timeout = 0.1 # 100ms timeout for non-blocking reads
|
||||
while True:
|
||||
try:
|
||||
# Read all available data and timestamp it
|
||||
chunk = ser.read(ser.in_waiting or 1)
|
||||
if not chunk:
|
||||
continue
|
||||
time_ = datetime.now()
|
||||
nanoseconds = time_.microsecond // 1000
|
||||
time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
|
||||
|
||||
# Add to buffer and process complete lines
|
||||
buffer += chunk
|
||||
while b"\n" in buffer:
|
||||
raw_line, buffer = buffer.split(b"\n", 1)
|
||||
line = raw_line.replace(b"\r", b"").decode(
|
||||
"utf8", "backslashreplace"
|
||||
)
|
||||
safe_print(parser.parse_line(line, time_str))
|
||||
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
config, line, backtrace_state=backtrace_state
|
||||
)
|
||||
raw = ser.readline()
|
||||
except serial.SerialException:
|
||||
_LOGGER.error("Serial port closed!")
|
||||
return 0
|
||||
line = (
|
||||
raw.replace(b"\r", b"")
|
||||
.replace(b"\n", b"")
|
||||
.decode("utf8", "backslashreplace")
|
||||
)
|
||||
time_ = datetime.now()
|
||||
nanoseconds = time_.microsecond // 1000
|
||||
time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
|
||||
safe_print(parser.parse_line(line, time_str))
|
||||
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
config, line, backtrace_state=backtrace_state
|
||||
)
|
||||
except serial.SerialException:
|
||||
tries += 1
|
||||
time.sleep(1)
|
||||
@@ -804,13 +796,7 @@ def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
exit_code = compile_program(args, config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
if CORE.is_host:
|
||||
from esphome.platformio_api import get_idedata
|
||||
|
||||
program_path = str(get_idedata(config).firmware_elf_path)
|
||||
_LOGGER.info("Successfully compiled program to path '%s'", program_path)
|
||||
else:
|
||||
_LOGGER.info("Successfully compiled program.")
|
||||
_LOGGER.info("Successfully compiled program.")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -860,8 +846,10 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
if CORE.is_host:
|
||||
from esphome.platformio_api import get_idedata
|
||||
|
||||
program_path = str(get_idedata(config).firmware_elf_path)
|
||||
_LOGGER.info("Running program from path '%s'", program_path)
|
||||
idedata = get_idedata(config)
|
||||
if idedata is None:
|
||||
return 1
|
||||
program_path = idedata.raw["prog_path"]
|
||||
return run_external_process(program_path)
|
||||
|
||||
# Get devices, resolving special identifiers like OTA
|
||||
|
||||
@@ -30,9 +30,7 @@ void A01nyubComponent::check_buffer_() {
|
||||
ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters);
|
||||
this->publish_state(meters);
|
||||
} else {
|
||||
char hex_buf[format_hex_pretty_size(4)];
|
||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s",
|
||||
format_hex_pretty_to(hex_buf, this->buffer_.data(), this->buffer_.size()));
|
||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "checksum failed: %02x != %02x", checksum, this->buffer_[3]);
|
||||
|
||||
@@ -29,9 +29,7 @@ void A02yyuwComponent::check_buffer_() {
|
||||
ESP_LOGV(TAG, "Distance from sensor: %f mm", distance);
|
||||
this->publish_state(distance);
|
||||
} else {
|
||||
char hex_buf[format_hex_pretty_size(4)];
|
||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s",
|
||||
format_hex_pretty_to(hex_buf, this->buffer_.data(), this->buffer_.size()));
|
||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "checksum failed: %02x != %02x", checksum, this->buffer_[3]);
|
||||
|
||||
@@ -25,13 +25,11 @@ class AddressableLightDisplay : public display::DisplayBuffer {
|
||||
if (enabled_ && !enabled) { // enabled -> disabled
|
||||
// - Tell the parent light to refresh, effectively wiping the display. Also
|
||||
// restores the previous effect (if any).
|
||||
if (this->last_effect_index_.has_value()) {
|
||||
light_state_->make_call().set_effect(*this->last_effect_index_).perform();
|
||||
}
|
||||
light_state_->make_call().set_effect(this->last_effect_).perform();
|
||||
|
||||
} else if (!enabled_ && enabled) { // disabled -> enabled
|
||||
// - Save the current effect index.
|
||||
this->last_effect_index_ = light_state_->get_current_effect_index();
|
||||
// - Save the current effect.
|
||||
this->last_effect_ = light_state_->get_effect_name();
|
||||
// - Disable any current effect.
|
||||
light_state_->make_call().set_effect(0).perform();
|
||||
}
|
||||
@@ -58,7 +56,7 @@ class AddressableLightDisplay : public display::DisplayBuffer {
|
||||
int32_t width_;
|
||||
int32_t height_;
|
||||
std::vector<Color> addressable_light_buffer_;
|
||||
optional<uint32_t> last_effect_index_;
|
||||
optional<std::string> last_effect_;
|
||||
optional<std::function<int(int, int)>> pixel_mapper_f_;
|
||||
};
|
||||
} // namespace addressable_light
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
static const char *const TAG = "alarm_control_panel";
|
||||
|
||||
@@ -114,4 +115,5 @@ void AlarmControlPanel::disarm(optional<std::string> code) {
|
||||
call.perform();
|
||||
}
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
} // namespace alarm_control_panel
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "alarm_control_panel_call.h"
|
||||
#include "alarm_control_panel_state.h"
|
||||
|
||||
@@ -7,7 +9,8 @@
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
enum AlarmControlPanelFeature : uint8_t {
|
||||
// Matches Home Assistant values
|
||||
@@ -138,4 +141,5 @@ class AlarmControlPanel : public EntityBase {
|
||||
LazyCallbackManager<void()> ready_callback_{};
|
||||
};
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
} // namespace alarm_control_panel
|
||||
} // namespace esphome
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
static const char *const TAG = "alarm_control_panel";
|
||||
|
||||
@@ -98,4 +99,5 @@ void AlarmControlPanelCall::perform() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
} // namespace alarm_control_panel
|
||||
} // namespace esphome
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
class AlarmControlPanel;
|
||||
|
||||
@@ -35,4 +36,5 @@ class AlarmControlPanelCall {
|
||||
void validate_();
|
||||
};
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
} // namespace alarm_control_panel
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "alarm_control_panel_state.h"
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state) {
|
||||
switch (state) {
|
||||
@@ -29,4 +30,5 @@ const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState stat
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
} // namespace alarm_control_panel
|
||||
} // namespace esphome
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
#include <cstdint>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
enum AlarmControlPanelState : uint8_t {
|
||||
ACP_STATE_DISARMED = 0,
|
||||
@@ -24,4 +25,5 @@ enum AlarmControlPanelState : uint8_t {
|
||||
*/
|
||||
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state);
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
} // namespace alarm_control_panel
|
||||
} // namespace esphome
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "alarm_control_panel.h"
|
||||
|
||||
namespace esphome::alarm_control_panel {
|
||||
namespace esphome {
|
||||
namespace alarm_control_panel {
|
||||
|
||||
/// Trigger on any state change
|
||||
class StateTrigger : public Trigger<> {
|
||||
@@ -164,4 +165,5 @@ template<typename... Ts> class AlarmControlPanelCondition : public Condition<Ts.
|
||||
AlarmControlPanel *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::alarm_control_panel
|
||||
} // namespace alarm_control_panel
|
||||
} // namespace esphome
|
||||
|
||||
@@ -102,7 +102,7 @@ message HelloRequest {
|
||||
// For example "Home Assistant"
|
||||
// Not strictly necessary to send but nice for debugging
|
||||
// purposes.
|
||||
string client_info = 1;
|
||||
string client_info = 1 [(pointer_to_buffer) = true];
|
||||
uint32 api_version_major = 2;
|
||||
uint32 api_version_minor = 3;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ message AuthenticationRequest {
|
||||
option (ifdef) = "USE_API_PASSWORD";
|
||||
|
||||
// The password to log in with
|
||||
string password = 1;
|
||||
string password = 1 [(pointer_to_buffer) = true];
|
||||
}
|
||||
|
||||
// Confirmation of successful connection. After this the connection is available for all traffic.
|
||||
@@ -477,7 +477,7 @@ message FanCommandRequest {
|
||||
bool has_speed_level = 10;
|
||||
int32 speed_level = 11;
|
||||
bool has_preset_mode = 12;
|
||||
string preset_mode = 13;
|
||||
string preset_mode = 13 [(pointer_to_buffer) = true];
|
||||
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
|
||||
}
|
||||
|
||||
@@ -579,7 +579,7 @@ message LightCommandRequest {
|
||||
bool has_flash_length = 16;
|
||||
uint32 flash_length = 17;
|
||||
bool has_effect = 18;
|
||||
string effect = 19;
|
||||
string effect = 19 [(pointer_to_buffer) = true];
|
||||
uint32 device_id = 28 [(field_ifdef) = "USE_DEVICES"];
|
||||
}
|
||||
|
||||
@@ -747,7 +747,7 @@ message NoiseEncryptionSetKeyRequest {
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_API_NOISE";
|
||||
|
||||
bytes key = 1;
|
||||
bytes key = 1 [(pointer_to_buffer) = true];
|
||||
}
|
||||
|
||||
message NoiseEncryptionSetKeyResponse {
|
||||
@@ -796,7 +796,7 @@ message HomeassistantActionResponse {
|
||||
uint32 call_id = 1; // Matches the call_id from HomeassistantActionRequest
|
||||
bool success = 2; // Whether the service call succeeded
|
||||
string error_message = 3; // Error message if success = false
|
||||
bytes response_data = 4 [(field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON"];
|
||||
bytes response_data = 4 [(pointer_to_buffer) = true, (field_ifdef) = "USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON"];
|
||||
}
|
||||
|
||||
// ==================== IMPORT HOME ASSISTANT STATES ====================
|
||||
@@ -824,9 +824,9 @@ message HomeAssistantStateResponse {
|
||||
option (no_delay) = true;
|
||||
option (ifdef) = "USE_API_HOMEASSISTANT_STATES";
|
||||
|
||||
string entity_id = 1;
|
||||
string state = 2;
|
||||
string attribute = 3;
|
||||
string entity_id = 1 [(pointer_to_buffer) = true];
|
||||
string state = 2 [(pointer_to_buffer) = true];
|
||||
string attribute = 3 [(pointer_to_buffer) = true];
|
||||
}
|
||||
|
||||
// ==================== IMPORT TIME ====================
|
||||
@@ -841,7 +841,7 @@ message GetTimeResponse {
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 epoch_seconds = 1;
|
||||
string timezone = 2;
|
||||
string timezone = 2 [(pointer_to_buffer) = true];
|
||||
}
|
||||
|
||||
// ==================== USER-DEFINES SERVICES ====================
|
||||
@@ -1091,11 +1091,11 @@ message ClimateCommandRequest {
|
||||
bool has_swing_mode = 14;
|
||||
ClimateSwingMode swing_mode = 15;
|
||||
bool has_custom_fan_mode = 16;
|
||||
string custom_fan_mode = 17;
|
||||
string custom_fan_mode = 17 [(pointer_to_buffer) = true];
|
||||
bool has_preset = 18;
|
||||
ClimatePreset preset = 19;
|
||||
bool has_custom_preset = 20;
|
||||
string custom_preset = 21;
|
||||
string custom_preset = 21 [(pointer_to_buffer) = true];
|
||||
bool has_target_humidity = 22;
|
||||
float target_humidity = 23;
|
||||
uint32 device_id = 24 [(field_ifdef) = "USE_DEVICES"];
|
||||
@@ -1274,7 +1274,7 @@ message SelectCommandRequest {
|
||||
option (base_class) = "CommandProtoMessage";
|
||||
|
||||
fixed32 key = 1;
|
||||
string state = 2;
|
||||
string state = 2 [(pointer_to_buffer) = true];
|
||||
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
|
||||
}
|
||||
|
||||
@@ -1292,7 +1292,7 @@ message ListEntitiesSirenResponse {
|
||||
|
||||
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
|
||||
bool disabled_by_default = 6;
|
||||
repeated string tones = 7 [(container_pointer_no_template) = "FixedVector<const char *>"];
|
||||
repeated string tones = 7;
|
||||
bool supports_duration = 8;
|
||||
bool supports_volume = 9;
|
||||
EntityCategory entity_category = 10;
|
||||
@@ -1692,7 +1692,7 @@ message BluetoothGATTWriteRequest {
|
||||
uint32 handle = 2;
|
||||
bool response = 3;
|
||||
|
||||
bytes data = 4;
|
||||
bytes data = 4 [(pointer_to_buffer) = true];
|
||||
}
|
||||
|
||||
message BluetoothGATTReadDescriptorRequest {
|
||||
@@ -1712,7 +1712,7 @@ message BluetoothGATTWriteDescriptorRequest {
|
||||
uint64 address = 1;
|
||||
uint32 handle = 2;
|
||||
|
||||
bytes data = 3;
|
||||
bytes data = 3 [(pointer_to_buffer) = true];
|
||||
}
|
||||
|
||||
message BluetoothGATTNotifyRequest {
|
||||
@@ -1937,7 +1937,7 @@ message VoiceAssistantAudio {
|
||||
option (source) = SOURCE_BOTH;
|
||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||
|
||||
bytes data = 1 [(pointer_to_buffer) = true];
|
||||
bytes data = 1;
|
||||
bool end = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,14 +101,16 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
|
||||
#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
|
||||
auto &noise_ctx = parent->get_noise_ctx();
|
||||
if (noise_ctx.has_psk()) {
|
||||
this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
|
||||
this->helper_ =
|
||||
std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx, &this->client_info_)};
|
||||
} else {
|
||||
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
|
||||
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
|
||||
}
|
||||
#elif defined(USE_API_PLAINTEXT)
|
||||
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
|
||||
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
|
||||
#elif defined(USE_API_NOISE)
|
||||
this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
|
||||
this->helper_ = std::unique_ptr<APIFrameHelper>{
|
||||
new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx(), &this->client_info_)};
|
||||
#else
|
||||
#error "No frame helper defined"
|
||||
#endif
|
||||
@@ -131,8 +133,8 @@ void APIConnection::start() {
|
||||
}
|
||||
// Initialize client name with peername (IP address) until Hello message provides actual name
|
||||
char peername[socket::PEERNAME_MAX_LEN];
|
||||
size_t len = this->helper_->getpeername_to(peername);
|
||||
this->helper_->set_client_name(peername, len);
|
||||
this->helper_->getpeername_to(peername);
|
||||
this->client_info_.name = peername;
|
||||
}
|
||||
|
||||
APIConnection::~APIConnection() {
|
||||
@@ -472,7 +474,7 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
if (msg.has_direction)
|
||||
call.set_direction(static_cast<fan::FanDirection>(msg.direction));
|
||||
if (msg.has_preset_mode)
|
||||
call.set_preset_mode(msg.preset_mode.c_str(), msg.preset_mode.size());
|
||||
call.set_preset_mode(reinterpret_cast<const char *>(msg.preset_mode), msg.preset_mode_len);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
@@ -558,7 +560,7 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||
if (msg.has_flash_length)
|
||||
call.set_flash_length(msg.flash_length);
|
||||
if (msg.has_effect)
|
||||
call.set_effect(msg.effect.c_str(), msg.effect.size());
|
||||
call.set_effect(reinterpret_cast<const char *>(msg.effect), msg.effect_len);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
@@ -737,11 +739,11 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
if (msg.has_fan_mode)
|
||||
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
|
||||
if (msg.has_custom_fan_mode)
|
||||
call.set_fan_mode(msg.custom_fan_mode.c_str(), msg.custom_fan_mode.size());
|
||||
call.set_fan_mode(reinterpret_cast<const char *>(msg.custom_fan_mode), msg.custom_fan_mode_len);
|
||||
if (msg.has_preset)
|
||||
call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
|
||||
if (msg.has_custom_preset)
|
||||
call.set_preset(msg.custom_preset.c_str(), msg.custom_preset.size());
|
||||
call.set_preset(reinterpret_cast<const char *>(msg.custom_preset), msg.custom_preset_len);
|
||||
if (msg.has_swing_mode)
|
||||
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
|
||||
call.perform();
|
||||
@@ -930,7 +932,7 @@ uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *
|
||||
}
|
||||
void APIConnection::select_command(const SelectCommandRequest &msg) {
|
||||
ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
|
||||
call.set_option(msg.state.c_str(), msg.state.size());
|
||||
call.set_option(reinterpret_cast<const char *>(msg.state), msg.state_len);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
@@ -1152,8 +1154,9 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
|
||||
if (homeassistant::global_homeassistant_time != nullptr) {
|
||||
homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds);
|
||||
#ifdef USE_TIME_TIMEZONE
|
||||
if (!value.timezone.empty()) {
|
||||
homeassistant::global_homeassistant_time->set_timezone(value.timezone.c_str(), value.timezone.size());
|
||||
if (value.timezone_len > 0) {
|
||||
homeassistant::global_homeassistant_time->set_timezone(reinterpret_cast<const char *>(value.timezone),
|
||||
value.timezone_len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1505,10 +1508,8 @@ void APIConnection::complete_authentication_() {
|
||||
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
|
||||
this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("connected"));
|
||||
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
||||
char peername_buf[socket::PEERNAME_MAX_LEN];
|
||||
this->helper_->getpeername_to(peername_buf);
|
||||
this->parent_->get_client_connected_trigger()->trigger(std::string(this->helper_->get_client_name()),
|
||||
std::string(peername_buf));
|
||||
// Trigger expects std::string, get fresh peername from socket
|
||||
this->parent_->get_client_connected_trigger()->trigger(this->client_info_.name, this->helper_->getpeername());
|
||||
#endif
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
if (homeassistant::global_homeassistant_time != nullptr) {
|
||||
@@ -1523,18 +1524,17 @@ void APIConnection::complete_authentication_() {
|
||||
}
|
||||
|
||||
bool APIConnection::send_hello_response(const HelloRequest &msg) {
|
||||
// Copy client name with truncation if needed (set_client_name handles truncation)
|
||||
this->helper_->set_client_name(msg.client_info.c_str(), msg.client_info.size());
|
||||
this->client_info_.name.assign(reinterpret_cast<const char *>(msg.client_info), msg.client_info_len);
|
||||
this->client_api_version_major_ = msg.api_version_major;
|
||||
this->client_api_version_minor_ = msg.api_version_minor;
|
||||
char peername[socket::PEERNAME_MAX_LEN];
|
||||
this->helper_->getpeername_to(peername);
|
||||
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->helper_->get_client_name(),
|
||||
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.name.c_str(),
|
||||
peername, this->client_api_version_major_, this->client_api_version_minor_);
|
||||
|
||||
HelloResponse resp;
|
||||
resp.api_version_major = 1;
|
||||
resp.api_version_minor = 14;
|
||||
resp.api_version_minor = 13;
|
||||
// Send only the version string - the client only logs this for debugging and doesn't use it otherwise
|
||||
resp.set_server_info(ESPHOME_VERSION_REF);
|
||||
resp.set_name(StringRef(App.get_name()));
|
||||
@@ -1553,7 +1553,7 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) {
|
||||
bool APIConnection::send_authenticate_response(const AuthenticationRequest &msg) {
|
||||
AuthenticationResponse resp;
|
||||
// bool invalid_password = 1;
|
||||
resp.invalid_password = !this->parent_->check_password(msg.password.byte(), msg.password.size());
|
||||
resp.invalid_password = !this->parent_->check_password(msg.password, msg.password_len);
|
||||
if (!resp.invalid_password) {
|
||||
this->complete_authentication_();
|
||||
}
|
||||
@@ -1696,37 +1696,28 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
||||
// Skip if entity_id is empty (invalid message)
|
||||
if (msg.entity_id.empty()) {
|
||||
if (msg.entity_id_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &it : this->parent_->get_state_subs()) {
|
||||
// Compare entity_id: check length matches and content matches
|
||||
size_t entity_id_len = strlen(it.entity_id);
|
||||
if (entity_id_len != msg.entity_id.size() ||
|
||||
memcmp(it.entity_id, msg.entity_id.c_str(), msg.entity_id.size()) != 0) {
|
||||
if (entity_id_len != msg.entity_id_len || memcmp(it.entity_id, msg.entity_id, msg.entity_id_len) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare attribute: either both have matching attribute, or both have none
|
||||
size_t sub_attr_len = it.attribute != nullptr ? strlen(it.attribute) : 0;
|
||||
if (sub_attr_len != msg.attribute.size() ||
|
||||
(sub_attr_len > 0 && memcmp(it.attribute, msg.attribute.c_str(), sub_attr_len) != 0)) {
|
||||
if (sub_attr_len != msg.attribute_len ||
|
||||
(sub_attr_len > 0 && memcmp(it.attribute, msg.attribute, sub_attr_len) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create null-terminated state for callback (parse_number needs null-termination)
|
||||
// HA state max length is 255, so 256 byte buffer covers all cases
|
||||
char state_buf[256];
|
||||
size_t copy_len = msg.state_len;
|
||||
if (copy_len >= sizeof(state_buf)) {
|
||||
copy_len = sizeof(state_buf) - 1; // Truncate to leave space for null terminator
|
||||
}
|
||||
if (copy_len > 0) {
|
||||
memcpy(state_buf, msg.state, copy_len);
|
||||
}
|
||||
state_buf[copy_len] = '\0';
|
||||
it.callback(StringRef(state_buf, copy_len));
|
||||
// Create temporary string for callback (callback takes const std::string &)
|
||||
// Handle empty state (nullptr with len=0)
|
||||
std::string state(msg.state_len > 0 ? reinterpret_cast<const char *>(msg.state) : "", msg.state_len);
|
||||
it.callback(state);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1761,20 +1752,20 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
||||
// the action list. This ensures async actions (delays, waits) complete first.
|
||||
}
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
void APIConnection::send_execute_service_response(uint32_t call_id, bool success, StringRef error_message) {
|
||||
void APIConnection::send_execute_service_response(uint32_t call_id, bool success, const std::string &error_message) {
|
||||
ExecuteServiceResponse resp;
|
||||
resp.call_id = call_id;
|
||||
resp.success = success;
|
||||
resp.set_error_message(error_message);
|
||||
resp.set_error_message(StringRef(error_message));
|
||||
this->send_message(resp, ExecuteServiceResponse::MESSAGE_TYPE);
|
||||
}
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
void APIConnection::send_execute_service_response(uint32_t call_id, bool success, StringRef error_message,
|
||||
void APIConnection::send_execute_service_response(uint32_t call_id, bool success, const std::string &error_message,
|
||||
const uint8_t *response_data, size_t response_data_len) {
|
||||
ExecuteServiceResponse resp;
|
||||
resp.call_id = call_id;
|
||||
resp.success = success;
|
||||
resp.set_error_message(error_message);
|
||||
resp.set_error_message(StringRef(error_message));
|
||||
resp.response_data = response_data;
|
||||
resp.response_data_len = response_data_len;
|
||||
this->send_message(resp, ExecuteServiceResponse::MESSAGE_TYPE);
|
||||
@@ -2116,14 +2107,14 @@ void APIConnection::process_state_subscriptions_() {
|
||||
void APIConnection::log_client_(int level, const LogString *message) {
|
||||
char peername[socket::PEERNAME_MAX_LEN];
|
||||
this->helper_->getpeername_to(peername);
|
||||
esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT("%s (%s): %s"), this->helper_->get_client_name(), peername,
|
||||
esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT("%s (%s): %s"), this->client_info_.name.c_str(), peername,
|
||||
LOG_STR_ARG(message));
|
||||
}
|
||||
|
||||
void APIConnection::log_warning_(const LogString *message, APIError err) {
|
||||
char peername[socket::PEERNAME_MAX_LEN];
|
||||
this->helper_->getpeername_to(peername);
|
||||
ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->helper_->get_client_name(), peername, LOG_STR_ARG(message),
|
||||
ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->client_info_.name.c_str(), peername, LOG_STR_ARG(message),
|
||||
LOG_STR_ARG(api_error_to_logstr(err)), errno);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,20 +9,25 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::api {
|
||||
|
||||
// Client information structure
|
||||
struct ClientInfo {
|
||||
std::string name; // Client name from Hello message
|
||||
// Note: peername (IP address) is not stored here to save memory.
|
||||
// Use helper_->getpeername_to() or helper_->getpeername() when needed.
|
||||
};
|
||||
|
||||
// Keepalive timeout in milliseconds
|
||||
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
|
||||
// Maximum number of entities to process in a single batch during initial state/info sending
|
||||
// API 1.14+ clients compute object_id client-side, so messages are smaller and we can fit more per batch
|
||||
// TODO: Remove MAX_INITIAL_PER_BATCH_LEGACY before 2026.7.0 - all clients should support API 1.14 by then
|
||||
static constexpr size_t MAX_INITIAL_PER_BATCH_LEGACY = 24; // For clients < API 1.14 (includes object_id)
|
||||
static constexpr size_t MAX_INITIAL_PER_BATCH = 34; // For clients >= API 1.14 (no object_id)
|
||||
// This was increased from 20 to 24 after removing the unique_id field from entity info messages,
|
||||
// which reduced message sizes allowing more entities per batch without exceeding packet limits
|
||||
static constexpr size_t MAX_INITIAL_PER_BATCH = 24;
|
||||
// Maximum number of packets to process in a single batch (platform-dependent)
|
||||
// This limit exists to prevent stack overflow from the PacketInfo array in process_batch_
|
||||
// Each PacketInfo is 8 bytes, so 64 * 8 = 512 bytes, 32 * 8 = 256 bytes
|
||||
@@ -229,9 +234,9 @@ class APIConnection final : public APIServerConnection {
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
void send_execute_service_response(uint32_t call_id, bool success, StringRef error_message);
|
||||
void send_execute_service_response(uint32_t call_id, bool success, const std::string &error_message);
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
void send_execute_service_response(uint32_t call_id, bool success, StringRef error_message,
|
||||
void send_execute_service_response(uint32_t call_id, bool success, const std::string &error_message,
|
||||
const uint8_t *response_data, size_t response_data_len);
|
||||
#endif // USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
#endif // USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
@@ -285,11 +290,13 @@ class APIConnection final : public APIServerConnection {
|
||||
bool try_to_clear_buffer(bool log_out_of_space);
|
||||
bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override;
|
||||
|
||||
const char *get_name() const { return this->helper_->get_client_name(); }
|
||||
const std::string &get_name() const { return this->client_info_.name; }
|
||||
/// Get peer name (IP address) into a stack buffer - avoids heap allocation
|
||||
size_t get_peername_to(std::span<char, socket::PEERNAME_MAX_LEN> buf) const {
|
||||
return this->helper_->getpeername_to(buf);
|
||||
}
|
||||
/// Get peer name as std::string - use sparingly, allocates on heap
|
||||
std::string get_peername() const { return this->helper_->getpeername(); }
|
||||
|
||||
protected:
|
||||
// Helper function to handle authentication completion
|
||||
@@ -322,16 +329,10 @@ class APIConnection final : public APIServerConnection {
|
||||
APIConnection *conn, uint32_t remaining_size, bool is_single) {
|
||||
// Set common fields that are shared by all entity types
|
||||
msg.key = entity->get_object_id_hash();
|
||||
|
||||
// API 1.14+ clients compute object_id client-side from the entity name
|
||||
// For older clients, we must send object_id for backward compatibility
|
||||
// See: https://github.com/esphome/backlog/issues/76
|
||||
// TODO: Remove this backward compat code before 2026.7.0 - all clients should support API 1.14 by then
|
||||
// Buffer must remain in scope until encode_message_to_buffer is called
|
||||
// Get object_id with zero heap allocation
|
||||
// Static case returns direct reference, dynamic case uses buffer
|
||||
char object_id_buf[OBJECT_ID_MAX_LEN];
|
||||
if (!conn->client_supports_api_version(1, 14)) {
|
||||
msg.set_object_id(entity->get_object_id_to(object_id_buf));
|
||||
}
|
||||
msg.set_object_id(entity->get_object_id_to(object_id_buf));
|
||||
|
||||
if (entity->has_own_name()) {
|
||||
msg.set_name(entity->get_name());
|
||||
@@ -354,24 +355,16 @@ class APIConnection final : public APIServerConnection {
|
||||
inline bool check_voice_assistant_api_connection_() const;
|
||||
#endif
|
||||
|
||||
// Get the max batch size based on client API version
|
||||
// API 1.14+ clients don't receive object_id, so messages are smaller and more fit per batch
|
||||
// TODO: Remove this method before 2026.7.0 and use MAX_INITIAL_PER_BATCH directly
|
||||
size_t get_max_batch_size_() const {
|
||||
return this->client_supports_api_version(1, 14) ? MAX_INITIAL_PER_BATCH : MAX_INITIAL_PER_BATCH_LEGACY;
|
||||
}
|
||||
|
||||
// Helper method to process multiple entities from an iterator in a batch
|
||||
template<typename Iterator> void process_iterator_batch_(Iterator &iterator) {
|
||||
size_t initial_size = this->deferred_batch_.size();
|
||||
size_t max_batch = this->get_max_batch_size_();
|
||||
while (!iterator.completed() && (this->deferred_batch_.size() - initial_size) < max_batch) {
|
||||
while (!iterator.completed() && (this->deferred_batch_.size() - initial_size) < MAX_INITIAL_PER_BATCH) {
|
||||
iterator.advance();
|
||||
}
|
||||
|
||||
// If the batch is full, process it immediately
|
||||
// Note: iterator.advance() already calls schedule_batch_() via schedule_message_()
|
||||
if (this->deferred_batch_.size() >= max_batch) {
|
||||
if (this->deferred_batch_.size() >= MAX_INITIAL_PER_BATCH) {
|
||||
this->process_batch_();
|
||||
}
|
||||
}
|
||||
@@ -535,7 +528,10 @@ class APIConnection final : public APIServerConnection {
|
||||
std::unique_ptr<camera::CameraImageReader> image_reader_;
|
||||
#endif
|
||||
|
||||
// Group 3: 4-byte types
|
||||
// Group 3: Client info struct (24 bytes on 32-bit: 2 strings × 12 bytes each)
|
||||
ClientInfo client_info_;
|
||||
|
||||
// Group 4: 4-byte types
|
||||
uint32_t last_traffic_;
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
int state_subs_at_ = -1;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "api_frame_helper.h"
|
||||
#ifdef USE_API
|
||||
#include "api_connection.h" // For ClientInfo struct
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -12,34 +13,20 @@ namespace esphome::api {
|
||||
|
||||
static const char *const TAG = "api.frame_helper";
|
||||
|
||||
// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
|
||||
static constexpr size_t API_MAX_LOG_BYTES = 168;
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
#define HELPER_LOG(msg, ...) \
|
||||
do { \
|
||||
char peername__[socket::PEERNAME_MAX_LEN]; \
|
||||
this->socket_->getpeername_to(peername__); \
|
||||
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \
|
||||
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), peername__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define HELPER_LOG(msg, ...) ((void) 0)
|
||||
#endif
|
||||
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
#define LOG_PACKET_RECEIVED(buffer) \
|
||||
do { \
|
||||
char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
|
||||
ESP_LOGVV(TAG, "Received frame: %s", \
|
||||
format_hex_pretty_to(hex_buf_, (buffer).data(), \
|
||||
(buffer).size() < API_MAX_LOG_BYTES ? (buffer).size() : API_MAX_LOG_BYTES)); \
|
||||
} while (0)
|
||||
#define LOG_PACKET_SENDING(data, len) \
|
||||
do { \
|
||||
char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
|
||||
ESP_LOGVV(TAG, "Sending raw: %s", \
|
||||
format_hex_pretty_to(hex_buf_, data, (len) < API_MAX_LOG_BYTES ? (len) : API_MAX_LOG_BYTES)); \
|
||||
} while (0)
|
||||
#define LOG_PACKET_RECEIVED(buffer) ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(buffer).c_str())
|
||||
#define LOG_PACKET_SENDING(data, len) ESP_LOGVV(TAG, "Sending raw: %s", format_hex_pretty(data, len).c_str())
|
||||
#else
|
||||
#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
|
||||
#define LOG_PACKET_SENDING(data, len) ((void) 0)
|
||||
|
||||
@@ -29,10 +29,10 @@ static constexpr uint16_t MAX_MESSAGE_SIZE = 8192; // 8 KiB for ESP8266
|
||||
static constexpr uint16_t MAX_MESSAGE_SIZE = 32768; // 32 KiB for ESP32 and other platforms
|
||||
#endif
|
||||
|
||||
class ProtoWriteBuffer;
|
||||
// Forward declaration
|
||||
struct ClientInfo;
|
||||
|
||||
// Max client name length (e.g., "Home Assistant 2026.1.0.dev0" = 28 chars)
|
||||
static constexpr size_t CLIENT_INFO_NAME_MAX_LEN = 32;
|
||||
class ProtoWriteBuffer;
|
||||
|
||||
struct ReadPacketBuffer {
|
||||
const uint8_t *data; // Points directly into frame helper's rx_buf_ (valid until next read_packet call)
|
||||
@@ -82,21 +82,14 @@ const LogString *api_error_to_logstr(APIError err);
|
||||
class APIFrameHelper {
|
||||
public:
|
||||
APIFrameHelper() = default;
|
||||
explicit APIFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
|
||||
|
||||
// Get client name (null-terminated)
|
||||
const char *get_client_name() const { return this->client_name_; }
|
||||
// Set client name from buffer with length (truncates if needed)
|
||||
void set_client_name(const char *name, size_t len) {
|
||||
size_t copy_len = std::min(len, sizeof(this->client_name_) - 1);
|
||||
memcpy(this->client_name_, name, copy_len);
|
||||
this->client_name_[copy_len] = '\0';
|
||||
}
|
||||
explicit APIFrameHelper(std::unique_ptr<socket::Socket> socket, const ClientInfo *client_info)
|
||||
: socket_(std::move(socket)), client_info_(client_info) {}
|
||||
virtual ~APIFrameHelper() = default;
|
||||
virtual APIError init() = 0;
|
||||
virtual APIError loop();
|
||||
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
|
||||
bool can_write_without_blocking() { return this->state_ == State::DATA && this->tx_buf_count_ == 0; }
|
||||
std::string getpeername() { return socket_->getpeername(); }
|
||||
int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); }
|
||||
size_t getpeername_to(std::span<char, socket::PEERNAME_MAX_LEN> buf) { return socket_->getpeername_to(buf); }
|
||||
APIError close() {
|
||||
@@ -197,8 +190,9 @@ class APIFrameHelper {
|
||||
std::vector<struct iovec> reusable_iovs_;
|
||||
std::vector<uint8_t> rx_buf_;
|
||||
|
||||
// Client name buffer - stores name from Hello message or initial peername
|
||||
char client_name_[CLIENT_INFO_NAME_MAX_LEN]{};
|
||||
// Pointer to client info (4 bytes on 32-bit)
|
||||
// Note: The pointed-to ClientInfo object must outlive this APIFrameHelper instance.
|
||||
const ClientInfo *client_info_{nullptr};
|
||||
|
||||
// Group smaller types together
|
||||
uint16_t rx_buf_len_ = 0;
|
||||
|
||||
@@ -24,34 +24,20 @@ static const char *const PROLOGUE_INIT = "NoiseAPIInit";
|
||||
#endif
|
||||
static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit")
|
||||
|
||||
// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
|
||||
static constexpr size_t API_MAX_LOG_BYTES = 168;
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
#define HELPER_LOG(msg, ...) \
|
||||
do { \
|
||||
char peername__[socket::PEERNAME_MAX_LEN]; \
|
||||
this->socket_->getpeername_to(peername__); \
|
||||
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \
|
||||
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), peername__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define HELPER_LOG(msg, ...) ((void) 0)
|
||||
#endif
|
||||
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
#define LOG_PACKET_RECEIVED(buffer) \
|
||||
do { \
|
||||
char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
|
||||
ESP_LOGVV(TAG, "Received frame: %s", \
|
||||
format_hex_pretty_to(hex_buf_, (buffer).data(), \
|
||||
(buffer).size() < API_MAX_LOG_BYTES ? (buffer).size() : API_MAX_LOG_BYTES)); \
|
||||
} while (0)
|
||||
#define LOG_PACKET_SENDING(data, len) \
|
||||
do { \
|
||||
char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
|
||||
ESP_LOGVV(TAG, "Sending raw: %s", \
|
||||
format_hex_pretty_to(hex_buf_, data, (len) < API_MAX_LOG_BYTES ? (len) : API_MAX_LOG_BYTES)); \
|
||||
} while (0)
|
||||
#define LOG_PACKET_RECEIVED(buffer) ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(buffer).c_str())
|
||||
#define LOG_PACKET_SENDING(data, len) ESP_LOGVV(TAG, "Sending raw: %s", format_hex_pretty(data, len).c_str())
|
||||
#else
|
||||
#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
|
||||
#define LOG_PACKET_SENDING(data, len) ((void) 0)
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace esphome::api {
|
||||
|
||||
class APINoiseFrameHelper final : public APIFrameHelper {
|
||||
public:
|
||||
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, APINoiseContext &ctx)
|
||||
: APIFrameHelper(std::move(socket)), ctx_(ctx) {
|
||||
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, APINoiseContext &ctx, const ClientInfo *client_info)
|
||||
: APIFrameHelper(std::move(socket), client_info), ctx_(ctx) {
|
||||
// Noise header structure:
|
||||
// Pos 0: indicator (0x01)
|
||||
// Pos 1-2: encrypted payload size (16-bit big-endian)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "api_frame_helper_plaintext.h"
|
||||
#ifdef USE_API
|
||||
#ifdef USE_API_PLAINTEXT
|
||||
#include "api_connection.h" // For ClientInfo struct
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -17,34 +18,20 @@ namespace esphome::api {
|
||||
|
||||
static const char *const TAG = "api.plaintext";
|
||||
|
||||
// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
|
||||
static constexpr size_t API_MAX_LOG_BYTES = 168;
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
#define HELPER_LOG(msg, ...) \
|
||||
do { \
|
||||
char peername__[socket::PEERNAME_MAX_LEN]; \
|
||||
this->socket_->getpeername_to(peername__); \
|
||||
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \
|
||||
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), peername__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define HELPER_LOG(msg, ...) ((void) 0)
|
||||
#endif
|
||||
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
#define LOG_PACKET_RECEIVED(buffer) \
|
||||
do { \
|
||||
char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
|
||||
ESP_LOGVV(TAG, "Received frame: %s", \
|
||||
format_hex_pretty_to(hex_buf_, (buffer).data(), \
|
||||
(buffer).size() < API_MAX_LOG_BYTES ? (buffer).size() : API_MAX_LOG_BYTES)); \
|
||||
} while (0)
|
||||
#define LOG_PACKET_SENDING(data, len) \
|
||||
do { \
|
||||
char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
|
||||
ESP_LOGVV(TAG, "Sending raw: %s", \
|
||||
format_hex_pretty_to(hex_buf_, data, (len) < API_MAX_LOG_BYTES ? (len) : API_MAX_LOG_BYTES)); \
|
||||
} while (0)
|
||||
#define LOG_PACKET_RECEIVED(buffer) ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(buffer).c_str())
|
||||
#define LOG_PACKET_SENDING(data, len) ESP_LOGVV(TAG, "Sending raw: %s", format_hex_pretty(data, len).c_str())
|
||||
#else
|
||||
#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
|
||||
#define LOG_PACKET_SENDING(data, len) ((void) 0)
|
||||
|
||||
@@ -7,7 +7,8 @@ namespace esphome::api {
|
||||
|
||||
class APIPlaintextFrameHelper final : public APIFrameHelper {
|
||||
public:
|
||||
explicit APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : APIFrameHelper(std::move(socket)) {
|
||||
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket, const ClientInfo *client_info)
|
||||
: APIFrameHelper(std::move(socket), client_info) {
|
||||
// Plaintext header structure (worst case):
|
||||
// Pos 0: indicator (0x00)
|
||||
// Pos 1-3: payload size varint (up to 3 bytes)
|
||||
|
||||
@@ -23,7 +23,9 @@ bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->client_info = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->client_info = value.data();
|
||||
this->client_info_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -47,7 +49,9 @@ void HelloResponse::calculate_size(ProtoSize &size) const {
|
||||
bool AuthenticationRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->password = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->password = value.data();
|
||||
this->password_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -444,7 +448,9 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
bool FanCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 13: {
|
||||
this->preset_mode = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->preset_mode = value.data();
|
||||
this->preset_mode_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -609,7 +615,9 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
bool LightCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 19: {
|
||||
this->effect = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->effect = value.data();
|
||||
this->effect_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -851,6 +859,7 @@ void SubscribeLogsResponse::calculate_size(ProtoSize &size) const {
|
||||
bool NoiseEncryptionSetKeyRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
// Use raw data directly to avoid allocation
|
||||
this->key = value.data();
|
||||
this->key_len = value.size();
|
||||
break;
|
||||
@@ -927,12 +936,12 @@ bool HomeassistantActionResponse::decode_varint(uint32_t field_id, ProtoVarInt v
|
||||
}
|
||||
bool HomeassistantActionResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
this->error_message = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 3:
|
||||
this->error_message = value.as_string();
|
||||
break;
|
||||
}
|
||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
case 4: {
|
||||
// Use raw data directly to avoid allocation
|
||||
this->response_data = value.data();
|
||||
this->response_data_len = value.size();
|
||||
break;
|
||||
@@ -958,15 +967,21 @@ void SubscribeHomeAssistantStateResponse::calculate_size(ProtoSize &size) const
|
||||
bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->entity_id = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->entity_id = value.data();
|
||||
this->entity_id_len = value.size();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
this->state = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->state = value.data();
|
||||
this->state_len = value.size();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
this->attribute = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->attribute = value.data();
|
||||
this->attribute_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -978,7 +993,9 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
|
||||
bool GetTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->timezone = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->timezone = value.data();
|
||||
this->timezone_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1043,10 +1060,9 @@ bool ExecuteServiceArgument::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
}
|
||||
bool ExecuteServiceArgument::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 4: {
|
||||
this->string_ = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 4:
|
||||
this->string_ = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 9:
|
||||
this->string_array.push_back(value.as_string());
|
||||
break;
|
||||
@@ -1137,7 +1153,7 @@ void ExecuteServiceResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_bool(1, this->success);
|
||||
size.add_length(1, this->error_message_ref_.size());
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
size.add_length(1, this->response_data_len);
|
||||
size.add_length(4, this->response_data_len);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -1392,11 +1408,15 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
bool ClimateCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 17: {
|
||||
this->custom_fan_mode = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->custom_fan_mode = value.data();
|
||||
this->custom_fan_mode_len = value.size();
|
||||
break;
|
||||
}
|
||||
case 21: {
|
||||
this->custom_preset = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->custom_preset = value.data();
|
||||
this->custom_preset_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1682,7 +1702,9 @@ bool SelectCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
bool SelectCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->state = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
// Use raw data directly to avoid allocation
|
||||
this->state = value.data();
|
||||
this->state_len = value.size();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1710,8 +1732,8 @@ void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(5, this->icon_ref_);
|
||||
#endif
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
for (const char *it : *this->tones) {
|
||||
buffer.encode_string(7, it, strlen(it), true);
|
||||
for (auto &it : this->tones) {
|
||||
buffer.encode_string(7, it, true);
|
||||
}
|
||||
buffer.encode_bool(8, this->supports_duration);
|
||||
buffer.encode_bool(9, this->supports_volume);
|
||||
@@ -1728,9 +1750,9 @@ void ListEntitiesSirenResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_length(1, this->icon_ref_.size());
|
||||
#endif
|
||||
size.add_bool(1, this->disabled_by_default);
|
||||
if (!this->tones->empty()) {
|
||||
for (const char *it : *this->tones) {
|
||||
size.add_length_force(1, strlen(it));
|
||||
if (!this->tones.empty()) {
|
||||
for (const auto &it : this->tones) {
|
||||
size.add_length_force(1, it.size());
|
||||
}
|
||||
}
|
||||
size.add_bool(1, this->supports_duration);
|
||||
@@ -1786,10 +1808,9 @@ bool SirenCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
bool SirenCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 5: {
|
||||
this->tone = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 5:
|
||||
this->tone = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1878,10 +1899,9 @@ bool LockCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
bool LockCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 4: {
|
||||
this->code = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 4:
|
||||
this->code = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2049,10 +2069,9 @@ bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt val
|
||||
}
|
||||
bool MediaPlayerCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 7: {
|
||||
this->media_url = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 7:
|
||||
this->media_url = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2260,6 +2279,7 @@ bool BluetoothGATTWriteRequest::decode_varint(uint32_t field_id, ProtoVarInt val
|
||||
bool BluetoothGATTWriteRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 4: {
|
||||
// Use raw data directly to avoid allocation
|
||||
this->data = value.data();
|
||||
this->data_len = value.size();
|
||||
break;
|
||||
@@ -2298,6 +2318,7 @@ bool BluetoothGATTWriteDescriptorRequest::decode_varint(uint32_t field_id, Proto
|
||||
bool BluetoothGATTWriteDescriptorRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
// Use raw data directly to avoid allocation
|
||||
this->data = value.data();
|
||||
this->data_len = value.size();
|
||||
break;
|
||||
@@ -2481,14 +2502,12 @@ bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
}
|
||||
bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->name = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 1:
|
||||
this->name = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
this->value = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 2:
|
||||
this->value = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2527,22 +2546,20 @@ bool VoiceAssistantAudio::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->data = value.data();
|
||||
this->data_len = value.size();
|
||||
case 1:
|
||||
this->data = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bytes(1, this->data, this->data_len);
|
||||
buffer.encode_bytes(1, this->data_ptr_, this->data_len_);
|
||||
buffer.encode_bool(2, this->end);
|
||||
}
|
||||
void VoiceAssistantAudio::calculate_size(ProtoSize &size) const {
|
||||
size.add_length(1, this->data_len);
|
||||
size.add_length(1, this->data_len_);
|
||||
size.add_bool(1, this->end);
|
||||
}
|
||||
bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
@@ -2566,14 +2583,12 @@ bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVar
|
||||
}
|
||||
bool VoiceAssistantTimerEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->timer_id = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 2:
|
||||
this->timer_id = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
this->name = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 3:
|
||||
this->name = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2591,18 +2606,15 @@ bool VoiceAssistantAnnounceRequest::decode_varint(uint32_t field_id, ProtoVarInt
|
||||
}
|
||||
bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->media_id = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 1:
|
||||
this->media_id = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
this->text = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 2:
|
||||
this->text = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
this->preannounce_media_id = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 3:
|
||||
this->preannounce_media_id = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2638,29 +2650,24 @@ bool VoiceAssistantExternalWakeWord::decode_varint(uint32_t field_id, ProtoVarIn
|
||||
}
|
||||
bool VoiceAssistantExternalWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->id = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 1:
|
||||
this->id = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
this->wake_word = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 2:
|
||||
this->wake_word = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
this->trained_languages.push_back(value.as_string());
|
||||
break;
|
||||
case 4: {
|
||||
this->model_type = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 4:
|
||||
this->model_type = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
this->model_hash = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 6:
|
||||
this->model_hash = value.as_string();
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
this->url = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 7:
|
||||
this->url = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2770,10 +2777,9 @@ bool AlarmControlPanelCommandRequest::decode_varint(uint32_t field_id, ProtoVarI
|
||||
}
|
||||
bool AlarmControlPanelCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
this->code = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 3:
|
||||
this->code = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2855,10 +2861,9 @@ bool TextCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
bool TextCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->state = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
|
||||
case 2:
|
||||
this->state = value.as_string();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -3326,6 +3331,7 @@ bool UpdateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
bool ZWaveProxyFrame::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
// Use raw data directly to avoid allocation
|
||||
this->data = value.data();
|
||||
this->data_len = value.size();
|
||||
break;
|
||||
@@ -3350,6 +3356,7 @@ bool ZWaveProxyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
bool ZWaveProxyRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
// Use raw data directly to avoid allocation
|
||||
this->data = value.data();
|
||||
this->data_len = value.size();
|
||||
break;
|
||||
@@ -3365,7 +3372,7 @@ void ZWaveProxyRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
}
|
||||
void ZWaveProxyRequest::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, static_cast<uint32_t>(this->type));
|
||||
size.add_length(1, this->data_len);
|
||||
size.add_length(2, this->data_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -357,11 +357,12 @@ class CommandProtoMessage : public ProtoDecodableMessage {
|
||||
class HelloRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 1;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 17;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 27;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "hello_request"; }
|
||||
#endif
|
||||
StringRef client_info{};
|
||||
const uint8_t *client_info{nullptr};
|
||||
uint16_t client_info_len{0};
|
||||
uint32_t api_version_major{0};
|
||||
uint32_t api_version_minor{0};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
@@ -397,11 +398,12 @@ class HelloResponse final : public ProtoMessage {
|
||||
class AuthenticationRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 3;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 9;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 19;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "authentication_request"; }
|
||||
#endif
|
||||
StringRef password{};
|
||||
const uint8_t *password{nullptr};
|
||||
uint16_t password_len{0};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -782,7 +784,7 @@ class FanStateResponse final : public StateResponseProtoMessage {
|
||||
class FanCommandRequest final : public CommandProtoMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 31;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 38;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 48;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "fan_command_request"; }
|
||||
#endif
|
||||
@@ -795,7 +797,8 @@ class FanCommandRequest final : public CommandProtoMessage {
|
||||
bool has_speed_level{false};
|
||||
int32_t speed_level{0};
|
||||
bool has_preset_mode{false};
|
||||
StringRef preset_mode{};
|
||||
const uint8_t *preset_mode{nullptr};
|
||||
uint16_t preset_mode_len{0};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -857,7 +860,7 @@ class LightStateResponse final : public StateResponseProtoMessage {
|
||||
class LightCommandRequest final : public CommandProtoMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 32;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 112;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 122;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "light_command_request"; }
|
||||
#endif
|
||||
@@ -886,7 +889,8 @@ class LightCommandRequest final : public CommandProtoMessage {
|
||||
bool has_flash_length{false};
|
||||
uint32_t flash_length{0};
|
||||
bool has_effect{false};
|
||||
StringRef effect{};
|
||||
const uint8_t *effect{nullptr};
|
||||
uint16_t effect_len{0};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -1069,7 +1073,7 @@ class SubscribeLogsResponse final : public ProtoMessage {
|
||||
class NoiseEncryptionSetKeyRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 124;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 9;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 19;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "noise_encryption_set_key_request"; }
|
||||
#endif
|
||||
@@ -1161,13 +1165,13 @@ class HomeassistantActionRequest final : public ProtoMessage {
|
||||
class HomeassistantActionResponse final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 130;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 24;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 34;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "homeassistant_action_response"; }
|
||||
#endif
|
||||
uint32_t call_id{0};
|
||||
bool success{false};
|
||||
StringRef error_message{};
|
||||
std::string error_message{};
|
||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
const uint8_t *response_data{nullptr};
|
||||
uint16_t response_data_len{0};
|
||||
@@ -1218,13 +1222,16 @@ class SubscribeHomeAssistantStateResponse final : public ProtoMessage {
|
||||
class HomeAssistantStateResponse final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 40;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 27;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 57;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "home_assistant_state_response"; }
|
||||
#endif
|
||||
StringRef entity_id{};
|
||||
StringRef state{};
|
||||
StringRef attribute{};
|
||||
const uint8_t *entity_id{nullptr};
|
||||
uint16_t entity_id_len{0};
|
||||
const uint8_t *state{nullptr};
|
||||
uint16_t state_len{0};
|
||||
const uint8_t *attribute{nullptr};
|
||||
uint16_t attribute_len{0};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -1249,12 +1256,13 @@ class GetTimeRequest final : public ProtoMessage {
|
||||
class GetTimeResponse final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 37;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 14;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 24;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "get_time_response"; }
|
||||
#endif
|
||||
uint32_t epoch_seconds{0};
|
||||
StringRef timezone{};
|
||||
const uint8_t *timezone{nullptr};
|
||||
uint16_t timezone_len{0};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -1302,7 +1310,7 @@ class ExecuteServiceArgument final : public ProtoDecodableMessage {
|
||||
bool bool_{false};
|
||||
int32_t legacy_int{0};
|
||||
float float_{0.0f};
|
||||
StringRef string_{};
|
||||
std::string string_{};
|
||||
int32_t int_{0};
|
||||
FixedVector<bool> bool_array{};
|
||||
FixedVector<int32_t> int_array{};
|
||||
@@ -1491,7 +1499,7 @@ class ClimateStateResponse final : public StateResponseProtoMessage {
|
||||
class ClimateCommandRequest final : public CommandProtoMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 48;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 84;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 104;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "climate_command_request"; }
|
||||
#endif
|
||||
@@ -1508,11 +1516,13 @@ class ClimateCommandRequest final : public CommandProtoMessage {
|
||||
bool has_swing_mode{false};
|
||||
enums::ClimateSwingMode swing_mode{};
|
||||
bool has_custom_fan_mode{false};
|
||||
StringRef custom_fan_mode{};
|
||||
const uint8_t *custom_fan_mode{nullptr};
|
||||
uint16_t custom_fan_mode_len{0};
|
||||
bool has_preset{false};
|
||||
enums::ClimatePreset preset{};
|
||||
bool has_custom_preset{false};
|
||||
StringRef custom_preset{};
|
||||
const uint8_t *custom_preset{nullptr};
|
||||
uint16_t custom_preset_len{0};
|
||||
bool has_target_humidity{false};
|
||||
float target_humidity{0.0f};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
@@ -1685,11 +1695,12 @@ class SelectStateResponse final : public StateResponseProtoMessage {
|
||||
class SelectCommandRequest final : public CommandProtoMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 54;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 18;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 28;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "select_command_request"; }
|
||||
#endif
|
||||
StringRef state{};
|
||||
const uint8_t *state{nullptr};
|
||||
uint16_t state_len{0};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -1708,7 +1719,7 @@ class ListEntitiesSirenResponse final : public InfoResponseProtoMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "list_entities_siren_response"; }
|
||||
#endif
|
||||
const FixedVector<const char *> *tones{};
|
||||
std::vector<std::string> tones{};
|
||||
bool supports_duration{false};
|
||||
bool supports_volume{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
@@ -1745,7 +1756,7 @@ class SirenCommandRequest final : public CommandProtoMessage {
|
||||
bool has_state{false};
|
||||
bool state{false};
|
||||
bool has_tone{false};
|
||||
StringRef tone{};
|
||||
std::string tone{};
|
||||
bool has_duration{false};
|
||||
uint32_t duration{0};
|
||||
bool has_volume{false};
|
||||
@@ -1806,7 +1817,7 @@ class LockCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
enums::LockCommand command{};
|
||||
bool has_code{false};
|
||||
StringRef code{};
|
||||
std::string code{};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -1916,7 +1927,7 @@ class MediaPlayerCommandRequest final : public CommandProtoMessage {
|
||||
bool has_volume{false};
|
||||
float volume{0.0f};
|
||||
bool has_media_url{false};
|
||||
StringRef media_url{};
|
||||
std::string media_url{};
|
||||
bool has_announcement{false};
|
||||
bool announcement{false};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
@@ -2146,7 +2157,7 @@ class BluetoothGATTReadResponse final : public ProtoMessage {
|
||||
class BluetoothGATTWriteRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 75;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 19;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 29;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "bluetooth_gatt_write_request"; }
|
||||
#endif
|
||||
@@ -2182,7 +2193,7 @@ class BluetoothGATTReadDescriptorRequest final : public ProtoDecodableMessage {
|
||||
class BluetoothGATTWriteDescriptorRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 77;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 17;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 27;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "bluetooth_gatt_write_descriptor_request"; }
|
||||
#endif
|
||||
@@ -2492,8 +2503,8 @@ class VoiceAssistantResponse final : public ProtoDecodableMessage {
|
||||
};
|
||||
class VoiceAssistantEventData final : public ProtoDecodableMessage {
|
||||
public:
|
||||
StringRef name{};
|
||||
StringRef value{};
|
||||
std::string name{};
|
||||
std::string value{};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -2521,12 +2532,17 @@ class VoiceAssistantEventResponse final : public ProtoDecodableMessage {
|
||||
class VoiceAssistantAudio final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 106;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 21;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 11;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "voice_assistant_audio"; }
|
||||
#endif
|
||||
const uint8_t *data{nullptr};
|
||||
uint16_t data_len{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;
|
||||
}
|
||||
bool end{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
@@ -2546,8 +2562,8 @@ class VoiceAssistantTimerEventResponse final : public ProtoDecodableMessage {
|
||||
const char *message_name() const override { return "voice_assistant_timer_event_response"; }
|
||||
#endif
|
||||
enums::VoiceAssistantTimerEvent event_type{};
|
||||
StringRef timer_id{};
|
||||
StringRef name{};
|
||||
std::string timer_id{};
|
||||
std::string name{};
|
||||
uint32_t total_seconds{0};
|
||||
uint32_t seconds_left{0};
|
||||
bool is_active{false};
|
||||
@@ -2566,9 +2582,9 @@ class VoiceAssistantAnnounceRequest final : public ProtoDecodableMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "voice_assistant_announce_request"; }
|
||||
#endif
|
||||
StringRef media_id{};
|
||||
StringRef text{};
|
||||
StringRef preannounce_media_id{};
|
||||
std::string media_id{};
|
||||
std::string text{};
|
||||
std::string preannounce_media_id{};
|
||||
bool start_conversation{false};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
@@ -2611,13 +2627,13 @@ class VoiceAssistantWakeWord final : public ProtoMessage {
|
||||
};
|
||||
class VoiceAssistantExternalWakeWord final : public ProtoDecodableMessage {
|
||||
public:
|
||||
StringRef id{};
|
||||
StringRef wake_word{};
|
||||
std::string id{};
|
||||
std::string wake_word{};
|
||||
std::vector<std::string> trained_languages{};
|
||||
StringRef model_type{};
|
||||
std::string model_type{};
|
||||
uint32_t model_size{0};
|
||||
StringRef model_hash{};
|
||||
StringRef url{};
|
||||
std::string model_hash{};
|
||||
std::string url{};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -2718,7 +2734,7 @@ class AlarmControlPanelCommandRequest final : public CommandProtoMessage {
|
||||
const char *message_name() const override { return "alarm_control_panel_command_request"; }
|
||||
#endif
|
||||
enums::AlarmControlPanelStateCommand command{};
|
||||
StringRef code{};
|
||||
std::string code{};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
@@ -2775,7 +2791,7 @@ class TextCommandRequest final : public CommandProtoMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "text_command_request"; }
|
||||
#endif
|
||||
StringRef state{};
|
||||
std::string state{};
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
@@ -736,7 +736,7 @@ template<> const char *proto_enum_to_string<enums::ZWaveProxyRequestType>(enums:
|
||||
void HelloRequest::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "HelloRequest");
|
||||
out.append(" client_info: ");
|
||||
out.append("'").append(this->client_info.c_str(), this->client_info.size()).append("'");
|
||||
out.append(format_hex_pretty(this->client_info, this->client_info_len));
|
||||
out.append("\n");
|
||||
dump_field(out, "api_version_major", this->api_version_major);
|
||||
dump_field(out, "api_version_minor", this->api_version_minor);
|
||||
@@ -752,7 +752,7 @@ void HelloResponse::dump_to(std::string &out) const {
|
||||
void AuthenticationRequest::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "AuthenticationRequest");
|
||||
out.append(" password: ");
|
||||
out.append("'").append(this->password.c_str(), this->password.size()).append("'");
|
||||
out.append(format_hex_pretty(this->password, this->password_len));
|
||||
out.append("\n");
|
||||
}
|
||||
void AuthenticationResponse::dump_to(std::string &out) const {
|
||||
@@ -965,7 +965,7 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
||||
dump_field(out, "speed_level", this->speed_level);
|
||||
dump_field(out, "has_preset_mode", this->has_preset_mode);
|
||||
out.append(" preset_mode: ");
|
||||
out.append("'").append(this->preset_mode.c_str(), this->preset_mode.size()).append("'");
|
||||
out.append(format_hex_pretty(this->preset_mode, this->preset_mode_len));
|
||||
out.append("\n");
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, "device_id", this->device_id);
|
||||
@@ -1043,7 +1043,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||
dump_field(out, "flash_length", this->flash_length);
|
||||
dump_field(out, "has_effect", this->has_effect);
|
||||
out.append(" effect: ");
|
||||
out.append("'").append(this->effect.c_str(), this->effect.size()).append("'");
|
||||
out.append(format_hex_pretty(this->effect, this->effect_len));
|
||||
out.append("\n");
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, "device_id", this->device_id);
|
||||
@@ -1205,9 +1205,7 @@ void HomeassistantActionResponse::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "HomeassistantActionResponse");
|
||||
dump_field(out, "call_id", this->call_id);
|
||||
dump_field(out, "success", this->success);
|
||||
out.append(" error_message: ");
|
||||
out.append("'").append(this->error_message.c_str(), this->error_message.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "error_message", this->error_message);
|
||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
out.append(" response_data: ");
|
||||
out.append(format_hex_pretty(this->response_data, this->response_data_len));
|
||||
@@ -1228,13 +1226,13 @@ void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
|
||||
void HomeAssistantStateResponse::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "HomeAssistantStateResponse");
|
||||
out.append(" entity_id: ");
|
||||
out.append("'").append(this->entity_id.c_str(), this->entity_id.size()).append("'");
|
||||
out.append(format_hex_pretty(this->entity_id, this->entity_id_len));
|
||||
out.append("\n");
|
||||
out.append(" state: ");
|
||||
out.append("'").append(this->state.c_str(), this->state.size()).append("'");
|
||||
out.append(format_hex_pretty(this->state, this->state_len));
|
||||
out.append("\n");
|
||||
out.append(" attribute: ");
|
||||
out.append("'").append(this->attribute.c_str(), this->attribute.size()).append("'");
|
||||
out.append(format_hex_pretty(this->attribute, this->attribute_len));
|
||||
out.append("\n");
|
||||
}
|
||||
#endif
|
||||
@@ -1243,7 +1241,7 @@ void GetTimeResponse::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "GetTimeResponse");
|
||||
dump_field(out, "epoch_seconds", this->epoch_seconds);
|
||||
out.append(" timezone: ");
|
||||
out.append("'").append(this->timezone.c_str(), this->timezone.size()).append("'");
|
||||
out.append(format_hex_pretty(this->timezone, this->timezone_len));
|
||||
out.append("\n");
|
||||
}
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
@@ -1268,9 +1266,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
|
||||
dump_field(out, "bool_", this->bool_);
|
||||
dump_field(out, "legacy_int", this->legacy_int);
|
||||
dump_field(out, "float_", this->float_);
|
||||
out.append(" string_: ");
|
||||
out.append("'").append(this->string_.c_str(), this->string_.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "string_", this->string_);
|
||||
dump_field(out, "int_", this->int_);
|
||||
for (const auto it : this->bool_array) {
|
||||
dump_field(out, "bool_array", static_cast<bool>(it), 4);
|
||||
@@ -1428,13 +1424,13 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||
dump_field(out, "swing_mode", static_cast<enums::ClimateSwingMode>(this->swing_mode));
|
||||
dump_field(out, "has_custom_fan_mode", this->has_custom_fan_mode);
|
||||
out.append(" custom_fan_mode: ");
|
||||
out.append("'").append(this->custom_fan_mode.c_str(), this->custom_fan_mode.size()).append("'");
|
||||
out.append(format_hex_pretty(this->custom_fan_mode, this->custom_fan_mode_len));
|
||||
out.append("\n");
|
||||
dump_field(out, "has_preset", this->has_preset);
|
||||
dump_field(out, "preset", static_cast<enums::ClimatePreset>(this->preset));
|
||||
dump_field(out, "has_custom_preset", this->has_custom_preset);
|
||||
out.append(" custom_preset: ");
|
||||
out.append("'").append(this->custom_preset.c_str(), this->custom_preset.size()).append("'");
|
||||
out.append(format_hex_pretty(this->custom_preset, this->custom_preset_len));
|
||||
out.append("\n");
|
||||
dump_field(out, "has_target_humidity", this->has_target_humidity);
|
||||
dump_field(out, "target_humidity", this->target_humidity);
|
||||
@@ -1562,7 +1558,7 @@ void SelectCommandRequest::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "SelectCommandRequest");
|
||||
dump_field(out, "key", this->key);
|
||||
out.append(" state: ");
|
||||
out.append("'").append(this->state.c_str(), this->state.size()).append("'");
|
||||
out.append(format_hex_pretty(this->state, this->state_len));
|
||||
out.append("\n");
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, "device_id", this->device_id);
|
||||
@@ -1579,7 +1575,7 @@ void ListEntitiesSirenResponse::dump_to(std::string &out) const {
|
||||
dump_field(out, "icon", this->icon_ref_);
|
||||
#endif
|
||||
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
||||
for (const auto &it : *this->tones) {
|
||||
for (const auto &it : this->tones) {
|
||||
dump_field(out, "tones", it, 4);
|
||||
}
|
||||
dump_field(out, "supports_duration", this->supports_duration);
|
||||
@@ -1603,9 +1599,7 @@ void SirenCommandRequest::dump_to(std::string &out) const {
|
||||
dump_field(out, "has_state", this->has_state);
|
||||
dump_field(out, "state", this->state);
|
||||
dump_field(out, "has_tone", this->has_tone);
|
||||
out.append(" tone: ");
|
||||
out.append("'").append(this->tone.c_str(), this->tone.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "tone", this->tone);
|
||||
dump_field(out, "has_duration", this->has_duration);
|
||||
dump_field(out, "duration", this->duration);
|
||||
dump_field(out, "has_volume", this->has_volume);
|
||||
@@ -1647,9 +1641,7 @@ void LockCommandRequest::dump_to(std::string &out) const {
|
||||
dump_field(out, "key", this->key);
|
||||
dump_field(out, "command", static_cast<enums::LockCommand>(this->command));
|
||||
dump_field(out, "has_code", this->has_code);
|
||||
out.append(" code: ");
|
||||
out.append("'").append(this->code.c_str(), this->code.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "code", this->code);
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, "device_id", this->device_id);
|
||||
#endif
|
||||
@@ -1727,9 +1719,7 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
|
||||
dump_field(out, "has_volume", this->has_volume);
|
||||
dump_field(out, "volume", this->volume);
|
||||
dump_field(out, "has_media_url", this->has_media_url);
|
||||
out.append(" media_url: ");
|
||||
out.append("'").append(this->media_url.c_str(), this->media_url.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "media_url", this->media_url);
|
||||
dump_field(out, "has_announcement", this->has_announcement);
|
||||
dump_field(out, "announcement", this->announcement);
|
||||
#ifdef USE_DEVICES
|
||||
@@ -1959,12 +1949,8 @@ void VoiceAssistantResponse::dump_to(std::string &out) const {
|
||||
}
|
||||
void VoiceAssistantEventData::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "VoiceAssistantEventData");
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name.c_str(), this->name.size()).append("'");
|
||||
out.append("\n");
|
||||
out.append(" value: ");
|
||||
out.append("'").append(this->value.c_str(), this->value.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "name", this->name);
|
||||
dump_field(out, "value", this->value);
|
||||
}
|
||||
void VoiceAssistantEventResponse::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "VoiceAssistantEventResponse");
|
||||
@@ -1978,34 +1964,28 @@ void VoiceAssistantEventResponse::dump_to(std::string &out) const {
|
||||
void VoiceAssistantAudio::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "VoiceAssistantAudio");
|
||||
out.append(" data: ");
|
||||
out.append(format_hex_pretty(this->data, this->data_len));
|
||||
if (this->data_ptr_ != nullptr) {
|
||||
out.append(format_hex_pretty(this->data_ptr_, this->data_len_));
|
||||
} else {
|
||||
out.append(format_hex_pretty(reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size()));
|
||||
}
|
||||
out.append("\n");
|
||||
dump_field(out, "end", this->end);
|
||||
}
|
||||
void VoiceAssistantTimerEventResponse::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "VoiceAssistantTimerEventResponse");
|
||||
dump_field(out, "event_type", static_cast<enums::VoiceAssistantTimerEvent>(this->event_type));
|
||||
out.append(" timer_id: ");
|
||||
out.append("'").append(this->timer_id.c_str(), this->timer_id.size()).append("'");
|
||||
out.append("\n");
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name.c_str(), this->name.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "timer_id", this->timer_id);
|
||||
dump_field(out, "name", this->name);
|
||||
dump_field(out, "total_seconds", this->total_seconds);
|
||||
dump_field(out, "seconds_left", this->seconds_left);
|
||||
dump_field(out, "is_active", this->is_active);
|
||||
}
|
||||
void VoiceAssistantAnnounceRequest::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "VoiceAssistantAnnounceRequest");
|
||||
out.append(" media_id: ");
|
||||
out.append("'").append(this->media_id.c_str(), this->media_id.size()).append("'");
|
||||
out.append("\n");
|
||||
out.append(" text: ");
|
||||
out.append("'").append(this->text.c_str(), this->text.size()).append("'");
|
||||
out.append("\n");
|
||||
out.append(" preannounce_media_id: ");
|
||||
out.append("'").append(this->preannounce_media_id.c_str(), this->preannounce_media_id.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "media_id", this->media_id);
|
||||
dump_field(out, "text", this->text);
|
||||
dump_field(out, "preannounce_media_id", this->preannounce_media_id);
|
||||
dump_field(out, "start_conversation", this->start_conversation);
|
||||
}
|
||||
void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { dump_field(out, "success", this->success); }
|
||||
@@ -2019,25 +1999,15 @@ void VoiceAssistantWakeWord::dump_to(std::string &out) const {
|
||||
}
|
||||
void VoiceAssistantExternalWakeWord::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "VoiceAssistantExternalWakeWord");
|
||||
out.append(" id: ");
|
||||
out.append("'").append(this->id.c_str(), this->id.size()).append("'");
|
||||
out.append("\n");
|
||||
out.append(" wake_word: ");
|
||||
out.append("'").append(this->wake_word.c_str(), this->wake_word.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "id", this->id);
|
||||
dump_field(out, "wake_word", this->wake_word);
|
||||
for (const auto &it : this->trained_languages) {
|
||||
dump_field(out, "trained_languages", it, 4);
|
||||
}
|
||||
out.append(" model_type: ");
|
||||
out.append("'").append(this->model_type.c_str(), this->model_type.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "model_type", this->model_type);
|
||||
dump_field(out, "model_size", this->model_size);
|
||||
out.append(" model_hash: ");
|
||||
out.append("'").append(this->model_hash.c_str(), this->model_hash.size()).append("'");
|
||||
out.append("\n");
|
||||
out.append(" url: ");
|
||||
out.append("'").append(this->url.c_str(), this->url.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "model_hash", this->model_hash);
|
||||
dump_field(out, "url", this->url);
|
||||
}
|
||||
void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "VoiceAssistantConfigurationRequest");
|
||||
@@ -2096,9 +2066,7 @@ void AlarmControlPanelCommandRequest::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "AlarmControlPanelCommandRequest");
|
||||
dump_field(out, "key", this->key);
|
||||
dump_field(out, "command", static_cast<enums::AlarmControlPanelStateCommand>(this->command));
|
||||
out.append(" code: ");
|
||||
out.append("'").append(this->code.c_str(), this->code.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "code", this->code);
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, "device_id", this->device_id);
|
||||
#endif
|
||||
@@ -2135,9 +2103,7 @@ void TextStateResponse::dump_to(std::string &out) const {
|
||||
void TextCommandRequest::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "TextCommandRequest");
|
||||
dump_field(out, "key", this->key);
|
||||
out.append(" state: ");
|
||||
out.append("'").append(this->state.c_str(), this->state.size()).append("'");
|
||||
out.append("\n");
|
||||
dump_field(out, "state", this->state);
|
||||
#ifdef USE_DEVICES
|
||||
dump_field(out, "device_id", this->device_id);
|
||||
#endif
|
||||
|
||||
@@ -187,14 +187,13 @@ void APIServer::loop() {
|
||||
|
||||
// Rare case: handle disconnection
|
||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||
char peername_buf[socket::PEERNAME_MAX_LEN];
|
||||
client->get_peername_to(peername_buf);
|
||||
this->client_disconnected_trigger_->trigger(std::string(client->get_name()), std::string(peername_buf));
|
||||
// Trigger expects std::string, get fresh peername from socket
|
||||
this->client_disconnected_trigger_->trigger(client->client_info_.name, client->get_peername());
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
this->unregister_active_action_calls_for_connection(client.get());
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Remove connection %s", client->get_name());
|
||||
ESP_LOGV(TAG, "Remove connection %s", client->client_info_.name.c_str());
|
||||
|
||||
// Swap with the last element and pop (avoids expensive vector shifts)
|
||||
if (client_index < this->clients_.size() - 1) {
|
||||
@@ -398,7 +397,7 @@ void APIServer::register_action_response_callback(uint32_t call_id, ActionRespon
|
||||
this->action_response_callbacks_.push_back({call_id, std::move(callback)});
|
||||
}
|
||||
|
||||
void APIServer::handle_action_response(uint32_t call_id, bool success, StringRef error_message) {
|
||||
void APIServer::handle_action_response(uint32_t call_id, bool success, const std::string &error_message) {
|
||||
for (auto it = this->action_response_callbacks_.begin(); it != this->action_response_callbacks_.end(); ++it) {
|
||||
if (it->call_id == call_id) {
|
||||
auto callback = std::move(it->callback);
|
||||
@@ -410,7 +409,7 @@ void APIServer::handle_action_response(uint32_t call_id, bool success, StringRef
|
||||
}
|
||||
}
|
||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
void APIServer::handle_action_response(uint32_t call_id, bool success, StringRef error_message,
|
||||
void APIServer::handle_action_response(uint32_t call_id, bool success, const std::string &error_message,
|
||||
const uint8_t *response_data, size_t response_data_len) {
|
||||
for (auto it = this->action_response_callbacks_.begin(); it != this->action_response_callbacks_.end(); ++it) {
|
||||
if (it->call_id == call_id) {
|
||||
@@ -428,8 +427,8 @@ void APIServer::handle_action_response(uint32_t call_id, bool success, StringRef
|
||||
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
// Helper to add subscription (reduces duplication)
|
||||
void APIServer::add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> f,
|
||||
bool once) {
|
||||
void APIServer::add_state_subscription_(const char *entity_id, const char *attribute,
|
||||
std::function<void(const std::string &)> f, bool once) {
|
||||
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
||||
.entity_id = entity_id, .attribute = attribute, .callback = std::move(f), .once = once,
|
||||
// entity_id_dynamic_storage and attribute_dynamic_storage remain nullptr (no heap allocation)
|
||||
@@ -438,7 +437,7 @@ void APIServer::add_state_subscription_(const char *entity_id, const char *attri
|
||||
|
||||
// Helper to add subscription with heap-allocated strings (reduces duplication)
|
||||
void APIServer::add_state_subscription_(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f, bool once) {
|
||||
std::function<void(const std::string &)> f, bool once) {
|
||||
HomeAssistantStateSubscription sub;
|
||||
// Allocate heap storage for the strings
|
||||
sub.entity_id_dynamic_storage = std::make_unique<std::string>(std::move(entity_id));
|
||||
@@ -458,36 +457,16 @@ void APIServer::add_state_subscription_(std::string entity_id, optional<std::str
|
||||
|
||||
// New const char* overload (for internal components - zero allocation)
|
||||
void APIServer::subscribe_home_assistant_state(const char *entity_id, const char *attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
std::function<void(const std::string &)> f) {
|
||||
this->add_state_subscription_(entity_id, attribute, std::move(f), false);
|
||||
}
|
||||
|
||||
void APIServer::get_home_assistant_state(const char *entity_id, const char *attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
std::function<void(const std::string &)> f) {
|
||||
this->add_state_subscription_(entity_id, attribute, std::move(f), true);
|
||||
}
|
||||
|
||||
// std::string overload with StringRef callback (zero-allocation callback)
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), false);
|
||||
}
|
||||
|
||||
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), true);
|
||||
}
|
||||
|
||||
// Legacy helper: wraps std::string callback and delegates to StringRef version
|
||||
void APIServer::add_state_subscription_(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f, bool once) {
|
||||
// Wrap callback to convert StringRef -> std::string, then delegate
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute),
|
||||
std::function<void(StringRef)>([f = std::move(f)](StringRef state) { f(state.str()); }),
|
||||
once);
|
||||
}
|
||||
|
||||
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string)
|
||||
// Existing std::string overload (for custom_api_device.h - heap allocation)
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), false);
|
||||
@@ -702,7 +681,7 @@ void APIServer::unregister_active_action_calls_for_connection(APIConnection *con
|
||||
}
|
||||
}
|
||||
|
||||
void APIServer::send_action_response(uint32_t action_call_id, bool success, StringRef error_message) {
|
||||
void APIServer::send_action_response(uint32_t action_call_id, bool success, const std::string &error_message) {
|
||||
for (auto &call : this->active_action_calls_) {
|
||||
if (call.action_call_id == action_call_id) {
|
||||
call.connection->send_execute_service_response(call.client_call_id, success, error_message);
|
||||
@@ -712,7 +691,7 @@ void APIServer::send_action_response(uint32_t action_call_id, bool success, Stri
|
||||
ESP_LOGW(TAG, "Cannot send response: no active call found for action_call_id %u", action_call_id);
|
||||
}
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
void APIServer::send_action_response(uint32_t action_call_id, bool success, StringRef error_message,
|
||||
void APIServer::send_action_response(uint32_t action_call_id, bool success, const std::string &error_message,
|
||||
const uint8_t *response_data, size_t response_data_len) {
|
||||
for (auto &call : this->active_action_calls_) {
|
||||
if (call.action_call_id == action_call_id) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/controller.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "list_entities.h"
|
||||
#include "subscribe_state.h"
|
||||
#ifdef USE_LOGGER
|
||||
@@ -144,10 +143,10 @@ class APIServer : public Component,
|
||||
// Action response handling
|
||||
using ActionResponseCallback = std::function<void(const class ActionResponse &)>;
|
||||
void register_action_response_callback(uint32_t call_id, ActionResponseCallback callback);
|
||||
void handle_action_response(uint32_t call_id, bool success, StringRef error_message);
|
||||
void handle_action_response(uint32_t call_id, bool success, const std::string &error_message);
|
||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
void handle_action_response(uint32_t call_id, bool success, StringRef error_message, const uint8_t *response_data,
|
||||
size_t response_data_len);
|
||||
void handle_action_response(uint32_t call_id, bool success, const std::string &error_message,
|
||||
const uint8_t *response_data, size_t response_data_len);
|
||||
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||
#endif // USE_API_HOMEASSISTANT_SERVICES
|
||||
@@ -166,9 +165,9 @@ class APIServer : public Component,
|
||||
void unregister_active_action_call(uint32_t action_call_id);
|
||||
void unregister_active_action_calls_for_connection(APIConnection *conn);
|
||||
// Send response for a specific action call (uses action_call_id, sends client_call_id in response)
|
||||
void send_action_response(uint32_t action_call_id, bool success, StringRef error_message);
|
||||
void send_action_response(uint32_t action_call_id, bool success, const std::string &error_message);
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
void send_action_response(uint32_t action_call_id, bool success, StringRef error_message,
|
||||
void send_action_response(uint32_t action_call_id, bool success, const std::string &error_message,
|
||||
const uint8_t *response_data, size_t response_data_len);
|
||||
#endif // USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
#endif // USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
@@ -196,7 +195,7 @@ class APIServer : public Component,
|
||||
struct HomeAssistantStateSubscription {
|
||||
const char *entity_id; // Pointer to flash (internal) or heap (external)
|
||||
const char *attribute; // Pointer to flash or nullptr (nullptr means no attribute)
|
||||
std::function<void(StringRef)> callback;
|
||||
std::function<void(const std::string &)> callback;
|
||||
bool once;
|
||||
|
||||
// Dynamic storage for external components using std::string API (custom_api_device.h)
|
||||
@@ -206,16 +205,12 @@ class APIServer : public Component,
|
||||
};
|
||||
|
||||
// New const char* overload (for internal components - zero allocation)
|
||||
void subscribe_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> f);
|
||||
void get_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> f);
|
||||
void subscribe_home_assistant_state(const char *entity_id, const char *attribute,
|
||||
std::function<void(const std::string &)> f);
|
||||
void get_home_assistant_state(const char *entity_id, const char *attribute,
|
||||
std::function<void(const std::string &)> f);
|
||||
|
||||
// std::string overload with StringRef callback (for custom_api_device.h with zero-allocation callback)
|
||||
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f);
|
||||
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f);
|
||||
|
||||
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string for callback)
|
||||
// Existing std::string overload (for custom_api_device.h - heap allocation)
|
||||
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f);
|
||||
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
@@ -243,11 +238,8 @@ class APIServer : public Component,
|
||||
#endif // USE_API_NOISE
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
// Helper methods to reduce code duplication
|
||||
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> f,
|
||||
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(const std::string &)> f,
|
||||
bool once);
|
||||
void add_state_subscription_(std::string entity_id, optional<std::string> attribute, std::function<void(StringRef)> f,
|
||||
bool once);
|
||||
// Legacy helper: wraps std::string callback and delegates to StringRef version
|
||||
void add_state_subscription_(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f, bool once);
|
||||
#endif // USE_API_HOMEASSISTANT_STATES
|
||||
|
||||
@@ -122,36 +122,21 @@ class CustomAPIDevice {
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "climate.kitchen", "current_temperature");
|
||||
* }
|
||||
*
|
||||
* void on_state_changed(StringRef state) {
|
||||
* // State of climate.kitchen current_temperature is `state`
|
||||
* // Use state.c_str() for C string, state.str() for std::string
|
||||
* void on_state_changed(const std::string &state) {
|
||||
* // State of sensor.weather_forecast is `state`
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||
* @param callback The member function to call when the entity state changes (zero-allocation).
|
||||
* @param callback The member function to call when the entity state changes.
|
||||
* @param entity_id The entity_id to track.
|
||||
* @param attribute The entity state attribute to track.
|
||||
*/
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(StringRef), const std::string &entity_id,
|
||||
void subscribe_homeassistant_state(void (T::*callback)(const std::string &), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), std::move(f));
|
||||
}
|
||||
|
||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant (legacy std::string version).
|
||||
*
|
||||
* @deprecated Use the StringRef overload for zero-allocation callbacks. Will be removed in 2027.1.0.
|
||||
*/
|
||||
template<typename T>
|
||||
ESPDEPRECATED("Use void callback(StringRef) instead. Will be removed in 2027.1.0.", "2026.1.0")
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
|
||||
// Explicit type to disambiguate overload resolution
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute),
|
||||
std::function<void(const std::string &)>(f));
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||
}
|
||||
|
||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||
@@ -163,39 +148,25 @@ class CustomAPIDevice {
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
||||
* }
|
||||
*
|
||||
* void on_state_changed(const std::string &entity_id, StringRef state) {
|
||||
* void on_state_changed(const std::string &entity_id, const std::string &state) {
|
||||
* // State of `entity_id` is `state`
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||
* @param callback The member function to call when the entity state changes (zero-allocation for state).
|
||||
* @param callback The member function to call when the entity state changes.
|
||||
* @param entity_id The entity_id to track.
|
||||
* @param attribute The entity state attribute to track.
|
||||
*/
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(const std::string &, StringRef), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
void subscribe_homeassistant_state(void (T::*callback)(const std::string &, const std::string &),
|
||||
const std::string &entity_id, const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), std::move(f));
|
||||
}
|
||||
|
||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant (legacy std::string version).
|
||||
*
|
||||
* @deprecated Use the StringRef overload for zero-allocation callbacks. Will be removed in 2027.1.0.
|
||||
*/
|
||||
template<typename T>
|
||||
ESPDEPRECATED("Use void callback(const std::string &, StringRef) instead. Will be removed in 2027.1.0.", "2026.1.0")
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
|
||||
// Explicit type to disambiguate overload resolution
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute),
|
||||
std::function<void(const std::string &)>(f));
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||
}
|
||||
#else
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(StringRef), const std::string &entity_id,
|
||||
void subscribe_homeassistant_state(void (T::*callback)(const std::string &), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
static_assert(sizeof(T) == 0,
|
||||
"subscribe_homeassistant_state() requires 'homeassistant_states: true' in the 'api:' section "
|
||||
@@ -203,24 +174,8 @@ class CustomAPIDevice {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
static_assert(sizeof(T) == 0,
|
||||
"subscribe_homeassistant_state() requires 'homeassistant_states: true' in the 'api:' section "
|
||||
"of your YAML configuration");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(const std::string &, StringRef), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
static_assert(sizeof(T) == 0,
|
||||
"subscribe_homeassistant_state() requires 'homeassistant_states: true' in the 'api:' section "
|
||||
"of your YAML configuration");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
void subscribe_homeassistant_state(void (T::*callback)(const std::string &, const std::string &),
|
||||
const std::string &entity_id, const std::string &attribute = "") {
|
||||
static_assert(sizeof(T) == 0,
|
||||
"subscribe_homeassistant_state() requires 'homeassistant_states: true' in the 'api:' section "
|
||||
"of your YAML configuration");
|
||||
|
||||
@@ -67,10 +67,10 @@ template<typename... Ts> class TemplatableKeyValuePair {
|
||||
// the callback is invoked synchronously while the message is on the stack).
|
||||
class ActionResponse {
|
||||
public:
|
||||
ActionResponse(bool success, StringRef error_message) : success_(success), error_message_(error_message) {}
|
||||
ActionResponse(bool success, const std::string &error_message) : success_(success), error_message_(error_message) {}
|
||||
|
||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
ActionResponse(bool success, StringRef error_message, const uint8_t *data, size_t data_len)
|
||||
ActionResponse(bool success, const std::string &error_message, const uint8_t *data, size_t data_len)
|
||||
: success_(success), error_message_(error_message) {
|
||||
if (data == nullptr || data_len == 0)
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "audio_reader.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include "audio.h"
|
||||
#include "audio_transfer_buffer.h"
|
||||
|
||||
@@ -7,12 +7,8 @@
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
// Maximum bytes to log in hex format for BLE writes (many logging buffers are 256 chars)
|
||||
static constexpr size_t BLE_WRITE_MAX_LOG_BYTES = 64;
|
||||
|
||||
namespace esphome::ble_client {
|
||||
|
||||
// placeholder class for static TAG .
|
||||
@@ -155,10 +151,7 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
||||
esph_log_w(Automation::TAG, "Cannot write to BLE characteristic - not connected");
|
||||
return false;
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(BLE_WRITE_MAX_LOG_BYTES)];
|
||||
esph_log_vv(Automation::TAG, "Will write %d bytes: %s", len, format_hex_pretty_to(hex_buf, data, len));
|
||||
#endif
|
||||
esph_log_vv(Automation::TAG, "Will write %d bytes: %s", len, format_hex_pretty(data, len).c_str());
|
||||
esp_err_t err =
|
||||
esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->char_handle_, len,
|
||||
const_cast<uint8_t *>(data), this->write_type_, ESP_GATT_AUTH_REQ_NONE);
|
||||
|
||||
@@ -50,7 +50,6 @@ TYPES = [
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(cg.Component),
|
||||
cv.GenerateID(CONF_BME68X_BSEC2_ID): cv.use_id(BME68xBSEC2Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
|
||||
@@ -97,6 +97,10 @@ async def to_code(config):
|
||||
cg.add_define("USE_CAPTIVE_PORTAL")
|
||||
|
||||
if CORE.using_arduino:
|
||||
if CORE.is_esp32:
|
||||
cg.add_library("ESP32 Async UDP", None)
|
||||
cg.add_library("DNSServer", None)
|
||||
cg.add_library("WiFi", None)
|
||||
if CORE.is_esp8266:
|
||||
cg.add_library("DNSServer", None)
|
||||
if CORE.is_libretiny:
|
||||
@@ -106,9 +110,6 @@ async def to_code(config):
|
||||
# Only compile the ESP-IDF DNS server when using ESP-IDF framework
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"dns_server_esp32_idf.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"dns_server_esp32_idf.cpp": {PlatformFramework.ESP32_IDF},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -69,11 +69,12 @@ void CaptivePortal::start() {
|
||||
|
||||
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
|
||||
|
||||
#if defined(USE_ESP32)
|
||||
#ifdef USE_ESP_IDF
|
||||
// Create DNS server instance for ESP-IDF
|
||||
this->dns_server_ = make_unique<DNSServer>();
|
||||
this->dns_server_->start(ip);
|
||||
#elif defined(USE_ARDUINO)
|
||||
#endif
|
||||
#ifdef USE_ARDUINO
|
||||
this->dns_server_ = make_unique<DNSServer>();
|
||||
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
|
||||
this->dns_server_->start(53, ESPHOME_F("*"), ip);
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_CAPTIVE_PORTAL
|
||||
#include <memory>
|
||||
#if defined(USE_ESP32)
|
||||
#include "dns_server_esp32_idf.h"
|
||||
#elif defined(USE_ARDUINO)
|
||||
#ifdef USE_ARDUINO
|
||||
#include <DNSServer.h>
|
||||
#endif
|
||||
#ifdef USE_ESP_IDF
|
||||
#include "dns_server_esp32_idf.h"
|
||||
#endif
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
@@ -22,14 +23,15 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override {
|
||||
#if defined(USE_ESP32)
|
||||
if (this->dns_server_ != nullptr) {
|
||||
this->dns_server_->process_next_request();
|
||||
}
|
||||
#elif defined(USE_ARDUINO)
|
||||
#ifdef USE_ARDUINO
|
||||
if (this->dns_server_ != nullptr) {
|
||||
this->dns_server_->processNextRequest();
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ESP_IDF
|
||||
if (this->dns_server_ != nullptr) {
|
||||
this->dns_server_->process_next_request();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
float get_setup_priority() const override;
|
||||
@@ -62,7 +64,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
web_server_base::WebServerBase *base_;
|
||||
bool initialized_{false};
|
||||
bool active_{false};
|
||||
#if defined(USE_ARDUINO) || defined(USE_ESP32)
|
||||
#if defined(USE_ARDUINO) || defined(USE_ESP_IDF)
|
||||
std::unique_ptr<DNSServer> dns_server_{nullptr};
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "dns_server_esp32_idf.h"
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
@@ -47,10 +47,7 @@ struct DNSAnswer {
|
||||
|
||||
void DNSServer::start(const network::IPAddress &ip) {
|
||||
this->server_ip_ = ip;
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
ESP_LOGV(TAG, "Starting DNS server on %s", ip.str_to(ip_buf));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Starting DNS server on %s", ip.str().c_str());
|
||||
|
||||
// Create loop-monitored UDP socket
|
||||
this->socket_ = socket::socket_ip_loop_monitored(SOCK_DGRAM, IPPROTO_UDP);
|
||||
@@ -205,4 +202,4 @@ void DNSServer::process_next_request() {
|
||||
|
||||
} // namespace esphome::captive_portal
|
||||
|
||||
#endif // USE_ESP32
|
||||
#endif // USE_ESP_IDF
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include <memory>
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -24,4 +24,4 @@ class DNSServer {
|
||||
|
||||
} // namespace esphome::captive_portal
|
||||
|
||||
#endif // USE_ESP32
|
||||
#endif // USE_ESP_IDF
|
||||
|
||||
@@ -169,16 +169,14 @@ void CC1101Component::loop() {
|
||||
}
|
||||
|
||||
// Read packet
|
||||
uint8_t payload_length, expected_rx;
|
||||
uint8_t payload_length;
|
||||
if (this->state_.LENGTH_CONFIG == static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_VARIABLE)) {
|
||||
this->read_(Register::FIFO, &payload_length, 1);
|
||||
expected_rx = payload_length + 1;
|
||||
} else {
|
||||
payload_length = this->state_.PKTLEN;
|
||||
expected_rx = payload_length;
|
||||
}
|
||||
if (payload_length == 0 || payload_length > 64 || rx_bytes != expected_rx) {
|
||||
ESP_LOGW(TAG, "Invalid packet: rx_bytes %u, payload_length %u", rx_bytes, payload_length);
|
||||
if (payload_length == 0 || payload_length > 64) {
|
||||
ESP_LOGW(TAG, "Invalid payload length: %u", payload_length);
|
||||
this->enter_idle_();
|
||||
this->strobe_(Command::FRX);
|
||||
this->strobe_(Command::RX);
|
||||
@@ -188,12 +186,13 @@ void CC1101Component::loop() {
|
||||
this->packet_.resize(payload_length);
|
||||
this->read_(Register::FIFO, this->packet_.data(), payload_length);
|
||||
|
||||
// Read status from registers (more reliable than FIFO status bytes due to timing issues)
|
||||
this->read_(Register::RSSI);
|
||||
this->read_(Register::LQI);
|
||||
float rssi = (this->state_.RSSI * RSSI_STEP) - RSSI_OFFSET;
|
||||
bool crc_ok = (this->state_.LQI & STATUS_CRC_OK_MASK) != 0;
|
||||
uint8_t lqi = this->state_.LQI & STATUS_LQI_MASK;
|
||||
// Read status and trigger
|
||||
uint8_t status[2];
|
||||
this->read_(Register::FIFO, status, 2);
|
||||
int8_t rssi_raw = static_cast<int8_t>(status[0]);
|
||||
float rssi = (rssi_raw * RSSI_STEP) - RSSI_OFFSET;
|
||||
bool crc_ok = (status[1] & STATUS_CRC_OK_MASK) != 0;
|
||||
uint8_t lqi = status[1] & STATUS_LQI_MASK;
|
||||
if (this->state_.CRC_EN == 0 || crc_ok) {
|
||||
this->packet_trigger_->trigger(this->packet_, rssi, lqi);
|
||||
}
|
||||
@@ -617,15 +616,12 @@ void CC1101Component::set_packet_mode(bool value) {
|
||||
this->state_.GDO0_CFG = 0x01;
|
||||
// Set max RX FIFO threshold to ensure we only trigger on end-of-packet
|
||||
this->state_.FIFO_THR = 15;
|
||||
// Don't append status bytes to FIFO - we read from registers instead
|
||||
this->state_.APPEND_STATUS = 0;
|
||||
} else {
|
||||
// Configure GDO0 for serial data (async serial mode)
|
||||
this->state_.GDO0_CFG = 0x0D;
|
||||
}
|
||||
if (this->initialized_) {
|
||||
this->write_(Register::PKTCTRL0);
|
||||
this->write_(Register::PKTCTRL1);
|
||||
this->write_(Register::IOCFG0);
|
||||
this->write_(Register::FIFOTHR);
|
||||
}
|
||||
|
||||
@@ -128,9 +128,7 @@ void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->
|
||||
bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) ^ this->inverted_; }
|
||||
|
||||
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); }
|
||||
size_t CH422GGPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return snprintf(buffer, len, "EXIO%u via CH422G", this->pin_);
|
||||
}
|
||||
std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); }
|
||||
void CH422GGPIOPin::set_flags(gpio::Flags flags) {
|
||||
flags_ = flags;
|
||||
this->parent_->pin_mode(this->pin_, flags);
|
||||
|
||||
@@ -50,7 +50,7 @@ class CH422GGPIOPin : public GPIOPin {
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
std::string dump_summary() const override;
|
||||
|
||||
void set_parent(CH422GComponent *parent) { parent_ = parent; }
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
|
||||
@@ -369,7 +369,7 @@ optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
}
|
||||
|
||||
void Climate::save_state_() {
|
||||
#if (defined(USE_ESP32) || (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0))) && \
|
||||
#if (defined(USE_ESP_IDF) || (defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0))) && \
|
||||
!defined(CLANG_TIDY)
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#define TEMP_IGNORE_MEMACCESS
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "cse7766.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace cse7766 {
|
||||
|
||||
static const char *const TAG = "cse7766";
|
||||
static constexpr size_t CSE7766_RAW_DATA_SIZE = 24;
|
||||
|
||||
void CSE7766Component::loop() {
|
||||
const uint32_t now = App.get_loop_component_start_time();
|
||||
@@ -72,8 +70,8 @@ bool CSE7766Component::check_byte_() {
|
||||
void CSE7766Component::parse_data_() {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
{
|
||||
char hex_buf[format_hex_pretty_size(CSE7766_RAW_DATA_SIZE)];
|
||||
ESP_LOGVV(TAG, "Raw data: %s", format_hex_pretty_to(hex_buf, this->raw_data_, sizeof(this->raw_data_)));
|
||||
std::string s = format_hex_pretty(this->raw_data_, sizeof(this->raw_data_));
|
||||
ESP_LOGVV(TAG, "Raw data: %s", s.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ void DallasTemperatureSensor::update() {
|
||||
}
|
||||
|
||||
float tempc = this->get_temp_c_();
|
||||
ESP_LOGD(TAG, "'%s': Got Temperature=%f°C", this->get_name().c_str(), tempc);
|
||||
ESP_LOGD(TAG, "'%s': Got Temperature=%.1f°C", this->get_name().c_str(), tempc);
|
||||
this->publish_state(tempc);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ void DebugComponent::get_device_info_(std::string &device_info) {
|
||||
#ifdef USE_ARDUINO
|
||||
ESP_LOGD(TAG, "Framework: Arduino");
|
||||
device_info += "Arduino";
|
||||
#elif defined(USE_ESP32)
|
||||
#elif defined(USE_ESP_IDF)
|
||||
ESP_LOGD(TAG, "Framework: ESP-IDF");
|
||||
device_info += "ESP-IDF";
|
||||
#else
|
||||
|
||||
@@ -7,9 +7,6 @@ namespace ee895 {
|
||||
|
||||
static const char *const TAG = "ee895";
|
||||
|
||||
// Serial number is 16 bytes
|
||||
static constexpr size_t EE895_SERIAL_NUMBER_SIZE = 16;
|
||||
|
||||
static const uint16_t CRC16_ONEWIRE_START = 0xFFFF;
|
||||
static const uint8_t FUNCTION_CODE_READ = 0x03;
|
||||
static const uint16_t SERIAL_NUMBER = 0x0000;
|
||||
@@ -29,10 +26,7 @@ void EE895Component::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char serial_hex[format_hex_size(EE895_SERIAL_NUMBER_SIZE)];
|
||||
#endif
|
||||
ESP_LOGV(TAG, " Serial Number: 0x%s", format_hex_to(serial_hex, serial_number + 2, EE895_SERIAL_NUMBER_SIZE));
|
||||
ESP_LOGV(TAG, " Serial Number: 0x%s", format_hex(serial_number + 2, 16).c_str());
|
||||
}
|
||||
|
||||
void EE895Component::dump_config() {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
namespace esphome::epaper_spi {
|
||||
|
||||
static const char *const TAG = "epaper_spi";
|
||||
static constexpr size_t EPAPER_MAX_CMD_LOG_BYTES = 128;
|
||||
|
||||
static constexpr const char *const EPAPER_STATE_STRINGS[] = {
|
||||
"IDLE", "UPDATE", "RESET", "RESET_END", "SHOULD_WAIT", "INITIALISE",
|
||||
@@ -69,11 +68,8 @@ void EPaperBase::data(uint8_t value) {
|
||||
// The command is the first byte, length is the length of data only in the second byte, followed by the data.
|
||||
// [COMMAND, LENGTH, DATA...]
|
||||
void EPaperBase::cmd_data(uint8_t command, const uint8_t *ptr, size_t length) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(EPAPER_MAX_CMD_LOG_BYTES)];
|
||||
ESP_LOGV(TAG, "Command: 0x%02X, Length: %d, Data: %s", command, length,
|
||||
format_hex_pretty_to(hex_buf, ptr, length, '.'));
|
||||
#endif
|
||||
format_hex_pretty(ptr, length, '.', false).c_str());
|
||||
|
||||
this->dc_pin_->digital_write(false);
|
||||
this->enable();
|
||||
|
||||
@@ -85,7 +85,6 @@ CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features"
|
||||
CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert"
|
||||
CONF_ENABLE_OTA_ROLLBACK = "enable_ota_rollback"
|
||||
CONF_EXECUTE_FROM_PSRAM = "execute_from_psram"
|
||||
CONF_MINIMUM_CHIP_REVISION = "minimum_chip_revision"
|
||||
CONF_RELEASE = "release"
|
||||
|
||||
LOG_LEVELS_IDF = [
|
||||
@@ -110,21 +109,6 @@ COMPILER_OPTIMIZATIONS = {
|
||||
"SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE",
|
||||
}
|
||||
|
||||
# ESP32 (original) chip revision options
|
||||
# Setting minimum revision to 3.0 or higher:
|
||||
# - Reduces flash size by excluding workaround code for older chip bugs
|
||||
# - For PSRAM users: disables CONFIG_SPIRAM_CACHE_WORKAROUND, which saves significant
|
||||
# IRAM by keeping C library functions in ROM instead of recompiling them
|
||||
# See: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/chip_revision.html
|
||||
ESP32_CHIP_REVISIONS = {
|
||||
"0.0": "CONFIG_ESP32_REV_MIN_0",
|
||||
"1.0": "CONFIG_ESP32_REV_MIN_1",
|
||||
"1.1": "CONFIG_ESP32_REV_MIN_1_1",
|
||||
"2.0": "CONFIG_ESP32_REV_MIN_2",
|
||||
"3.0": "CONFIG_ESP32_REV_MIN_3",
|
||||
"3.1": "CONFIG_ESP32_REV_MIN_3_1",
|
||||
}
|
||||
|
||||
# Socket limit configuration for ESP-IDF
|
||||
# ESP-IDF CONFIG_LWIP_MAX_SOCKETS has range 1-253, default 10
|
||||
DEFAULT_MAX_SOCKETS = 10 # ESP-IDF default
|
||||
@@ -373,12 +357,11 @@ def _is_framework_url(source: str) -> bool:
|
||||
# The default/recommended arduino framework version
|
||||
# - https://github.com/espressif/arduino-esp32/releases
|
||||
ARDUINO_FRAMEWORK_VERSION_LOOKUP = {
|
||||
"recommended": cv.Version(3, 3, 5),
|
||||
"latest": cv.Version(3, 3, 5),
|
||||
"dev": cv.Version(3, 3, 5),
|
||||
"recommended": cv.Version(3, 3, 2),
|
||||
"latest": cv.Version(3, 3, 4),
|
||||
"dev": cv.Version(3, 3, 4),
|
||||
}
|
||||
ARDUINO_PLATFORM_VERSION_LOOKUP = {
|
||||
cv.Version(3, 3, 5): cv.Version(55, 3, 35),
|
||||
cv.Version(3, 3, 4): cv.Version(55, 3, 31, "2"),
|
||||
cv.Version(3, 3, 3): cv.Version(55, 3, 31, "2"),
|
||||
cv.Version(3, 3, 2): cv.Version(55, 3, 31, "2"),
|
||||
@@ -391,33 +374,15 @@ ARDUINO_PLATFORM_VERSION_LOOKUP = {
|
||||
cv.Version(3, 1, 1): cv.Version(53, 3, 11),
|
||||
cv.Version(3, 1, 0): cv.Version(53, 3, 10),
|
||||
}
|
||||
# Maps Arduino framework versions to a compatible ESP-IDF version
|
||||
# These versions correspond to pioarduino/esp-idf releases
|
||||
# See: https://github.com/pioarduino/esp-idf/releases
|
||||
ARDUINO_IDF_VERSION_LOOKUP = {
|
||||
cv.Version(3, 3, 5): cv.Version(5, 5, 2),
|
||||
cv.Version(3, 3, 4): cv.Version(5, 5, 1),
|
||||
cv.Version(3, 3, 3): cv.Version(5, 5, 1),
|
||||
cv.Version(3, 3, 2): cv.Version(5, 5, 1),
|
||||
cv.Version(3, 3, 1): cv.Version(5, 5, 1),
|
||||
cv.Version(3, 3, 0): cv.Version(5, 5, 0),
|
||||
cv.Version(3, 2, 1): cv.Version(5, 4, 2),
|
||||
cv.Version(3, 2, 0): cv.Version(5, 4, 2),
|
||||
cv.Version(3, 1, 3): cv.Version(5, 3, 2),
|
||||
cv.Version(3, 1, 2): cv.Version(5, 3, 2),
|
||||
cv.Version(3, 1, 1): cv.Version(5, 3, 1),
|
||||
cv.Version(3, 1, 0): cv.Version(5, 3, 0),
|
||||
}
|
||||
|
||||
# The default/recommended esp-idf framework version
|
||||
# - https://github.com/espressif/esp-idf/releases
|
||||
ESP_IDF_FRAMEWORK_VERSION_LOOKUP = {
|
||||
"recommended": cv.Version(5, 5, 2),
|
||||
"latest": cv.Version(5, 5, 2),
|
||||
"dev": cv.Version(5, 5, 2),
|
||||
"recommended": cv.Version(5, 5, 1),
|
||||
"latest": cv.Version(5, 5, 1),
|
||||
"dev": cv.Version(5, 5, 1),
|
||||
}
|
||||
ESP_IDF_PLATFORM_VERSION_LOOKUP = {
|
||||
cv.Version(5, 5, 2): cv.Version(55, 3, 35),
|
||||
cv.Version(5, 5, 1): cv.Version(55, 3, 31, "2"),
|
||||
cv.Version(5, 5, 0): cv.Version(55, 3, 31, "2"),
|
||||
cv.Version(5, 4, 3): cv.Version(55, 3, 32),
|
||||
@@ -434,9 +399,9 @@ ESP_IDF_PLATFORM_VERSION_LOOKUP = {
|
||||
# The platform-espressif32 version
|
||||
# - https://github.com/pioarduino/platform-espressif32/releases
|
||||
PLATFORM_VERSION_LOOKUP = {
|
||||
"recommended": cv.Version(55, 3, 35),
|
||||
"latest": cv.Version(55, 3, 35),
|
||||
"dev": cv.Version(55, 3, 35),
|
||||
"recommended": cv.Version(55, 3, 31, "2"),
|
||||
"latest": cv.Version(55, 3, 31, "2"),
|
||||
"dev": cv.Version(55, 3, 31, "2"),
|
||||
}
|
||||
|
||||
|
||||
@@ -582,16 +547,6 @@ def final_validate(config):
|
||||
path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_IGNORE_EFUSE_MAC_CRC],
|
||||
)
|
||||
)
|
||||
if (
|
||||
config[CONF_VARIANT] != VARIANT_ESP32
|
||||
and advanced.get(CONF_MINIMUM_CHIP_REVISION) is not None
|
||||
):
|
||||
errs.append(
|
||||
cv.Invalid(
|
||||
f"'{CONF_MINIMUM_CHIP_REVISION}' is only supported on {VARIANT_ESP32}",
|
||||
path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_MINIMUM_CHIP_REVISION],
|
||||
)
|
||||
)
|
||||
if advanced[CONF_EXECUTE_FROM_PSRAM]:
|
||||
if config[CONF_VARIANT] != VARIANT_ESP32S3:
|
||||
errs.append(
|
||||
@@ -734,9 +689,6 @@ FRAMEWORK_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean,
|
||||
cv.Optional(CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False): cv.boolean,
|
||||
cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MINIMUM_CHIP_REVISION): cv.one_of(
|
||||
*ESP32_CHIP_REVISIONS
|
||||
),
|
||||
# DHCP server is needed for WiFi AP mode. When WiFi component is used,
|
||||
# it will handle disabling DHCP server when AP is not configured.
|
||||
# Default to false (disabled) when WiFi is not used.
|
||||
@@ -789,14 +741,12 @@ FRAMEWORK_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
# Remove this class in 2026.7.0
|
||||
class _FrameworkMigrationWarning:
|
||||
shown = False
|
||||
|
||||
|
||||
def _show_framework_migration_message(name: str, variant: str) -> None:
|
||||
"""Show a message about the framework default change and how to switch back to Arduino."""
|
||||
# Remove this function in 2026.7.0
|
||||
"""Show a friendly message about framework migration when defaulting to Arduino."""
|
||||
if _FrameworkMigrationWarning.shown:
|
||||
return
|
||||
_FrameworkMigrationWarning.shown = True
|
||||
@@ -806,27 +756,41 @@ def _show_framework_migration_message(name: str, variant: str) -> None:
|
||||
message = (
|
||||
color(
|
||||
AnsiFore.BOLD_CYAN,
|
||||
f"💡 NOTICE: {name} does not have a framework specified.",
|
||||
f"💡 IMPORTANT: {name} doesn't have a framework specified!",
|
||||
)
|
||||
+ "\n\n"
|
||||
+ f"Starting with ESPHome 2026.1.0, the default framework for {variant} is ESP-IDF.\n"
|
||||
+ "(We've been warning about this change since ESPHome 2025.8.0)\n"
|
||||
+ f"Currently, {variant} defaults to the Arduino framework.\n"
|
||||
+ color(AnsiFore.YELLOW, "This will change to ESP-IDF in ESPHome 2026.1.0.\n")
|
||||
+ "\n"
|
||||
+ "Why we made this change:\n"
|
||||
+ color(AnsiFore.GREEN, " ✨ Up to 40% smaller firmware binaries\n")
|
||||
+ "Note: Newer ESP32 variants (C6, H2, P4, etc.) already use ESP-IDF by default.\n"
|
||||
+ "\n"
|
||||
+ "Why change? ESP-IDF offers:\n"
|
||||
+ color(AnsiFore.GREEN, " ✨ Up to 40% smaller binaries\n")
|
||||
+ color(AnsiFore.GREEN, " 🚀 Better performance and optimization\n")
|
||||
+ color(AnsiFore.GREEN, " ⚡ 2-3x faster compile times\n")
|
||||
+ color(AnsiFore.GREEN, " 🚀 Better performance and newer features\n")
|
||||
+ color(AnsiFore.GREEN, " 🔧 More actively maintained by ESPHome\n")
|
||||
+ color(AnsiFore.GREEN, " 📦 Custom-built firmware for your exact needs\n")
|
||||
+ color(
|
||||
AnsiFore.GREEN,
|
||||
" 🔧 Active development and testing by ESPHome developers\n",
|
||||
)
|
||||
+ "\n"
|
||||
+ "To continue using Arduino, add this to your YAML under 'esp32:':\n"
|
||||
+ color(AnsiFore.WHITE, " framework:\n")
|
||||
+ color(AnsiFore.WHITE, " type: arduino\n")
|
||||
+ "Trade-offs:\n"
|
||||
+ color(AnsiFore.YELLOW, " 🔄 Some components need migration\n")
|
||||
+ "\n"
|
||||
+ "To silence this message with ESP-IDF, explicitly set:\n"
|
||||
+ color(AnsiFore.WHITE, " framework:\n")
|
||||
+ color(AnsiFore.WHITE, " type: esp-idf\n")
|
||||
+ "What should I do?\n"
|
||||
+ color(AnsiFore.CYAN, " Option 1")
|
||||
+ ": Migrate to ESP-IDF (recommended)\n"
|
||||
+ " Add this to your YAML under 'esp32:':\n"
|
||||
+ color(AnsiFore.WHITE, " framework:\n")
|
||||
+ color(AnsiFore.WHITE, " type: esp-idf\n")
|
||||
+ "\n"
|
||||
+ "Migration guide: "
|
||||
+ color(AnsiFore.CYAN, " Option 2")
|
||||
+ ": Keep using Arduino (still supported)\n"
|
||||
+ " Add this to your YAML under 'esp32:':\n"
|
||||
+ color(AnsiFore.WHITE, " framework:\n")
|
||||
+ color(AnsiFore.WHITE, " type: arduino\n")
|
||||
+ "\n"
|
||||
+ "Need help? Check out the migration guide:\n"
|
||||
+ color(
|
||||
AnsiFore.BLUE,
|
||||
"https://esphome.io/guides/esp32_arduino_to_idf/",
|
||||
@@ -841,13 +805,13 @@ def _set_default_framework(config):
|
||||
config[CONF_FRAMEWORK] = FRAMEWORK_SCHEMA({})
|
||||
if CONF_TYPE not in config[CONF_FRAMEWORK]:
|
||||
variant = config[CONF_VARIANT]
|
||||
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF
|
||||
# Show migration message for variants that previously defaulted to Arduino
|
||||
# Remove this message in 2026.7.0
|
||||
if variant in ARDUINO_ALLOWED_VARIANTS:
|
||||
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO
|
||||
_show_framework_migration_message(
|
||||
config.get(CONF_NAME, "This device"), variant
|
||||
)
|
||||
else:
|
||||
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF
|
||||
|
||||
return config
|
||||
|
||||
@@ -1041,13 +1005,6 @@ async def to_code(config):
|
||||
add_idf_sdkconfig_option("CONFIG_MBEDTLS_PSK_MODES", True)
|
||||
add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True)
|
||||
|
||||
# Add IDF framework source for Arduino builds to ensure it uses the same version as
|
||||
# the ESP-IDF framework
|
||||
if (idf_ver := ARDUINO_IDF_VERSION_LOOKUP.get(framework_ver)) is not None:
|
||||
cg.add_platformio_option(
|
||||
"platform_packages", [_format_framework_espidf_version(idf_ver, None)]
|
||||
)
|
||||
|
||||
# ESP32-S2 Arduino: Disable USB Serial on boot to avoid TinyUSB dependency
|
||||
if get_esp32_variant() == VARIANT_ESP32S2:
|
||||
cg.add_build_unflag("-DARDUINO_USB_CDC_ON_BOOT=1")
|
||||
@@ -1060,16 +1017,6 @@ async def to_code(config):
|
||||
add_idf_sdkconfig_option(
|
||||
f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True
|
||||
)
|
||||
|
||||
# Set minimum chip revision for ESP32 variant
|
||||
# Setting this to 3.0 or higher reduces flash size by excluding workaround code,
|
||||
# and for PSRAM users saves significant IRAM by keeping C library functions in ROM.
|
||||
if variant == VARIANT_ESP32:
|
||||
min_rev = conf[CONF_ADVANCED].get(CONF_MINIMUM_CHIP_REVISION)
|
||||
if min_rev is not None:
|
||||
for rev, flag in ESP32_CHIP_REVISIONS.items():
|
||||
add_idf_sdkconfig_option(flag, rev == min_rev)
|
||||
cg.add_define("USE_ESP32_MIN_CHIP_REVISION_SET")
|
||||
add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False)
|
||||
add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True)
|
||||
add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM_FILENAME", "partitions.csv")
|
||||
|
||||
@@ -1488,10 +1488,6 @@ BOARDS = {
|
||||
"name": "Arduino Nano ESP32",
|
||||
"variant": VARIANT_ESP32S3,
|
||||
},
|
||||
"arduino_nesso_n1": {
|
||||
"name": "Arduino Nesso-N1",
|
||||
"variant": VARIANT_ESP32C6,
|
||||
},
|
||||
"atd147_s3": {
|
||||
"name": "ArtronShop ATD1.47-S3",
|
||||
"variant": VARIANT_ESP32S3,
|
||||
@@ -1660,10 +1656,6 @@ BOARDS = {
|
||||
"name": "Espressif ESP32-C6-DevKitM-1",
|
||||
"variant": VARIANT_ESP32C6,
|
||||
},
|
||||
"esp32-c61-devkitc1-n8r2": {
|
||||
"name": "Espressif ESP32-C61-DevKitC-1 N8R2 (8 MB Flash Quad, 2 MB PSRAM Quad)",
|
||||
"variant": VARIANT_ESP32C61,
|
||||
},
|
||||
"esp32-devkitlipo": {
|
||||
"name": "OLIMEX ESP32-DevKit-LiPo",
|
||||
"variant": VARIANT_ESP32,
|
||||
@@ -1681,15 +1673,11 @@ BOARDS = {
|
||||
"variant": VARIANT_ESP32H2,
|
||||
},
|
||||
"esp32-p4": {
|
||||
"name": "Espressif ESP32-P4 ES (pre rev.300) generic",
|
||||
"name": "Espressif ESP32-P4 generic",
|
||||
"variant": VARIANT_ESP32P4,
|
||||
},
|
||||
"esp32-p4-evboard": {
|
||||
"name": "Espressif ESP32-P4 Function EV Board (ES pre rev.300)",
|
||||
"variant": VARIANT_ESP32P4,
|
||||
},
|
||||
"esp32-p4_r3": {
|
||||
"name": "Espressif ESP32-P4 rev.300 generic",
|
||||
"name": "Espressif ESP32-P4 Function EV Board",
|
||||
"variant": VARIANT_ESP32P4,
|
||||
},
|
||||
"esp32-pico-devkitm-2": {
|
||||
@@ -2105,7 +2093,7 @@ BOARDS = {
|
||||
"variant": VARIANT_ESP32,
|
||||
},
|
||||
"m5stack-tab5-p4": {
|
||||
"name": "M5STACK Tab5 esp32-p4 Board (ES pre rev.300)",
|
||||
"name": "M5STACK Tab5 esp32-p4 Board",
|
||||
"variant": VARIANT_ESP32P4,
|
||||
},
|
||||
"m5stack-timer-cam": {
|
||||
|
||||
@@ -97,8 +97,10 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
|
||||
gpio_intr_enable(this->get_pin_num());
|
||||
}
|
||||
|
||||
size_t ESP32InternalGPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return snprintf(buffer, len, "GPIO%" PRIu32, static_cast<uint32_t>(this->pin_));
|
||||
std::string ESP32InternalGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast<uint32_t>(this->pin_));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ESP32InternalGPIOPin::setup() {
|
||||
|
||||
@@ -24,7 +24,7 @@ class ESP32InternalGPIOPin : public InternalGPIOPin {
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return this->pin_; }
|
||||
|
||||
@@ -24,9 +24,7 @@ extern "C" {
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
// Prevent Arduino from releasing BT memory at startup (esp32-hal-misc.c).
|
||||
// Without this, esp_bt_controller_init() fails with ESP_ERR_INVALID_STATE.
|
||||
extern "C" bool btInUse() { return true; } // NOLINT(readability-identifier-naming)
|
||||
#include <esp32-hal-bt.h>
|
||||
#endif
|
||||
|
||||
namespace esphome::esp32_ble {
|
||||
@@ -171,6 +169,12 @@ void ESP32BLE::advertising_init_() {
|
||||
bool ESP32BLE::ble_setup_() {
|
||||
esp_err_t err;
|
||||
#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
|
||||
#ifdef USE_ARDUINO
|
||||
if (!btStart()) {
|
||||
ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
|
||||
// start bt controller
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
@@ -195,6 +199,7 @@ bool ESP32BLE::ble_setup_() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
#else
|
||||
@@ -333,6 +338,12 @@ bool ESP32BLE::ble_dismantle_() {
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
|
||||
#ifdef USE_ARDUINO
|
||||
if (!btStop()) {
|
||||
ESP_LOGE(TAG, "btStop failed: %d", esp_bt_controller_get_status());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
// stop bt controller
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
|
||||
@@ -356,6 +367,7 @@ bool ESP32BLE::ble_dismantle_() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
if (esp_hosted_bt_controller_disable() != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_hosted_bt_controller_disable failed");
|
||||
|
||||
@@ -37,9 +37,6 @@ namespace esphome::esp32_ble_tracker {
|
||||
|
||||
static const char *const TAG = "esp32_ble_tracker";
|
||||
|
||||
// BLE advertisement max: 31 bytes adv data + 31 bytes scan response
|
||||
static constexpr size_t BLE_ADV_MAX_LOG_BYTES = 62;
|
||||
|
||||
ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
const char *client_state_to_string(ClientState state) {
|
||||
@@ -448,7 +445,6 @@ void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
|
||||
uuid.to_str(uuid_buf);
|
||||
ESP_LOGVV(TAG, " Service UUID: %s", uuid_buf);
|
||||
}
|
||||
char hex_buf[format_hex_pretty_size(BLE_ADV_MAX_LOG_BYTES)];
|
||||
for (auto &data : this->manufacturer_datas_) {
|
||||
auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(data);
|
||||
if (ibeacon.has_value()) {
|
||||
@@ -462,8 +458,7 @@ void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
|
||||
} else {
|
||||
char uuid_buf[esp32_ble::UUID_STR_LEN];
|
||||
data.uuid.to_str(uuid_buf);
|
||||
ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", uuid_buf,
|
||||
format_hex_pretty_to(hex_buf, data.data.data(), data.data.size()));
|
||||
ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", uuid_buf, format_hex_pretty(data.data).c_str());
|
||||
}
|
||||
}
|
||||
for (auto &data : this->service_datas_) {
|
||||
@@ -471,11 +466,11 @@ void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
|
||||
char uuid_buf[esp32_ble::UUID_STR_LEN];
|
||||
data.uuid.to_str(uuid_buf);
|
||||
ESP_LOGVV(TAG, " UUID: %s", uuid_buf);
|
||||
ESP_LOGVV(TAG, " Data: %s", format_hex_pretty_to(hex_buf, data.data.data(), data.data.size()));
|
||||
ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str());
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, " Adv data: %s",
|
||||
format_hex_pretty_to(hex_buf, scan_result.ble_adv, scan_result.adv_data_len + scan_result.scan_rsp_len));
|
||||
format_hex_pretty(scan_result.ble_adv, scan_result.adv_data_len + scan_result.scan_rsp_len).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "esphome/components/esp32_ble/ble.h"
|
||||
#include "esphome/components/esp32_ble_server/ble_2902.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
@@ -15,7 +14,6 @@ namespace esp32_improv {
|
||||
using namespace bytebuffer;
|
||||
|
||||
static const char *const TAG = "esp32_improv.component";
|
||||
static constexpr size_t IMPROV_MAX_LOG_BYTES = 128;
|
||||
static const char *const ESPHOME_MY_LINK = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome";
|
||||
static constexpr uint16_t STOP_ADVERTISING_DELAY =
|
||||
10000; // Delay (ms) before stopping service to allow BLE clients to read the final state
|
||||
@@ -316,11 +314,7 @@ void ESP32ImprovComponent::dump_config() {
|
||||
void ESP32ImprovComponent::process_incoming_data_() {
|
||||
uint8_t length = this->incoming_data_[1];
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(IMPROV_MAX_LOG_BYTES)];
|
||||
ESP_LOGV(TAG, "Processing bytes - %s",
|
||||
format_hex_pretty_to(hex_buf, this->incoming_data_.data(), this->incoming_data_.size()));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
|
||||
if (this->incoming_data_.size() - 3 == length) {
|
||||
this->set_error_(improv::ERROR_NONE);
|
||||
improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_);
|
||||
@@ -409,12 +403,8 @@ void ESP32ImprovComponent::check_wifi_connection_() {
|
||||
#ifdef USE_WEBSERVER
|
||||
for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) {
|
||||
if (ip.is_ip4()) {
|
||||
// "http://" (7) + IPv4 max (15) + ":" (1) + port max (5) + null = 29
|
||||
char url_buffer[32];
|
||||
memcpy(url_buffer, "http://", 7); // NOLINT(bugprone-not-null-terminated-result) - str_to null-terminates
|
||||
ip.str_to(url_buffer + 7);
|
||||
size_t len = strlen(url_buffer);
|
||||
snprintf(url_buffer + len, sizeof(url_buffer) - len, ":%d", USE_WEBSERVER_PORT);
|
||||
char url_buffer[64];
|
||||
snprintf(url_buffer, sizeof(url_buffer), "http://%s:%d", ip.str().c_str(), USE_WEBSERVER_PORT);
|
||||
url_strings[url_count++] = url_buffer;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ from .const import (
|
||||
KEY_ESP8266,
|
||||
KEY_FLASH_SIZE,
|
||||
KEY_PIN_INITIAL_STATES,
|
||||
KEY_WAVEFORM_REQUIRED,
|
||||
esp8266_ns,
|
||||
)
|
||||
from .gpio import PinInitialState, add_pin_initial_states_array
|
||||
@@ -192,13 +191,7 @@ async def to_code(config):
|
||||
cg.add_define(ThreadModel.SINGLE)
|
||||
|
||||
cg.add_platformio_option(
|
||||
"extra_scripts",
|
||||
[
|
||||
"pre:testing_mode.py",
|
||||
"pre:exclude_updater.py",
|
||||
"pre:exclude_waveform.py",
|
||||
"post:post_build.py",
|
||||
],
|
||||
"extra_scripts", ["pre:testing_mode.py", "post:post_build.py"]
|
||||
)
|
||||
|
||||
conf = config[CONF_FRAMEWORK]
|
||||
@@ -270,24 +263,10 @@ async def to_code(config):
|
||||
cg.add_platformio_option("board_build.ldscript", ld_script)
|
||||
|
||||
CORE.add_job(add_pin_initial_states_array)
|
||||
CORE.add_job(finalize_waveform_config)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.WORKAROUNDS)
|
||||
async def finalize_waveform_config() -> None:
|
||||
"""Add waveform stubs define if waveform is not required.
|
||||
|
||||
This runs at WORKAROUNDS priority (-999) to ensure all components
|
||||
have had a chance to call require_waveform() first.
|
||||
"""
|
||||
if not CORE.data.get(KEY_ESP8266, {}).get(KEY_WAVEFORM_REQUIRED, False):
|
||||
# No component needs waveform - enable stubs and exclude Arduino waveform code
|
||||
# Use build flag (visible to both C++ code and PlatformIO script)
|
||||
cg.add_build_flag("-DUSE_ESP8266_WAVEFORM_STUBS")
|
||||
|
||||
|
||||
# Called by writer.py
|
||||
def copy_files() -> None:
|
||||
def copy_files():
|
||||
dir = Path(__file__).parent
|
||||
post_build_file = dir / "post_build.py.script"
|
||||
copy_file_if_changed(
|
||||
@@ -299,13 +278,3 @@ def copy_files() -> None:
|
||||
testing_mode_file,
|
||||
CORE.relative_build_path("testing_mode.py"),
|
||||
)
|
||||
exclude_updater_file = dir / "exclude_updater.py.script"
|
||||
copy_file_if_changed(
|
||||
exclude_updater_file,
|
||||
CORE.relative_build_path("exclude_updater.py"),
|
||||
)
|
||||
exclude_waveform_file = dir / "exclude_waveform.py.script"
|
||||
copy_file_if_changed(
|
||||
exclude_waveform_file,
|
||||
CORE.relative_build_path("exclude_waveform.py"),
|
||||
)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.core import CORE
|
||||
|
||||
KEY_ESP8266 = "esp8266"
|
||||
KEY_BOARD = "board"
|
||||
@@ -7,25 +6,6 @@ KEY_PIN_INITIAL_STATES = "pin_initial_states"
|
||||
CONF_RESTORE_FROM_FLASH = "restore_from_flash"
|
||||
CONF_EARLY_PIN_INIT = "early_pin_init"
|
||||
KEY_FLASH_SIZE = "flash_size"
|
||||
KEY_WAVEFORM_REQUIRED = "waveform_required"
|
||||
|
||||
# esp8266 namespace is already defined by arduino, manually prefix esphome
|
||||
esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266")
|
||||
|
||||
|
||||
def require_waveform() -> None:
|
||||
"""Mark that Arduino waveform/PWM support is required.
|
||||
|
||||
Call this from components that need the Arduino waveform generator
|
||||
(startWaveform, stopWaveform, analogWrite, Tone, Servo).
|
||||
|
||||
If no component calls this, the waveform code is excluded from the build
|
||||
to save ~596 bytes of RAM and 464 bytes of flash.
|
||||
|
||||
Example:
|
||||
from esphome.components.esp8266.const import require_waveform
|
||||
|
||||
async def to_code(config):
|
||||
require_waveform()
|
||||
"""
|
||||
CORE.data.setdefault(KEY_ESP8266, {})[KEY_WAVEFORM_REQUIRED] = True
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# pylint: disable=E0602
|
||||
Import("env") # noqa
|
||||
|
||||
import os
|
||||
|
||||
# Filter out Updater.cpp from the Arduino core build
|
||||
# This saves 228 bytes of .bss by not instantiating the global Update object
|
||||
# ESPHome uses its own native OTA backend instead
|
||||
|
||||
|
||||
def filter_updater_from_core(env, node):
|
||||
"""Filter callback to exclude Updater.cpp from framework build."""
|
||||
path = node.get_path()
|
||||
if path.endswith("Updater.cpp"):
|
||||
print(f"ESPHome: Excluding {os.path.basename(path)} from build (using native OTA backend)")
|
||||
return None
|
||||
return node
|
||||
|
||||
|
||||
# Apply the filter to framework sources
|
||||
env.AddBuildMiddleware(filter_updater_from_core, "**/cores/esp8266/Updater.cpp")
|
||||
@@ -1,50 +0,0 @@
|
||||
# pylint: disable=E0602
|
||||
Import("env") # noqa
|
||||
|
||||
import os
|
||||
|
||||
# Filter out waveform/PWM code from the Arduino core build
|
||||
# This saves ~596 bytes of RAM and 464 bytes of flash by not
|
||||
# instantiating the waveform generator state structures (wvfState + pwmState).
|
||||
#
|
||||
# The waveform code is used by: analogWrite, Tone, Servo, and direct
|
||||
# startWaveform/stopWaveform calls. ESPHome's esp8266_pwm component
|
||||
# calls require_waveform() to keep this code when needed.
|
||||
#
|
||||
# When excluded, we provide stub implementations of stopWaveform() and
|
||||
# _stopPWM() since digitalWrite() calls these unconditionally.
|
||||
|
||||
|
||||
def has_define_flag(env, name):
|
||||
"""Check if a define exists in the build flags."""
|
||||
define_flag = f"-D{name}"
|
||||
# Check BUILD_FLAGS (where ESPHome puts its defines)
|
||||
for flag in env.get("BUILD_FLAGS", []):
|
||||
if flag == define_flag or flag.startswith(f"{define_flag}="):
|
||||
return True
|
||||
# Also check CPPDEFINES list (parsed defines)
|
||||
for define in env.get("CPPDEFINES", []):
|
||||
if isinstance(define, tuple):
|
||||
if define[0] == name:
|
||||
return True
|
||||
elif define == name:
|
||||
return True
|
||||
return False
|
||||
|
||||
# USE_ESP8266_WAVEFORM_STUBS is defined when no component needs waveform
|
||||
if has_define_flag(env, "USE_ESP8266_WAVEFORM_STUBS"):
|
||||
|
||||
def filter_waveform_from_core(env, node):
|
||||
"""Filter callback to exclude waveform files from framework build."""
|
||||
path = node.get_path()
|
||||
filename = os.path.basename(path)
|
||||
if filename in (
|
||||
"core_esp8266_waveform_pwm.cpp",
|
||||
"core_esp8266_waveform_phase.cpp",
|
||||
):
|
||||
print(f"ESPHome: Excluding {filename} from build (waveform not required)")
|
||||
return None
|
||||
return node
|
||||
|
||||
# Apply the filter to framework sources
|
||||
env.AddBuildMiddleware(filter_waveform_from_core, "**/cores/esp8266/*.cpp")
|
||||
@@ -98,8 +98,10 @@ void ESP8266GPIOPin::pin_mode(gpio::Flags flags) {
|
||||
pinMode(pin_, flags_to_mode(flags, pin_)); // NOLINT
|
||||
}
|
||||
|
||||
size_t ESP8266GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return snprintf(buffer, len, "GPIO%u", this->pin_);
|
||||
std::string ESP8266GPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%u", pin_);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool ESP8266GPIOPin::digital_read() {
|
||||
|
||||
@@ -17,7 +17,7 @@ class ESP8266GPIOPin : public InternalGPIOPin {
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#ifdef USE_ESP8266_WAVEFORM_STUBS
|
||||
|
||||
// Stub implementations for Arduino waveform/PWM functions.
|
||||
//
|
||||
// When the waveform generator is not needed (no esp8266_pwm component),
|
||||
// we exclude core_esp8266_waveform_pwm.cpp from the build to save ~596 bytes
|
||||
// of RAM and 464 bytes of flash.
|
||||
//
|
||||
// These stubs satisfy calls from the Arduino GPIO code when the real
|
||||
// waveform implementation is excluded. They must be in the global namespace
|
||||
// with C linkage to match the Arduino core function declarations.
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Empty namespace to satisfy linter - actual stubs must be at global scope
|
||||
namespace esphome::esp8266 {} // namespace esphome::esp8266
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Called by Arduino GPIO code to stop any waveform on a pin
|
||||
int stopWaveform(uint8_t pin) {
|
||||
(void) pin;
|
||||
return 1; // Success (no waveform to stop)
|
||||
}
|
||||
|
||||
// Called by Arduino GPIO code to stop any PWM on a pin
|
||||
bool _stopPWM(uint8_t pin) {
|
||||
(void) pin;
|
||||
return false; // No PWM was running
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // USE_ESP8266_WAVEFORM_STUBS
|
||||
@@ -1,7 +1,6 @@
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
from esphome.components.esp8266.const import require_waveform
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN
|
||||
|
||||
@@ -35,9 +34,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config) -> None:
|
||||
require_waveform()
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await output.register_output(var, config)
|
||||
|
||||
@@ -31,7 +31,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
}
|
||||
)
|
||||
),
|
||||
cv.only_on_esp32,
|
||||
cv.only_with_esp_idf,
|
||||
only_on_variant(supported=[VARIANT_ESP32P4]),
|
||||
)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from esphome.const import (
|
||||
CONF_SAFE_MODE,
|
||||
CONF_VERSION,
|
||||
)
|
||||
from esphome.core import coroutine_with_priority
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
@@ -28,7 +28,17 @@ CODEOWNERS = ["@esphome/core"]
|
||||
DEPENDENCIES = ["network"]
|
||||
|
||||
|
||||
AUTO_LOAD = ["sha256", "socket"]
|
||||
def supports_sha256() -> bool:
|
||||
"""Check if the current platform supports SHA256 for OTA authentication."""
|
||||
return bool(CORE.is_esp32 or CORE.is_esp8266 or CORE.is_rp2040 or CORE.is_libretiny)
|
||||
|
||||
|
||||
def AUTO_LOAD() -> list[str]:
|
||||
"""Conditionally auto-load sha256 only on platforms that support it."""
|
||||
base_components = ["md5", "socket"]
|
||||
if supports_sha256():
|
||||
return base_components + ["sha256"]
|
||||
return base_components
|
||||
|
||||
|
||||
esphome = cg.esphome_ns.namespace("esphome")
|
||||
@@ -145,6 +155,11 @@ async def to_code(config: ConfigType) -> None:
|
||||
if config.get(CONF_PASSWORD):
|
||||
cg.add(var.set_auth_password(config[CONF_PASSWORD]))
|
||||
cg.add_define("USE_OTA_PASSWORD")
|
||||
# Only include hash algorithms when password is configured
|
||||
cg.add_define("USE_OTA_MD5")
|
||||
# Only include SHA256 support on platforms that have it
|
||||
if supports_sha256():
|
||||
cg.add_define("USE_OTA_SHA256")
|
||||
cg.add_define("USE_OTA_VERSION", config[CONF_VERSION])
|
||||
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#include "ota_esphome.h"
|
||||
#ifdef USE_OTA
|
||||
#ifdef USE_OTA_PASSWORD
|
||||
#ifdef USE_OTA_MD5
|
||||
#include "esphome/components/md5/md5.h"
|
||||
#endif
|
||||
#ifdef USE_OTA_SHA256
|
||||
#include "esphome/components/sha256/sha256.h"
|
||||
#endif
|
||||
#endif
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/components/socket/socket.h"
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#include "esphome/components/ota/ota_backend_esp8266.h"
|
||||
#include "esphome/components/ota/ota_backend_arduino_esp8266.h"
|
||||
#include "esphome/components/ota/ota_backend_arduino_libretiny.h"
|
||||
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
|
||||
#include "esphome/components/ota/ota_backend_esp_idf.h"
|
||||
@@ -27,6 +31,15 @@ static constexpr size_t OTA_BUFFER_SIZE = 1024; // buffer size
|
||||
static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 20000; // milliseconds for initial handshake
|
||||
static constexpr uint32_t OTA_SOCKET_TIMEOUT_DATA = 90000; // milliseconds for data transfer
|
||||
|
||||
#ifdef USE_OTA_PASSWORD
|
||||
#ifdef USE_OTA_MD5
|
||||
static constexpr size_t MD5_HEX_SIZE = 32; // MD5 hash as hex string (16 bytes * 2)
|
||||
#endif
|
||||
#ifdef USE_OTA_SHA256
|
||||
static constexpr size_t SHA256_HEX_SIZE = 64; // SHA256 hash as hex string (32 bytes * 2)
|
||||
#endif
|
||||
#endif // USE_OTA_PASSWORD
|
||||
|
||||
void ESPHomeOTAComponent::setup() {
|
||||
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
|
||||
if (this->server_ == nullptr) {
|
||||
@@ -95,7 +108,15 @@ void ESPHomeOTAComponent::loop() {
|
||||
}
|
||||
|
||||
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
|
||||
#ifdef USE_OTA_SHA256
|
||||
static const uint8_t FEATURE_SUPPORTS_SHA256_AUTH = 0x02;
|
||||
#endif
|
||||
|
||||
// Temporary flag to allow MD5 downgrade for ~3 versions (until 2026.1.0)
|
||||
// This allows users to downgrade via OTA if they encounter issues after updating.
|
||||
// Without this, users would need to do a serial flash to downgrade.
|
||||
// TODO: Remove this flag and all associated code in 2026.1.0
|
||||
#define ALLOW_OTA_DOWNGRADE_MD5
|
||||
|
||||
void ESPHomeOTAComponent::handle_handshake_() {
|
||||
/// Handle the OTA handshake and authentication.
|
||||
@@ -444,9 +465,7 @@ void ESPHomeOTAComponent::log_socket_error_(const LogString *msg) {
|
||||
void ESPHomeOTAComponent::log_read_error_(const LogString *what) { ESP_LOGW(TAG, "Read %s failed", LOG_STR_ARG(what)); }
|
||||
|
||||
void ESPHomeOTAComponent::log_start_(const LogString *phase) {
|
||||
char peername[socket::PEERNAME_MAX_LEN];
|
||||
this->client_->getpeername_to(peername);
|
||||
ESP_LOGD(TAG, "Starting %s from %s", LOG_STR_ARG(phase), peername);
|
||||
ESP_LOGD(TAG, "Starting %s from %s", LOG_STR_ARG(phase), this->client_->getpeername().c_str());
|
||||
}
|
||||
|
||||
void ESPHomeOTAComponent::log_remote_closed_(const LogString *during) {
|
||||
@@ -528,8 +547,26 @@ void ESPHomeOTAComponent::yield_and_feed_watchdog_() {
|
||||
void ESPHomeOTAComponent::log_auth_warning_(const LogString *msg) { ESP_LOGW(TAG, "Auth: %s", LOG_STR_ARG(msg)); }
|
||||
|
||||
bool ESPHomeOTAComponent::select_auth_type_() {
|
||||
#ifdef USE_OTA_SHA256
|
||||
bool client_supports_sha256 = (this->ota_features_ & FEATURE_SUPPORTS_SHA256_AUTH) != 0;
|
||||
|
||||
#ifdef ALLOW_OTA_DOWNGRADE_MD5
|
||||
// Allow fallback to MD5 if client doesn't support SHA256
|
||||
if (client_supports_sha256) {
|
||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_SHA256_AUTH;
|
||||
return true;
|
||||
}
|
||||
#ifdef USE_OTA_MD5
|
||||
this->log_auth_warning_(LOG_STR("Using deprecated MD5"));
|
||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_AUTH;
|
||||
return true;
|
||||
#else
|
||||
this->log_auth_warning_(LOG_STR("SHA256 required"));
|
||||
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
||||
return false;
|
||||
#endif // USE_OTA_MD5
|
||||
|
||||
#else // !ALLOW_OTA_DOWNGRADE_MD5
|
||||
// Require SHA256
|
||||
if (!client_supports_sha256) {
|
||||
this->log_auth_warning_(LOG_STR("SHA256 required"));
|
||||
@@ -538,6 +575,20 @@ bool ESPHomeOTAComponent::select_auth_type_() {
|
||||
}
|
||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_SHA256_AUTH;
|
||||
return true;
|
||||
#endif // ALLOW_OTA_DOWNGRADE_MD5
|
||||
|
||||
#else // !USE_OTA_SHA256
|
||||
#ifdef USE_OTA_MD5
|
||||
// Only MD5 available
|
||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_AUTH;
|
||||
return true;
|
||||
#else
|
||||
// No auth methods available
|
||||
this->log_auth_warning_(LOG_STR("No auth methods available"));
|
||||
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
||||
return false;
|
||||
#endif // USE_OTA_MD5
|
||||
#endif // USE_OTA_SHA256
|
||||
}
|
||||
|
||||
bool ESPHomeOTAComponent::handle_auth_send_() {
|
||||
@@ -561,12 +612,31 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
|
||||
// [1+hex_size...1+2*hex_size-1]: cnonce (hex_size bytes) - client's nonce
|
||||
// [1+2*hex_size...1+3*hex_size-1]: response (hex_size bytes) - client's hash
|
||||
|
||||
// CRITICAL ESP32-S3 HARDWARE SHA ACCELERATION: Hash object must stay in same stack frame
|
||||
// (no passing to other functions). All hash operations must happen in this function.
|
||||
sha256::SHA256 hasher;
|
||||
// Declare both hash objects in same stack frame, use pointer to select.
|
||||
// NOTE: Both objects are declared here even though only one is used. This is REQUIRED for ESP32-S3
|
||||
// hardware SHA acceleration - the object must exist in this stack frame for all operations.
|
||||
// Do NOT try to "optimize" by creating the object inside the if block, as it would go out of scope.
|
||||
#ifdef USE_OTA_SHA256
|
||||
sha256::SHA256 sha_hasher;
|
||||
#endif
|
||||
#ifdef USE_OTA_MD5
|
||||
md5::MD5Digest md5_hasher;
|
||||
#endif
|
||||
HashBase *hasher = nullptr;
|
||||
|
||||
const size_t hex_size = hasher.get_size() * 2;
|
||||
const size_t nonce_len = hasher.get_size() / 4;
|
||||
#ifdef USE_OTA_SHA256
|
||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
||||
hasher = &sha_hasher;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_OTA_MD5
|
||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_AUTH) {
|
||||
hasher = &md5_hasher;
|
||||
}
|
||||
#endif
|
||||
|
||||
const size_t hex_size = hasher->get_size() * 2;
|
||||
const size_t nonce_len = hasher->get_size() / 4;
|
||||
const size_t auth_buf_size = 1 + 3 * hex_size;
|
||||
this->auth_buf_ = std::make_unique<uint8_t[]>(auth_buf_size);
|
||||
this->auth_buf_pos_ = 0;
|
||||
@@ -578,17 +648,22 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasher.init();
|
||||
hasher.add(buf, nonce_len);
|
||||
hasher.calculate();
|
||||
hasher->init();
|
||||
hasher->add(buf, nonce_len);
|
||||
hasher->calculate();
|
||||
this->auth_buf_[0] = this->auth_type_;
|
||||
hasher.get_hex(buf);
|
||||
hasher->get_hex(buf);
|
||||
|
||||
ESP_LOGV(TAG, "Auth: Nonce is %.*s", hex_size, buf);
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char log_buf[65]; // Fixed size for SHA256 hex (64) + null, works for MD5 (32) too
|
||||
memcpy(log_buf, buf, hex_size);
|
||||
log_buf[hex_size] = '\0';
|
||||
ESP_LOGV(TAG, "Auth: Nonce is %s", log_buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Try to write auth_type + nonce
|
||||
constexpr size_t hex_size = SHA256_HEX_SIZE;
|
||||
size_t hex_size = this->get_auth_hex_size_();
|
||||
const size_t to_write = 1 + hex_size;
|
||||
size_t remaining = to_write - this->auth_buf_pos_;
|
||||
|
||||
@@ -610,7 +685,7 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
|
||||
}
|
||||
|
||||
bool ESPHomeOTAComponent::handle_auth_read_() {
|
||||
constexpr size_t hex_size = SHA256_HEX_SIZE;
|
||||
size_t hex_size = this->get_auth_hex_size_();
|
||||
const size_t to_read = hex_size * 2; // CNonce + Response
|
||||
|
||||
// Try to read remaining bytes (CNonce + Response)
|
||||
@@ -635,25 +710,55 @@ bool ESPHomeOTAComponent::handle_auth_read_() {
|
||||
const char *cnonce = nonce + hex_size;
|
||||
const char *response = cnonce + hex_size;
|
||||
|
||||
// CRITICAL ESP32-S3 HARDWARE SHA ACCELERATION: Hash object must stay in same stack frame
|
||||
// (no passing to other functions). All hash operations must happen in this function.
|
||||
sha256::SHA256 hasher;
|
||||
|
||||
hasher.init();
|
||||
hasher.add(this->password_.c_str(), this->password_.length());
|
||||
hasher.add(nonce, hex_size * 2); // Add both nonce and cnonce (contiguous in buffer)
|
||||
hasher.calculate();
|
||||
|
||||
ESP_LOGV(TAG, "Auth: CNonce is %.*s", hex_size, cnonce);
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char computed_hash[SHA256_HEX_SIZE + 1]; // Buffer for hex-encoded hash (max expected length + null terminator)
|
||||
hasher.get_hex(computed_hash);
|
||||
ESP_LOGV(TAG, "Auth: Result is %.*s", hex_size, computed_hash);
|
||||
// CRITICAL ESP32-S3: Hash objects must stay in same stack frame (no passing to other functions).
|
||||
// Declare both hash objects in same stack frame, use pointer to select.
|
||||
// NOTE: Both objects are declared here even though only one is used. This is REQUIRED for ESP32-S3
|
||||
// hardware SHA acceleration - the object must exist in this stack frame for all operations.
|
||||
// Do NOT try to "optimize" by creating the object inside the if block, as it would go out of scope.
|
||||
#ifdef USE_OTA_SHA256
|
||||
sha256::SHA256 sha_hasher;
|
||||
#endif
|
||||
#ifdef USE_OTA_MD5
|
||||
md5::MD5Digest md5_hasher;
|
||||
#endif
|
||||
HashBase *hasher = nullptr;
|
||||
|
||||
#ifdef USE_OTA_SHA256
|
||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
||||
hasher = &sha_hasher;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_OTA_MD5
|
||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_AUTH) {
|
||||
hasher = &md5_hasher;
|
||||
}
|
||||
#endif
|
||||
|
||||
hasher->init();
|
||||
hasher->add(this->password_.c_str(), this->password_.length());
|
||||
hasher->add(nonce, hex_size * 2); // Add both nonce and cnonce (contiguous in buffer)
|
||||
hasher->calculate();
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char log_buf[65]; // Fixed size for SHA256 hex (64) + null, works for MD5 (32) too
|
||||
// Log CNonce
|
||||
memcpy(log_buf, cnonce, hex_size);
|
||||
log_buf[hex_size] = '\0';
|
||||
ESP_LOGV(TAG, "Auth: CNonce is %s", log_buf);
|
||||
|
||||
// Log computed hash
|
||||
hasher->get_hex(log_buf);
|
||||
log_buf[hex_size] = '\0';
|
||||
ESP_LOGV(TAG, "Auth: Result is %s", log_buf);
|
||||
|
||||
// Log received response
|
||||
memcpy(log_buf, response, hex_size);
|
||||
log_buf[hex_size] = '\0';
|
||||
ESP_LOGV(TAG, "Auth: Response is %s", log_buf);
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Auth: Response is %.*s", hex_size, response);
|
||||
|
||||
// Compare response
|
||||
bool matches = hasher.equals_hex(response);
|
||||
bool matches = hasher->equals_hex(response);
|
||||
|
||||
if (!matches) {
|
||||
this->log_auth_warning_(LOG_STR("Password mismatch"));
|
||||
@@ -667,6 +772,21 @@ bool ESPHomeOTAComponent::handle_auth_read_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t ESPHomeOTAComponent::get_auth_hex_size_() const {
|
||||
#ifdef USE_OTA_SHA256
|
||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
||||
return SHA256_HEX_SIZE;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_OTA_MD5
|
||||
return MD5_HEX_SIZE;
|
||||
#else
|
||||
#ifndef USE_OTA_SHA256
|
||||
#error "Either USE_OTA_MD5 or USE_OTA_SHA256 must be defined when USE_OTA_PASSWORD is enabled"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void ESPHomeOTAComponent::cleanup_auth_() {
|
||||
this->auth_buf_ = nullptr;
|
||||
this->auth_buf_pos_ = 0;
|
||||
|
||||
@@ -44,10 +44,10 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
|
||||
void handle_handshake_();
|
||||
void handle_data_();
|
||||
#ifdef USE_OTA_PASSWORD
|
||||
static constexpr size_t SHA256_HEX_SIZE = 64; // SHA256 hash as hex string (32 bytes * 2)
|
||||
bool handle_auth_send_();
|
||||
bool handle_auth_read_();
|
||||
bool select_auth_type_();
|
||||
size_t get_auth_hex_size_() const;
|
||||
void cleanup_auth_();
|
||||
void log_auth_warning_(const LogString *msg);
|
||||
#endif // USE_OTA_PASSWORD
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <esp_event.h>
|
||||
@@ -300,10 +299,9 @@ void ESPNowComponent::loop() {
|
||||
// Intentionally left as if instead of else in case the peer is added above
|
||||
if (esp_now_is_peer_exist(info.src_addr)) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(ESP_NOW_MAX_DATA_LEN)];
|
||||
ESP_LOGV(TAG, "<<< [%s -> %s] %s", format_mac_address_pretty(info.src_addr).c_str(),
|
||||
format_mac_address_pretty(info.des_addr).c_str(),
|
||||
format_hex_pretty_to(hex_buf, packet->packet_.receive.data, packet->packet_.receive.size));
|
||||
format_hex_pretty(packet->packet_.receive.data, packet->packet_.receive.size).c_str());
|
||||
#endif
|
||||
if (memcmp(info.des_addr, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
|
||||
for (auto *handler : this->broadcasted_handlers_) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "ethernet_component.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
@@ -40,9 +39,6 @@ namespace ethernet {
|
||||
|
||||
static const char *const TAG = "ethernet";
|
||||
|
||||
// PHY register size for hex logging
|
||||
static constexpr size_t PHY_REG_SIZE = 2;
|
||||
|
||||
EthernetComponent *global_eth_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
void EthernetComponent::log_error_and_mark_failed_(esp_err_t err, const char *message) {
|
||||
@@ -648,12 +644,6 @@ void EthernetComponent::dump_connect_params_() {
|
||||
dns_ip2 = dns_getserver(1);
|
||||
}
|
||||
|
||||
// Use stack buffers for IP address formatting to avoid heap allocations
|
||||
char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
char subnet_buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
char gateway_buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
char dns1_buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
char dns2_buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" IP Address: %s\n"
|
||||
" Hostname: '%s'\n"
|
||||
@@ -661,9 +651,9 @@ void EthernetComponent::dump_connect_params_() {
|
||||
" Gateway: %s\n"
|
||||
" DNS1: %s\n"
|
||||
" DNS2: %s",
|
||||
network::IPAddress(&ip.ip).str_to(ip_buf), App.get_name().c_str(),
|
||||
network::IPAddress(&ip.netmask).str_to(subnet_buf), network::IPAddress(&ip.gw).str_to(gateway_buf),
|
||||
network::IPAddress(dns_ip1).str_to(dns1_buf), network::IPAddress(dns_ip2).str_to(dns2_buf));
|
||||
network::IPAddress(&ip.ip).str().c_str(), App.get_name().c_str(),
|
||||
network::IPAddress(&ip.netmask).str().c_str(), network::IPAddress(&ip.gw).str().c_str(),
|
||||
network::IPAddress(dns_ip1).str().c_str(), network::IPAddress(dns_ip2).str().c_str());
|
||||
|
||||
#if USE_NETWORK_IPV6
|
||||
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
|
||||
@@ -675,13 +665,12 @@ void EthernetComponent::dump_connect_params_() {
|
||||
}
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" MAC Address: %s\n"
|
||||
" Is Full Duplex: %s\n"
|
||||
" Link Speed: %u",
|
||||
this->get_eth_mac_address_pretty_into_buffer(mac_buf),
|
||||
YESNO(this->get_duplex_mode() == ETH_DUPLEX_FULL), this->get_link_speed() == ETH_SPEED_100M ? 100 : 10);
|
||||
this->get_eth_mac_address_pretty().c_str(), YESNO(this->get_duplex_mode() == ETH_DUPLEX_FULL),
|
||||
this->get_link_speed() == ETH_SPEED_100M ? 100 : 10);
|
||||
}
|
||||
|
||||
#ifdef USE_ETHERNET_SPI
|
||||
@@ -722,16 +711,11 @@ void EthernetComponent::get_eth_mac_address_raw(uint8_t *mac) {
|
||||
}
|
||||
|
||||
std::string EthernetComponent::get_eth_mac_address_pretty() {
|
||||
char buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||
return std::string(this->get_eth_mac_address_pretty_into_buffer(buf));
|
||||
}
|
||||
|
||||
const char *EthernetComponent::get_eth_mac_address_pretty_into_buffer(
|
||||
std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf) {
|
||||
uint8_t mac[6];
|
||||
get_eth_mac_address_raw(mac);
|
||||
format_mac_addr_upper(mac, buf.data());
|
||||
return buf.data();
|
||||
char buf[18];
|
||||
format_mac_addr_upper(mac, buf);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
eth_duplex_t EthernetComponent::get_duplex_mode() {
|
||||
@@ -777,10 +761,7 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
|
||||
uint32_t phy_control_2;
|
||||
err = mac->read_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, &(phy_control_2));
|
||||
ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(PHY_REG_SIZE)];
|
||||
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty_to(hex_buf, (uint8_t *) &phy_control_2, PHY_REG_SIZE));
|
||||
#endif
|
||||
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
|
||||
|
||||
/*
|
||||
* Bit 7 is `RMII Reference Clock Select`. Default is `0`.
|
||||
@@ -797,10 +778,7 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
|
||||
ESPHL_ERROR_CHECK(err, "Write PHY Control 2 failed");
|
||||
err = mac->read_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, &(phy_control_2));
|
||||
ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s",
|
||||
format_hex_pretty_to(hex_buf, (uint8_t *) &phy_control_2, PHY_REG_SIZE));
|
||||
#endif
|
||||
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
|
||||
}
|
||||
}
|
||||
#endif // USE_ETHERNET_KSZ8081
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
@@ -94,7 +93,6 @@ class EthernetComponent : public Component {
|
||||
void set_use_address(const char *use_address);
|
||||
void get_eth_mac_address_raw(uint8_t *mac);
|
||||
std::string get_eth_mac_address_pretty();
|
||||
const char *get_eth_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
|
||||
eth_duplex_t get_duplex_mode();
|
||||
eth_speed_t get_link_speed();
|
||||
bool powerdown();
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome::ethernet_info {
|
||||
namespace esphome {
|
||||
namespace ethernet_info {
|
||||
|
||||
static const char *const TAG = "ethernet_info";
|
||||
|
||||
@@ -11,6 +12,7 @@ void IPAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo IP
|
||||
void DNSAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo DNS Address", this); }
|
||||
void MACAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo MAC Address", this); }
|
||||
|
||||
} // namespace esphome::ethernet_info
|
||||
} // namespace ethernet_info
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome::ethernet_info {
|
||||
namespace esphome {
|
||||
namespace ethernet_info {
|
||||
|
||||
class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextSensor {
|
||||
public:
|
||||
@@ -39,27 +40,21 @@ class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextS
|
||||
class DNSAddressEthernetInfo : public PollingComponent, public text_sensor::TextSensor {
|
||||
public:
|
||||
void update() override {
|
||||
auto dns1 = ethernet::global_eth_component->get_dns_address(0);
|
||||
auto dns2 = ethernet::global_eth_component->get_dns_address(1);
|
||||
auto dns_one = ethernet::global_eth_component->get_dns_address(0);
|
||||
auto dns_two = ethernet::global_eth_component->get_dns_address(1);
|
||||
|
||||
if (dns1 != this->last_dns1_ || dns2 != this->last_dns2_) {
|
||||
this->last_dns1_ = dns1;
|
||||
this->last_dns2_ = dns2;
|
||||
// IP_ADDRESS_BUFFER_SIZE (40) = max IP (39) + null; space reuses first null's slot
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE * 2];
|
||||
dns1.str_to(buf);
|
||||
size_t len1 = strlen(buf);
|
||||
buf[len1] = ' ';
|
||||
dns2.str_to(buf + len1 + 1);
|
||||
this->publish_state(buf);
|
||||
std::string dns_results = dns_one.str() + " " + dns_two.str();
|
||||
|
||||
if (dns_results != this->last_results_) {
|
||||
this->last_results_ = dns_results;
|
||||
this->publish_state(dns_results);
|
||||
}
|
||||
}
|
||||
float get_setup_priority() const override { return setup_priority::ETHERNET; }
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
network::IPAddress last_dns1_;
|
||||
network::IPAddress last_dns2_;
|
||||
std::string last_results_;
|
||||
};
|
||||
|
||||
class MACAddressEthernetInfo : public Component, public text_sensor::TextSensor {
|
||||
@@ -69,6 +64,7 @@ class MACAddressEthernetInfo : public Component, public text_sensor::TextSensor
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::ethernet_info
|
||||
} // namespace ethernet_info
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -8,9 +8,6 @@ namespace esphome::hlk_fm22x {
|
||||
|
||||
static const char *const TAG = "hlk_fm22x";
|
||||
|
||||
// Maximum response size is 36 bytes (VERIFY reply: face_id + 32-byte name)
|
||||
static constexpr size_t HLK_FM22X_MAX_RESPONSE_SIZE = 36;
|
||||
|
||||
void HlkFm22xComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up HLK-FM22X...");
|
||||
this->set_enrolling_(false);
|
||||
@@ -145,10 +142,7 @@ void HlkFm22xComponent::recv_command_() {
|
||||
data.push_back(byte);
|
||||
}
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(HLK_FM22X_MAX_RESPONSE_SIZE)];
|
||||
ESP_LOGV(TAG, "Recv type: 0x%.2X, data: %s", response_type, format_hex_pretty_to(hex_buf, data.data(), data.size()));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Recv type: 0x%.2X, data: %s", response_type, format_hex_pretty(data).c_str());
|
||||
|
||||
byte = this->read();
|
||||
if (byte != checksum) {
|
||||
|
||||
@@ -34,9 +34,9 @@ void HLW8012Component::setup() {
|
||||
}
|
||||
void HLW8012Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "HLW8012:");
|
||||
LOG_PIN(" SEL Pin: ", this->sel_pin_);
|
||||
LOG_PIN(" CF Pin: ", this->cf_pin_);
|
||||
LOG_PIN(" CF1 Pin: ", this->cf1_pin_);
|
||||
LOG_PIN(" SEL Pin: ", this->sel_pin_)
|
||||
LOG_PIN(" CF Pin: ", this->cf_pin_)
|
||||
LOG_PIN(" CF1 Pin: ", this->cf1_pin_)
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Change measurement mode every %" PRIu32 "\n"
|
||||
" Current resistor: %.1f mΩ\n"
|
||||
|
||||
@@ -30,7 +30,7 @@ class HmacMD5 {
|
||||
void get_bytes(uint8_t *output);
|
||||
|
||||
/// Retrieve the HMAC-MD5 digest as hex characters.
|
||||
/// The output must be able to hold 33 bytes or more (32 hex chars + null terminator).
|
||||
/// The output must be able to hold 32 bytes or more.
|
||||
void get_hex(char *output);
|
||||
|
||||
/// Compare the digest against a provided byte-encoded digest (16 bytes).
|
||||
|
||||
@@ -35,7 +35,7 @@ class HmacSHA256 {
|
||||
void get_bytes(uint8_t *output);
|
||||
|
||||
/// Retrieve the HMAC-SHA256 digest as hex characters.
|
||||
/// The output must be able to hold 65 bytes or more (64 hex chars + null terminator).
|
||||
/// The output must be able to hold 64 bytes or more.
|
||||
void get_hex(char *output);
|
||||
|
||||
/// Compare the digest against a provided byte-encoded digest (32 bytes).
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "homeassistant_binary_sensor.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace homeassistant {
|
||||
@@ -9,30 +8,31 @@ namespace homeassistant {
|
||||
static const char *const TAG = "homeassistant.binary_sensor";
|
||||
|
||||
void HomeassistantBinarySensor::setup() {
|
||||
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, this->attribute_, [this](StringRef state) {
|
||||
auto val = parse_on_off(state.c_str());
|
||||
switch (val) {
|
||||
case PARSE_NONE:
|
||||
case PARSE_TOGGLE:
|
||||
ESP_LOGW(TAG, "Can't convert '%s' to binary state!", state.c_str());
|
||||
break;
|
||||
case PARSE_ON:
|
||||
case PARSE_OFF:
|
||||
bool new_state = val == PARSE_ON;
|
||||
if (this->attribute_ != nullptr) {
|
||||
ESP_LOGD(TAG, "'%s::%s': Got attribute state %s", this->entity_id_, this->attribute_, ONOFF(new_state));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_, ONOFF(new_state));
|
||||
api::global_api_server->subscribe_home_assistant_state(
|
||||
this->entity_id_, this->attribute_, [this](const std::string &state) {
|
||||
auto val = parse_on_off(state.c_str());
|
||||
switch (val) {
|
||||
case PARSE_NONE:
|
||||
case PARSE_TOGGLE:
|
||||
ESP_LOGW(TAG, "Can't convert '%s' to binary state!", state.c_str());
|
||||
break;
|
||||
case PARSE_ON:
|
||||
case PARSE_OFF:
|
||||
bool new_state = val == PARSE_ON;
|
||||
if (this->attribute_ != nullptr) {
|
||||
ESP_LOGD(TAG, "'%s::%s': Got attribute state %s", this->entity_id_, this->attribute_, ONOFF(new_state));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_, ONOFF(new_state));
|
||||
}
|
||||
if (this->initial_) {
|
||||
this->publish_initial_state(new_state);
|
||||
} else {
|
||||
this->publish_state(new_state);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (this->initial_) {
|
||||
this->publish_initial_state(new_state);
|
||||
} else {
|
||||
this->publish_state(new_state);
|
||||
}
|
||||
break;
|
||||
}
|
||||
this->initial_ = false;
|
||||
});
|
||||
this->initial_ = false;
|
||||
});
|
||||
}
|
||||
void HomeassistantBinarySensor::dump_config() {
|
||||
LOG_BINARY_SENSOR("", "Homeassistant Binary Sensor", this);
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace homeassistant {
|
||||
|
||||
static const char *const TAG = "homeassistant.number";
|
||||
|
||||
void HomeassistantNumber::state_changed_(StringRef state) {
|
||||
auto number_value = parse_number<float>(state.c_str());
|
||||
void HomeassistantNumber::state_changed_(const std::string &state) {
|
||||
auto number_value = parse_number<float>(state);
|
||||
if (!number_value.has_value()) {
|
||||
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_, state.c_str());
|
||||
this->publish_state(NAN);
|
||||
@@ -24,8 +23,8 @@ void HomeassistantNumber::state_changed_(StringRef state) {
|
||||
this->publish_state(number_value.value());
|
||||
}
|
||||
|
||||
void HomeassistantNumber::min_retrieved_(StringRef min) {
|
||||
auto min_value = parse_number<float>(min.c_str());
|
||||
void HomeassistantNumber::min_retrieved_(const std::string &min) {
|
||||
auto min_value = parse_number<float>(min);
|
||||
if (!min_value.has_value()) {
|
||||
ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_, min.c_str());
|
||||
return;
|
||||
@@ -34,8 +33,8 @@ void HomeassistantNumber::min_retrieved_(StringRef min) {
|
||||
this->traits.set_min_value(min_value.value());
|
||||
}
|
||||
|
||||
void HomeassistantNumber::max_retrieved_(StringRef max) {
|
||||
auto max_value = parse_number<float>(max.c_str());
|
||||
void HomeassistantNumber::max_retrieved_(const std::string &max) {
|
||||
auto max_value = parse_number<float>(max);
|
||||
if (!max_value.has_value()) {
|
||||
ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_, max.c_str());
|
||||
return;
|
||||
@@ -44,8 +43,8 @@ void HomeassistantNumber::max_retrieved_(StringRef max) {
|
||||
this->traits.set_max_value(max_value.value());
|
||||
}
|
||||
|
||||
void HomeassistantNumber::step_retrieved_(StringRef step) {
|
||||
auto step_value = parse_number<float>(step.c_str());
|
||||
void HomeassistantNumber::step_retrieved_(const std::string &step) {
|
||||
auto step_value = parse_number<float>(step);
|
||||
if (!step_value.has_value()) {
|
||||
ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_, step.c_str());
|
||||
return;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace homeassistant {
|
||||
@@ -16,10 +18,10 @@ class HomeassistantNumber : public number::Number, public Component {
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void state_changed_(StringRef state);
|
||||
void min_retrieved_(StringRef min);
|
||||
void max_retrieved_(StringRef max);
|
||||
void step_retrieved_(StringRef step);
|
||||
void state_changed_(const std::string &state);
|
||||
void min_retrieved_(const std::string &min);
|
||||
void max_retrieved_(const std::string &max);
|
||||
void step_retrieved_(const std::string &step);
|
||||
|
||||
void control(float value) override;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "homeassistant_sensor.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace homeassistant {
|
||||
@@ -9,21 +8,22 @@ namespace homeassistant {
|
||||
static const char *const TAG = "homeassistant.sensor";
|
||||
|
||||
void HomeassistantSensor::setup() {
|
||||
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, this->attribute_, [this](StringRef state) {
|
||||
auto val = parse_number<float>(state.c_str());
|
||||
if (!val.has_value()) {
|
||||
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_, state.c_str());
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
api::global_api_server->subscribe_home_assistant_state(
|
||||
this->entity_id_, this->attribute_, [this](const std::string &state) {
|
||||
auto val = parse_number<float>(state);
|
||||
if (!val.has_value()) {
|
||||
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_, state.c_str());
|
||||
this->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->attribute_ != nullptr) {
|
||||
ESP_LOGD(TAG, "'%s::%s': Got attribute state %.2f", this->entity_id_, this->attribute_, *val);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "'%s': Got state %.2f", this->entity_id_, *val);
|
||||
}
|
||||
this->publish_state(*val);
|
||||
});
|
||||
if (this->attribute_ != nullptr) {
|
||||
ESP_LOGD(TAG, "'%s::%s': Got attribute state %.2f", this->entity_id_, this->attribute_, *val);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "'%s': Got state %.2f", this->entity_id_, *val);
|
||||
}
|
||||
this->publish_state(*val);
|
||||
});
|
||||
}
|
||||
void HomeassistantSensor::dump_config() {
|
||||
LOG_SENSOR("", "Homeassistant Sensor", this);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "homeassistant_switch.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace homeassistant {
|
||||
@@ -11,7 +10,7 @@ static const char *const TAG = "homeassistant.switch";
|
||||
using namespace esphome::switch_;
|
||||
|
||||
void HomeassistantSwitch::setup() {
|
||||
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, nullptr, [this](StringRef state) {
|
||||
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, nullptr, [this](const std::string &state) {
|
||||
auto val = parse_on_off(state.c_str());
|
||||
switch (val) {
|
||||
case PARSE_NONE:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "homeassistant_text_sensor.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace homeassistant {
|
||||
@@ -9,14 +8,15 @@ namespace homeassistant {
|
||||
static const char *const TAG = "homeassistant.text_sensor";
|
||||
|
||||
void HomeassistantTextSensor::setup() {
|
||||
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, this->attribute_, [this](StringRef state) {
|
||||
if (this->attribute_ != nullptr) {
|
||||
ESP_LOGD(TAG, "'%s::%s': Got attribute state '%s'", this->entity_id_, this->attribute_, state.c_str());
|
||||
} else {
|
||||
ESP_LOGD(TAG, "'%s': Got state '%s'", this->entity_id_, state.c_str());
|
||||
}
|
||||
this->publish_state(state.str());
|
||||
});
|
||||
api::global_api_server->subscribe_home_assistant_state(
|
||||
this->entity_id_, this->attribute_, [this](const std::string &state) {
|
||||
if (this->attribute_ != nullptr) {
|
||||
ESP_LOGD(TAG, "'%s::%s': Got attribute state '%s'", this->entity_id_, this->attribute_, state.c_str());
|
||||
} else {
|
||||
ESP_LOGD(TAG, "'%s': Got state '%s'", this->entity_id_, state.c_str());
|
||||
}
|
||||
this->publish_state(state);
|
||||
});
|
||||
}
|
||||
void HomeassistantTextSensor::dump_config() {
|
||||
LOG_TEXT_SENSOR("", "Homeassistant Text Sensor", this);
|
||||
|
||||
@@ -25,7 +25,11 @@ void HostGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::Interr
|
||||
}
|
||||
void HostGPIOPin::pin_mode(gpio::Flags flags) { ESP_LOGD(TAG, "Setting pin %d mode to %02X", pin_, (uint32_t) flags); }
|
||||
|
||||
size_t HostGPIOPin::dump_summary(char *buffer, size_t len) const { return snprintf(buffer, len, "GPIO%u", this->pin_); }
|
||||
std::string HostGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%u", pin_);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool HostGPIOPin::digital_read() { return inverted_; }
|
||||
void HostGPIOPin::digital_write(bool value) {
|
||||
|
||||
@@ -17,7 +17,7 @@ class HostGPIOPin : public InternalGPIOPin {
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
|
||||
@@ -7,8 +7,6 @@ namespace hte501 {
|
||||
|
||||
static const char *const TAG = "hte501";
|
||||
|
||||
static constexpr size_t HTE501_SERIAL_NUMBER_SIZE = 7;
|
||||
|
||||
void HTE501Component::setup() {
|
||||
uint8_t address[] = {0x70, 0x29};
|
||||
uint8_t identification[9];
|
||||
@@ -18,10 +16,7 @@ void HTE501Component::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char serial_hex[format_hex_size(HTE501_SERIAL_NUMBER_SIZE)];
|
||||
#endif
|
||||
ESP_LOGV(TAG, " Serial Number: 0x%s", format_hex_to(serial_hex, identification, HTE501_SERIAL_NUMBER_SIZE));
|
||||
ESP_LOGV(TAG, " Serial Number: 0x%s", format_hex(identification + 0, 7).c_str());
|
||||
}
|
||||
|
||||
void HTE501Component::dump_config() {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "esphome/components/md5/md5.h"
|
||||
#include "esphome/components/watchdog/watchdog.h"
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#include "esphome/components/ota/ota_backend_esp8266.h"
|
||||
#include "esphome/components/ota/ota_backend_arduino_esp8266.h"
|
||||
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
|
||||
#include "esphome/components/ota/ota_backend_esp_idf.h"
|
||||
|
||||
|
||||
@@ -111,9 +111,6 @@ void HOT HUB75Display::draw_pixel_at(int x, int y, Color color) {
|
||||
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
return;
|
||||
|
||||
driver_->set_pixel(x, y, color.r, color.g, color.b);
|
||||
App.feed_wdt();
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ namespace i2c {
|
||||
|
||||
static const char *const TAG = "i2c.arduino";
|
||||
|
||||
// Maximum bytes to log in hex format (truncates larger transfers)
|
||||
static constexpr size_t I2C_MAX_LOG_BYTES = 32;
|
||||
|
||||
void ArduinoI2CBus::setup() {
|
||||
recover_();
|
||||
|
||||
@@ -110,10 +107,7 @@ ErrorCode ArduinoI2CBus::write_readv(uint8_t address, const uint8_t *write_buffe
|
||||
return ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(I2C_MAX_LOG_BYTES)];
|
||||
ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty_to(hex_buf, write_buffer, write_count));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str());
|
||||
|
||||
uint8_t status = 0;
|
||||
if (write_count != 0 || read_count == 0) {
|
||||
|
||||
@@ -15,9 +15,6 @@ namespace i2c {
|
||||
|
||||
static const char *const TAG = "i2c.idf";
|
||||
|
||||
// Maximum bytes to log in hex format (truncates larger transfers)
|
||||
static constexpr size_t I2C_MAX_LOG_BYTES = 32;
|
||||
|
||||
void IDFI2CBus::setup() {
|
||||
static i2c_port_t next_hp_port = I2C_NUM_0;
|
||||
#if SOC_LP_I2C_SUPPORTED
|
||||
@@ -150,10 +147,7 @@ ErrorCode IDFI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, s
|
||||
jobs[num_jobs++].write.total_bytes = 1;
|
||||
} else {
|
||||
if (write_count != 0) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(I2C_MAX_LOG_BYTES)];
|
||||
ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty_to(hex_buf, write_buffer, write_count));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str());
|
||||
jobs[num_jobs++].command = I2C_MASTER_CMD_START;
|
||||
jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
|
||||
jobs[num_jobs].write.ack_check = true;
|
||||
|
||||
@@ -8,9 +8,8 @@ extern "C" {
|
||||
uint8_t temprature_sens_read();
|
||||
}
|
||||
#elif defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32C3) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C5) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || defined(USE_ESP32_VARIANT_ESP32H2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
#include "driver/temperature_sensor.h"
|
||||
#endif // USE_ESP32_VARIANT
|
||||
#endif // USE_ESP32
|
||||
@@ -28,9 +27,9 @@ namespace internal_temperature {
|
||||
|
||||
static const char *const TAG = "internal_temperature";
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C5) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || defined(USE_ESP32_VARIANT_ESP32H2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C61) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
static temperature_sensor_handle_t tsensNew = NULL;
|
||||
#endif // USE_ESP32_VARIANT
|
||||
#endif // USE_ESP32
|
||||
@@ -45,9 +44,8 @@ void InternalTemperatureSensor::update() {
|
||||
temperature = (raw - 32) / 1.8f;
|
||||
success = (raw != 128);
|
||||
#elif defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32C3) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C5) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || defined(USE_ESP32_VARIANT_ESP32H2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
esp_err_t result = temperature_sensor_get_celsius(tsensNew, &temperature);
|
||||
success = (result == ESP_OK);
|
||||
if (!success) {
|
||||
@@ -83,9 +81,9 @@ void InternalTemperatureSensor::update() {
|
||||
|
||||
void InternalTemperatureSensor::setup() {
|
||||
#ifdef USE_ESP32
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C5) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61) || defined(USE_ESP32_VARIANT_ESP32H2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C61) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32P4) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
temperature_sensor_config_t tsens_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
|
||||
|
||||
esp_err_t result = temperature_sensor_install(&tsens_config, &tsensNew);
|
||||
|
||||
@@ -39,9 +39,7 @@ void Jsnsr04tComponent::check_buffer_() {
|
||||
ESP_LOGV(TAG, "Distance from sensor: %umm, %.3fm", distance, meters);
|
||||
this->publish_state(meters);
|
||||
} else {
|
||||
char hex_buf[format_hex_pretty_size(4)];
|
||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s",
|
||||
format_hex_pretty_to(hex_buf, this->buffer_.data(), this->buffer_.size()));
|
||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "checksum failed: %02x != %02x", checksum, this->buffer_[3]);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "kuntze.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
@@ -11,17 +10,11 @@ static const char *const TAG = "kuntze";
|
||||
static const uint8_t CMD_READ_REG = 0x03;
|
||||
static const uint16_t REGISTER[] = {4136, 4160, 4680, 6000, 4688, 4728, 5832};
|
||||
|
||||
// Maximum bytes to log for Modbus responses (2 registers = 4 bytes, plus byte count = 5 bytes)
|
||||
static constexpr size_t KUNTZE_MAX_LOG_BYTES = 8;
|
||||
|
||||
void Kuntze::on_modbus_data(const std::vector<uint8_t> &data) {
|
||||
auto get_16bit = [&](int i) -> uint16_t { return (uint16_t(data[i * 2]) << 8) | uint16_t(data[i * 2 + 1]); };
|
||||
|
||||
this->waiting_ = false;
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(KUNTZE_MAX_LOG_BYTES)];
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Data: %s", format_hex_pretty_to(hex_buf, data.data(), data.size()));
|
||||
ESP_LOGV(TAG, "Data: %s", format_hex_pretty(data).c_str());
|
||||
|
||||
float value = (float) get_16bit(0);
|
||||
for (int i = 0; i < data[3]; i++)
|
||||
|
||||
@@ -413,8 +413,7 @@ bool LD2410Component::handle_ack_data_() {
|
||||
return true;
|
||||
}
|
||||
if (!ld2410::validate_header_footer(CMD_FRAME_HEADER, this->buffer_data_)) {
|
||||
char hex_buf[format_hex_pretty_size(HEADER_FOOTER_SIZE)];
|
||||
ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, HEADER_FOOTER_SIZE));
|
||||
ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty(this->buffer_data_, HEADER_FOOTER_SIZE).c_str());
|
||||
return true;
|
||||
}
|
||||
if (this->buffer_data_[COMMAND_STATUS] != 0x01) {
|
||||
@@ -598,17 +597,11 @@ void LD2410Component::readline_(int readch) {
|
||||
return; // Not enough data to process yet
|
||||
}
|
||||
if (ld2410::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(MAX_LINE_LENGTH)];
|
||||
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, this->buffer_pos_));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
||||
this->handle_periodic_data_();
|
||||
this->buffer_pos_ = 0; // Reset position index for next message
|
||||
} else if (ld2410::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(MAX_LINE_LENGTH)];
|
||||
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, this->buffer_pos_));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
||||
if (this->handle_ack_data_()) {
|
||||
this->buffer_pos_ = 0; // Reset position index for next message
|
||||
} else {
|
||||
|
||||
@@ -457,8 +457,7 @@ bool LD2412Component::handle_ack_data_() {
|
||||
return true;
|
||||
}
|
||||
if (!ld2412::validate_header_footer(CMD_FRAME_HEADER, this->buffer_data_)) {
|
||||
char hex_buf[format_hex_pretty_size(HEADER_FOOTER_SIZE)];
|
||||
ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, HEADER_FOOTER_SIZE));
|
||||
ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty(this->buffer_data_, HEADER_FOOTER_SIZE).c_str());
|
||||
return true;
|
||||
}
|
||||
if (this->buffer_data_[COMMAND_STATUS] != 0x01) {
|
||||
@@ -671,17 +670,11 @@ void LD2412Component::readline_(int readch) {
|
||||
return; // Not enough data to process yet
|
||||
}
|
||||
if (ld2412::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(MAX_LINE_LENGTH)];
|
||||
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, this->buffer_pos_));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
||||
this->handle_periodic_data_();
|
||||
this->buffer_pos_ = 0; // Reset position index for next message
|
||||
} else if (ld2412::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(MAX_LINE_LENGTH)];
|
||||
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, this->buffer_pos_));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
||||
if (this->handle_ack_data_()) {
|
||||
this->buffer_pos_ = 0; // Reset position index for next message
|
||||
} else {
|
||||
|
||||
@@ -607,8 +607,7 @@ bool LD2450Component::handle_ack_data_() {
|
||||
return true;
|
||||
}
|
||||
if (!ld2450::validate_header_footer(CMD_FRAME_HEADER, this->buffer_data_)) {
|
||||
char hex_buf[format_hex_pretty_size(HEADER_FOOTER_SIZE)];
|
||||
ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, HEADER_FOOTER_SIZE));
|
||||
ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty(this->buffer_data_, HEADER_FOOTER_SIZE).c_str());
|
||||
return true;
|
||||
}
|
||||
if (this->buffer_data_[COMMAND_STATUS] != 0x01) {
|
||||
@@ -759,17 +758,11 @@ void LD2450Component::readline_(int readch) {
|
||||
}
|
||||
if (this->buffer_data_[this->buffer_pos_ - 2] == DATA_FRAME_FOOTER[0] &&
|
||||
this->buffer_data_[this->buffer_pos_ - 1] == DATA_FRAME_FOOTER[1]) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(MAX_LINE_LENGTH)];
|
||||
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, this->buffer_pos_));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
||||
this->handle_periodic_data_();
|
||||
this->buffer_pos_ = 0; // Reset position index for next frame
|
||||
} else if (ld2450::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(MAX_LINE_LENGTH)];
|
||||
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty_to(hex_buf, this->buffer_data_, this->buffer_pos_));
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
||||
if (this->handle_ack_data_()) {
|
||||
this->buffer_pos_ = 0; // Reset position index for next message
|
||||
} else {
|
||||
|
||||
@@ -63,8 +63,10 @@ void ArduinoInternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||
pinMode(pin_, flags_to_mode(flags)); // NOLINT
|
||||
}
|
||||
|
||||
size_t ArduinoInternalGPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
return snprintf(buffer, len, "%u", this->pin_);
|
||||
std::string ArduinoInternalGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%u", pin_);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool ArduinoInternalGPIOPin::digital_read() {
|
||||
|
||||
@@ -16,7 +16,7 @@ class ArduinoInternalGPIOPin : public InternalGPIOPin {
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
|
||||
@@ -36,7 +36,7 @@ static const char *get_color_mode_json_str(ColorMode mode) {
|
||||
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (state.supports_effects()) {
|
||||
root[ESPHOME_F("effect")] = state.get_effect_name_ref();
|
||||
root[ESPHOME_F("effect")] = state.get_effect_name();
|
||||
root[ESPHOME_F("effect_index")] = state.get_current_effect_index();
|
||||
root[ESPHOME_F("effect_count")] = state.get_effect_count();
|
||||
}
|
||||
|
||||
@@ -337,22 +337,6 @@ async def to_code(config):
|
||||
is_at_least_very_verbose = this_severity >= very_verbose_severity
|
||||
has_serial_logging = baud_rate != 0
|
||||
|
||||
# Add defines for which Serial object is needed (allows linker to exclude unused)
|
||||
if CORE.is_esp8266:
|
||||
hw_uart = config.get(CONF_HARDWARE_UART, UART0)
|
||||
if has_serial_logging and hw_uart in (UART0, UART0_SWAP):
|
||||
cg.add_define("USE_ESP8266_LOGGER_SERIAL")
|
||||
# Exclude Serial1 from Arduino build
|
||||
cg.add_build_flag("-DNO_GLOBAL_SERIAL1")
|
||||
elif has_serial_logging and hw_uart == UART1:
|
||||
cg.add_define("USE_ESP8266_LOGGER_SERIAL1")
|
||||
# Exclude Serial from Arduino build
|
||||
cg.add_build_flag("-DNO_GLOBAL_SERIAL")
|
||||
else:
|
||||
# No serial logging - exclude both
|
||||
cg.add_build_flag("-DNO_GLOBAL_SERIAL")
|
||||
cg.add_build_flag("-DNO_GLOBAL_SERIAL1")
|
||||
|
||||
if (
|
||||
(CORE.is_esp8266 or CORE.is_rp2040)
|
||||
and has_serial_logging
|
||||
|
||||
@@ -65,8 +65,8 @@ void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const ch
|
||||
uint16_t buffer_at = 0; // Initialize buffer position
|
||||
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
|
||||
MAX_CONSOLE_LOG_MSG_SIZE);
|
||||
// Add newline before writing to console
|
||||
this->add_newline_to_buffer_(console_buffer, &buffer_at, MAX_CONSOLE_LOG_MSG_SIZE);
|
||||
// Add newline if platform needs it (ESP32 doesn't add via write_msg_)
|
||||
this->add_newline_to_buffer_if_needed_(console_buffer, &buffer_at, MAX_CONSOLE_LOG_MSG_SIZE);
|
||||
this->write_msg_(console_buffer, buffer_at);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user