mirror of
https://github.com/esphome/esphome.git
synced 2026-03-01 02:14:19 -07:00
Merge branch 'dev' into ble_set_security_params
This commit is contained in:
@@ -1 +1 @@
|
||||
74867fc82764102ce1275ea2bc43e3aeee7619679537c6db61114a33342bb4c7
|
||||
b97e16a84153b2a4cfc51137cd6121db3c32374504b2bea55144413b3e573052
|
||||
|
||||
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -6,8 +6,9 @@
|
||||
|
||||
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Developer breaking change (an API change that could break external components)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) — [policy](https://developers.esphome.io/contributing/code/#what-constitutes-a-c-breaking-change)
|
||||
- [ ] Developer breaking change (an API change that could break external components) — [policy](https://developers.esphome.io/contributing/code/#what-is-considered-public-c-api)
|
||||
- [ ] Undocumented C++ API change (removal or change of undocumented public methods that lambda users may depend on) — [policy](https://developers.esphome.io/contributing/code/#c-user-expectations)
|
||||
- [ ] Code quality improvements to existing code or addition of tests
|
||||
- [ ] Other
|
||||
|
||||
|
||||
4
.github/actions/build-image/action.yaml
vendored
4
.github/actions/build-image/action.yaml
vendored
@@ -47,7 +47,7 @@ runs:
|
||||
|
||||
- name: Build and push to ghcr by digest
|
||||
id: build-ghcr
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
@@ -73,7 +73,7 @@ runs:
|
||||
|
||||
- name: Build and push to dockerhub by digest
|
||||
id: build-dockerhub
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
||||
1
.github/scripts/auto-label-pr/constants.js
vendored
1
.github/scripts/auto-label-pr/constants.js
vendored
@@ -27,6 +27,7 @@ module.exports = {
|
||||
'new-feature',
|
||||
'breaking-change',
|
||||
'developer-breaking-change',
|
||||
'undocumented-api-change',
|
||||
'code-quality',
|
||||
'deprecated-component'
|
||||
],
|
||||
|
||||
1
.github/scripts/auto-label-pr/detectors.js
vendored
1
.github/scripts/auto-label-pr/detectors.js
vendored
@@ -238,6 +238,7 @@ async function detectPRTemplateCheckboxes(context) {
|
||||
{ pattern: /- \[x\] New feature \(non-breaking change which adds functionality\)/i, label: 'new-feature' },
|
||||
{ pattern: /- \[x\] Breaking change \(fix or feature that would cause existing functionality to not work as expected\)/i, label: 'breaking-change' },
|
||||
{ pattern: /- \[x\] Developer breaking change \(an API change that could break external components\)/i, label: 'developer-breaking-change' },
|
||||
{ pattern: /- \[x\] Undocumented C\+\+ API change \(removal or change of undocumented public methods that lambda users may depend on\)/i, label: 'undocumented-api-change' },
|
||||
{ pattern: /- \[x\] Code quality improvements to existing code or addition of tests/i, label: 'code-quality' }
|
||||
];
|
||||
|
||||
|
||||
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@@ -115,6 +115,7 @@ jobs:
|
||||
python-version:
|
||||
- "3.11"
|
||||
- "3.13"
|
||||
- "3.14"
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macOS-latest
|
||||
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
|
||||
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -86,6 +86,6 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
|
||||
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Stale
|
||||
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
debug-only: ${{ github.ref != 'refs/heads/dev' }} # Dry-run when not run on dev branch
|
||||
remove-stale-when-updated: true
|
||||
|
||||
@@ -11,7 +11,7 @@ ci:
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.15.0
|
||||
rev: v0.15.3
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -213,6 +213,7 @@ esphome/components/hbridge/light/* @DotNetDann
|
||||
esphome/components/hbridge/switch/* @dwmw2
|
||||
esphome/components/hc8/* @omartijn
|
||||
esphome/components/hdc2010/* @optimusprimespace @ssieb
|
||||
esphome/components/hdc302x/* @joshuasing
|
||||
esphome/components/he60r/* @clydebarrow
|
||||
esphome/components/heatpumpir/* @rob-deutsch
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
@@ -411,6 +412,7 @@ esphome/components/rp2040_pwm/* @jesserockz
|
||||
esphome/components/rpi_dpi_rgb/* @clydebarrow
|
||||
esphome/components/rtl87xx/* @kuba2k2
|
||||
esphome/components/rtttl/* @glmnet
|
||||
esphome/components/runtime_image/* @clydebarrow @guillempages @kahrendt
|
||||
esphome/components/runtime_stats/* @bdraco
|
||||
esphome/components/rx8130/* @beormund
|
||||
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
|
||||
|
||||
2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 2026.2.0-dev
|
||||
PROJECT_NUMBER = 2026.3.0-dev
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
@@ -9,7 +9,8 @@ FROM ghcr.io/esphome/docker-base:${BUILD_OS}-ha-addon-${BUILD_BASE_VERSION} AS b
|
||||
ARG BUILD_TYPE
|
||||
FROM base-source-${BUILD_TYPE} AS base
|
||||
|
||||
RUN git config --system --add safe.directory "*"
|
||||
RUN git config --system --add safe.directory "*" \
|
||||
&& git config --system advice.detachedHead false
|
||||
|
||||
# Install build tools for Python packages that require compilation
|
||||
# (e.g., ruamel.yaml.clibz used by ESP-IDF's idf-component-manager)
|
||||
|
||||
@@ -431,6 +431,14 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
|
||||
return 1
|
||||
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
||||
|
||||
process_stacktrace = None
|
||||
|
||||
try:
|
||||
module = importlib.import_module("esphome.components." + CORE.target_platform)
|
||||
process_stacktrace = getattr(module, "process_stacktrace")
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
backtrace_state = False
|
||||
ser = serial.Serial()
|
||||
ser.baudrate = baud_rate
|
||||
@@ -472,9 +480,14 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
|
||||
)
|
||||
safe_print(parser.parse_line(line, time_str))
|
||||
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
config, line, backtrace_state=backtrace_state
|
||||
)
|
||||
if process_stacktrace:
|
||||
backtrace_state = process_stacktrace(
|
||||
config, line, backtrace_state
|
||||
)
|
||||
else:
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
config, line, backtrace_state=backtrace_state
|
||||
)
|
||||
except serial.SerialException:
|
||||
_LOGGER.error("Serial port closed!")
|
||||
return 0
|
||||
@@ -944,12 +957,6 @@ def command_clean_all(args: ArgsProtocol) -> int | None:
|
||||
return 0
|
||||
|
||||
|
||||
def command_mqtt_fingerprint(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
from esphome import mqtt
|
||||
|
||||
return mqtt.get_fingerprint(config)
|
||||
|
||||
|
||||
def command_version(args: ArgsProtocol) -> int | None:
|
||||
safe_print(f"Version: {const.__version__}")
|
||||
return 0
|
||||
@@ -1237,7 +1244,6 @@ POST_CONFIG_ACTIONS = {
|
||||
"run": command_run,
|
||||
"clean": command_clean,
|
||||
"clean-mqtt": command_clean_mqtt,
|
||||
"mqtt-fingerprint": command_mqtt_fingerprint,
|
||||
"idedata": command_idedata,
|
||||
"rename": command_rename,
|
||||
"discover": command_discover,
|
||||
@@ -1451,13 +1457,6 @@ def parse_args(argv):
|
||||
)
|
||||
parser_wizard.add_argument("configuration", help="Your YAML configuration file.")
|
||||
|
||||
parser_fingerprint = subparsers.add_parser(
|
||||
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||
)
|
||||
parser_fingerprint.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
subparsers.add_parser("version", help="Print the ESPHome version and exit.")
|
||||
|
||||
parser_clean = subparsers.add_parser(
|
||||
|
||||
@@ -256,7 +256,7 @@ SYMBOL_PATTERNS = {
|
||||
"ipv6_stack": ["nd6_", "ip6_", "mld6_", "icmp6_", "icmp6_input"],
|
||||
# Order matters! More specific categories must come before general ones.
|
||||
# mdns must come before bluetooth to avoid "_mdns_disable_pcb" matching "ble_" pattern
|
||||
"mdns_lib": ["mdns"],
|
||||
"mdns_lib": ["mdns", "packet$"],
|
||||
# memory_mgmt must come before wifi_stack to catch mmu_hal_* symbols
|
||||
"memory_mgmt": [
|
||||
"mem_",
|
||||
@@ -794,7 +794,6 @@ SYMBOL_PATTERNS = {
|
||||
"s_dp",
|
||||
"s_ni",
|
||||
"s_reg_dump",
|
||||
"packet$",
|
||||
"d_mult_table",
|
||||
"K",
|
||||
"fcstab",
|
||||
|
||||
@@ -92,10 +92,7 @@ void AbsoluteHumidityComponent::loop() {
|
||||
// Calculate absolute humidity
|
||||
const float absolute_humidity = vapor_density(es, hr, temperature_k);
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Saturation vapor pressure %f kPa\n"
|
||||
"Publishing absolute humidity %f g/m³",
|
||||
es, absolute_humidity);
|
||||
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa, absolute humidity %f g/m³", es, absolute_humidity);
|
||||
|
||||
// Publish absolute humidity
|
||||
this->status_clear_warning();
|
||||
|
||||
@@ -199,12 +199,19 @@ void AcDimmer::setup() {
|
||||
setTimer1Callback(&timer_interrupt);
|
||||
#endif
|
||||
#ifdef USE_ESP32
|
||||
dimmer_timer = timer_begin(TIMER_FREQUENCY_HZ);
|
||||
timer_attach_interrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr);
|
||||
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||
// are not callable from ISR (placed in flash storage).
|
||||
// Here we just use an interrupt firing every 50 µs.
|
||||
timer_alarm(dimmer_timer, TIMER_INTERVAL_US, true, 0);
|
||||
if (dimmer_timer == nullptr) {
|
||||
dimmer_timer = timer_begin(TIMER_FREQUENCY_HZ);
|
||||
if (dimmer_timer == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create GPTimer for AC dimmer");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
timer_attach_interrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr);
|
||||
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||
// are not callable from ISR (placed in flash storage).
|
||||
// Here we just use an interrupt firing every 50 µs.
|
||||
timer_alarm(dimmer_timer, TIMER_INTERVAL_US, true, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -67,10 +67,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
|
||||
if (chr == nullptr) {
|
||||
ESP_LOGW(TAG,
|
||||
"[%s] No control service found at device, not an Anova..?\n"
|
||||
"[%s] Note, this component does not currently support Anova Nano.",
|
||||
this->get_name().c_str(), this->get_name().c_str());
|
||||
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
|
||||
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
this->char_handle_ = chr->handle;
|
||||
|
||||
@@ -233,8 +233,8 @@ def _consume_api_sockets(config: ConfigType) -> ConfigType:
|
||||
|
||||
# API needs 1 listening socket + typically 3 concurrent client connections
|
||||
# (not max_connections, which is the upper limit rarely reached)
|
||||
sockets_needed = 1 + 3
|
||||
socket.consume_sockets(sockets_needed, "api")(config)
|
||||
socket.consume_sockets(3, "api")(config)
|
||||
socket.consume_sockets(1, "api", socket.SocketType.TCP_LISTEN)(config)
|
||||
return config
|
||||
|
||||
|
||||
|
||||
@@ -989,6 +989,7 @@ enum ClimateAction {
|
||||
CLIMATE_ACTION_IDLE = 4;
|
||||
CLIMATE_ACTION_DRYING = 5;
|
||||
CLIMATE_ACTION_FAN = 6;
|
||||
CLIMATE_ACTION_DEFROSTING = 7;
|
||||
}
|
||||
enum ClimatePreset {
|
||||
CLIMATE_PRESET_NONE = 0;
|
||||
|
||||
@@ -60,6 +60,11 @@ static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 5;
|
||||
static constexpr uint8_t MAX_PING_RETRIES = 60;
|
||||
static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
|
||||
static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
|
||||
// Timeout for completing the handshake (Noise transport + HelloRequest).
|
||||
// A stalled handshake from a buggy client or network glitch holds a connection
|
||||
// slot, which can prevent legitimate clients from reconnecting. Also hardens
|
||||
// against the less likely case of intentional connection slot exhaustion.
|
||||
static constexpr uint32_t HANDSHAKE_TIMEOUT_MS = 15000;
|
||||
|
||||
static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION);
|
||||
|
||||
@@ -205,7 +210,12 @@ void APIConnection::loop() {
|
||||
this->fatal_error_with_log_(LOG_STR("Reading failed"), err);
|
||||
return;
|
||||
} else {
|
||||
this->last_traffic_ = now;
|
||||
// Only update last_traffic_ after authentication to ensure the
|
||||
// handshake timeout is an absolute deadline from connection start.
|
||||
// Pre-auth messages (e.g. PingRequest) must not reset the timer.
|
||||
if (this->is_authenticated()) {
|
||||
this->last_traffic_ = now;
|
||||
}
|
||||
// read a packet
|
||||
this->read_message(buffer.data_len, buffer.type, buffer.data);
|
||||
if (this->flags_.remove)
|
||||
@@ -223,6 +233,15 @@ void APIConnection::loop() {
|
||||
this->process_active_iterator_();
|
||||
}
|
||||
|
||||
// Disconnect clients that haven't completed the handshake in time.
|
||||
// Stale half-open connections from buggy clients or network issues can
|
||||
// accumulate and block legitimate clients from reconnecting.
|
||||
if (!this->is_authenticated() && now - this->last_traffic_ > HANDSHAKE_TIMEOUT_MS) {
|
||||
this->on_fatal_error();
|
||||
this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR("handshake timeout; disconnecting"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->flags_.sent_ping) {
|
||||
// Disconnect if not responded within 2.5*keepalive
|
||||
if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
|
||||
@@ -328,9 +347,7 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
|
||||
#endif
|
||||
|
||||
// Calculate size
|
||||
ProtoSize size_calc;
|
||||
msg.calculate_size(size_calc);
|
||||
uint32_t calculated_size = size_calc.get_size();
|
||||
uint32_t calculated_size = msg.calculated_size();
|
||||
|
||||
// Cache frame sizes to avoid repeated virtual calls
|
||||
const uint8_t header_padding = conn->helper_->frame_header_padding();
|
||||
@@ -358,19 +375,14 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
|
||||
shared_buf.resize(current_size + footer_size + header_padding);
|
||||
}
|
||||
|
||||
// Encode directly into buffer
|
||||
size_t size_before_encode = shared_buf.size();
|
||||
msg.encode({&shared_buf});
|
||||
// Pre-resize buffer to include payload, then encode through raw pointer
|
||||
size_t write_start = shared_buf.size();
|
||||
shared_buf.resize(write_start + calculated_size);
|
||||
ProtoWriteBuffer buffer{&shared_buf, write_start};
|
||||
msg.encode(buffer);
|
||||
|
||||
// Calculate actual encoded size (not including header that was already added)
|
||||
size_t actual_payload_size = shared_buf.size() - size_before_encode;
|
||||
|
||||
// Return actual total size (header + actual payload + footer)
|
||||
size_t actual_total_size = header_padding + actual_payload_size + footer_size;
|
||||
|
||||
// Verify that calculate_size() returned the correct value
|
||||
assert(calculated_size == actual_payload_size);
|
||||
return static_cast<uint16_t>(actual_total_size);
|
||||
// Return total size (header + payload + footer)
|
||||
return static_cast<uint16_t>(header_padding + calculated_size + footer_size);
|
||||
}
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
@@ -1334,9 +1346,8 @@ uint16_t APIConnection::try_send_water_heater_state(EntityBase *entity, APIConne
|
||||
resp.target_temperature_low = wh->get_target_temperature_low();
|
||||
resp.target_temperature_high = wh->get_target_temperature_high();
|
||||
resp.state = wh->get_state();
|
||||
resp.key = wh->get_object_id_hash();
|
||||
|
||||
return encode_message_to_buffer(resp, WaterHeaterStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
return fill_and_encode_entity_state(wh, resp, WaterHeaterStateResponse::MESSAGE_TYPE, conn, remaining_size);
|
||||
}
|
||||
uint16_t APIConnection::try_send_water_heater_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
||||
auto *wh = static_cast<water_heater::WaterHeater *>(entity);
|
||||
@@ -1484,6 +1495,8 @@ void APIConnection::complete_authentication_() {
|
||||
}
|
||||
|
||||
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
|
||||
// Reset traffic timer so keepalive starts from authentication, not connection start
|
||||
this->last_traffic_ = App.get_loop_component_start_time();
|
||||
this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("connected"));
|
||||
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
||||
{
|
||||
@@ -1510,9 +1523,15 @@ bool APIConnection::send_hello_response_(const HelloRequest &msg) {
|
||||
this->client_api_version_major_ = msg.api_version_major;
|
||||
this->client_api_version_minor_ = msg.api_version_minor;
|
||||
char peername[socket::SOCKADDR_STR_LEN];
|
||||
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 %" PRIu16 ".%" PRIu16, this->helper_->get_client_name(),
|
||||
this->helper_->get_peername_to(peername), this->client_api_version_major_, this->client_api_version_minor_);
|
||||
|
||||
// TODO: Remove before 2026.8.0 (one version after get_object_id backward compat removal)
|
||||
if (!this->client_supports_api_version(1, 14)) {
|
||||
ESP_LOGW(TAG, "'%s' using outdated API %" PRIu16 ".%" PRIu16 ", update to 1.14+", this->helper_->get_client_name(),
|
||||
this->client_api_version_major_, this->client_api_version_minor_);
|
||||
}
|
||||
|
||||
HelloResponse resp;
|
||||
resp.api_version_major = 1;
|
||||
resp.api_version_minor = 14;
|
||||
@@ -1827,12 +1846,14 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
||||
return false;
|
||||
}
|
||||
bool APIConnection::send_message_impl(const ProtoMessage &msg, uint8_t message_type) {
|
||||
ProtoSize size;
|
||||
msg.calculate_size(size);
|
||||
uint32_t payload_size = msg.calculated_size();
|
||||
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||
this->prepare_first_message_buffer(shared_buf, size.get_size());
|
||||
msg.encode({&shared_buf});
|
||||
return this->send_buffer({&shared_buf}, message_type);
|
||||
this->prepare_first_message_buffer(shared_buf, payload_size);
|
||||
size_t write_start = shared_buf.size();
|
||||
shared_buf.resize(write_start + payload_size);
|
||||
ProtoWriteBuffer buffer{&shared_buf, write_start};
|
||||
msg.encode(buffer);
|
||||
return this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type);
|
||||
}
|
||||
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
|
||||
const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE);
|
||||
@@ -1864,6 +1885,8 @@ void APIConnection::on_fatal_error() {
|
||||
this->flags_.remove = true;
|
||||
}
|
||||
|
||||
void __attribute__((flatten)) APIConnection::DeferredBatch::push_item(const BatchItem &item) { items.push_back(item); }
|
||||
|
||||
void APIConnection::DeferredBatch::add_item(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
|
||||
uint8_t aux_data_index) {
|
||||
// Check if we already have a message of this type for this entity
|
||||
@@ -1880,7 +1903,7 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, uint8_t message_
|
||||
}
|
||||
}
|
||||
// No existing item found (or event), add new one
|
||||
items.push_back({entity, message_type, estimated_size, aux_data_index});
|
||||
this->push_item({entity, message_type, estimated_size, aux_data_index});
|
||||
}
|
||||
|
||||
void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, uint8_t message_type, uint8_t estimated_size) {
|
||||
@@ -1888,7 +1911,7 @@ void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, uint8_t me
|
||||
// This avoids expensive vector::insert which shifts all elements
|
||||
// Note: We only ever have one high-priority message at a time (ping OR disconnect)
|
||||
// If we're disconnecting, pings are blocked, so this simple swap is sufficient
|
||||
items.push_back({entity, message_type, estimated_size, AUX_DATA_UNUSED});
|
||||
this->push_item({entity, message_type, estimated_size, AUX_DATA_UNUSED});
|
||||
if (items.size() > 1) {
|
||||
// Swap the new high-priority item to the front
|
||||
std::swap(items.front(), items.back());
|
||||
@@ -1921,10 +1944,6 @@ bool APIConnection::schedule_batch_() {
|
||||
}
|
||||
|
||||
void APIConnection::process_batch_() {
|
||||
// Ensure MessageInfo remains trivially destructible for our placement new approach
|
||||
static_assert(std::is_trivially_destructible<MessageInfo>::value,
|
||||
"MessageInfo must remain trivially destructible with this placement-new approach");
|
||||
|
||||
if (this->deferred_batch_.empty()) {
|
||||
this->flags_.batch_scheduled = false;
|
||||
return;
|
||||
@@ -1949,6 +1968,10 @@ void APIConnection::process_batch_() {
|
||||
for (size_t i = 0; i < num_items; i++) {
|
||||
total_estimated_size += this->deferred_batch_[i].estimated_size;
|
||||
}
|
||||
// Clamp to MAX_BATCH_PACKET_SIZE — we won't send more than that per batch
|
||||
if (total_estimated_size > MAX_BATCH_PACKET_SIZE) {
|
||||
total_estimated_size = MAX_BATCH_PACKET_SIZE;
|
||||
}
|
||||
|
||||
this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size);
|
||||
|
||||
@@ -1972,7 +1995,20 @@ void APIConnection::process_batch_() {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t messages_to_process = std::min(num_items, MAX_MESSAGES_PER_BATCH);
|
||||
// Multi-message path — heavy stack frame isolated in separate noinline function
|
||||
this->process_batch_multi_(shared_buf, num_items, header_padding, footer_size);
|
||||
}
|
||||
|
||||
// Separated from process_batch_() so the single-message fast path gets a minimal
|
||||
// stack frame without the MAX_MESSAGES_PER_BATCH * sizeof(MessageInfo) array.
|
||||
void APIConnection::process_batch_multi_(std::vector<uint8_t> &shared_buf, size_t num_items, uint8_t header_padding,
|
||||
uint8_t footer_size) {
|
||||
// Ensure MessageInfo remains trivially destructible for our placement new approach
|
||||
static_assert(std::is_trivially_destructible<MessageInfo>::value,
|
||||
"MessageInfo must remain trivially destructible with this placement-new approach");
|
||||
|
||||
const size_t messages_to_process = std::min(num_items, MAX_MESSAGES_PER_BATCH);
|
||||
const uint8_t frame_overhead = header_padding + footer_size;
|
||||
|
||||
// Stack-allocated array for message info
|
||||
alignas(MessageInfo) char message_info_storage[MAX_MESSAGES_PER_BATCH * sizeof(MessageInfo)];
|
||||
@@ -1999,7 +2035,7 @@ void APIConnection::process_batch_() {
|
||||
|
||||
// Message was encoded successfully
|
||||
// payload_size is header_padding + actual payload size + footer_size
|
||||
uint16_t proto_payload_size = payload_size - header_padding - footer_size;
|
||||
uint16_t proto_payload_size = payload_size - frame_overhead;
|
||||
// Use placement new to construct MessageInfo in pre-allocated stack array
|
||||
// This avoids default-constructing all MAX_MESSAGES_PER_BATCH elements
|
||||
// Explicit destruction is not needed because MessageInfo is trivially destructible,
|
||||
@@ -2015,42 +2051,38 @@ void APIConnection::process_batch_() {
|
||||
current_offset = shared_buf.size() + footer_size;
|
||||
}
|
||||
|
||||
if (items_processed == 0) {
|
||||
this->deferred_batch_.clear();
|
||||
return;
|
||||
}
|
||||
if (items_processed > 0) {
|
||||
// Add footer space for the last message (for Noise protocol MAC)
|
||||
if (footer_size > 0) {
|
||||
shared_buf.resize(shared_buf.size() + footer_size);
|
||||
}
|
||||
|
||||
// Add footer space for the last message (for Noise protocol MAC)
|
||||
if (footer_size > 0) {
|
||||
shared_buf.resize(shared_buf.size() + footer_size);
|
||||
}
|
||||
|
||||
// Send all collected messages
|
||||
APIError err = this->helper_->write_protobuf_messages(ProtoWriteBuffer{&shared_buf},
|
||||
std::span<const MessageInfo>(message_info, items_processed));
|
||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||
this->fatal_error_with_log_(LOG_STR("Batch write failed"), err);
|
||||
}
|
||||
// Send all collected messages
|
||||
APIError err = this->helper_->write_protobuf_messages(ProtoWriteBuffer{&shared_buf},
|
||||
std::span<const MessageInfo>(message_info, items_processed));
|
||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||
this->fatal_error_with_log_(LOG_STR("Batch write failed"), err);
|
||||
}
|
||||
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
// Log messages after send attempt for VV debugging
|
||||
// It's safe to use the buffer for logging at this point regardless of send result
|
||||
for (size_t i = 0; i < items_processed; i++) {
|
||||
const auto &item = this->deferred_batch_[i];
|
||||
this->log_batch_item_(item);
|
||||
}
|
||||
// Log messages after send attempt for VV debugging
|
||||
// It's safe to use the buffer for logging at this point regardless of send result
|
||||
for (size_t i = 0; i < items_processed; i++) {
|
||||
const auto &item = this->deferred_batch_[i];
|
||||
this->log_batch_item_(item);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Handle remaining items more efficiently
|
||||
if (items_processed < this->deferred_batch_.size()) {
|
||||
// Remove processed items from the beginning
|
||||
this->deferred_batch_.remove_front(items_processed);
|
||||
// Reschedule for remaining items
|
||||
this->schedule_batch_();
|
||||
} else {
|
||||
// All items processed
|
||||
this->clear_batch_();
|
||||
// Partial batch — remove processed items and reschedule
|
||||
if (items_processed < this->deferred_batch_.size()) {
|
||||
this->deferred_batch_.remove_front(items_processed);
|
||||
this->schedule_batch_();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// All items processed (or none could be processed)
|
||||
this->clear_batch_();
|
||||
}
|
||||
|
||||
// Dispatch message encoding based on message_type
|
||||
|
||||
@@ -541,6 +541,8 @@ class APIConnection final : public APIServerConnectionBase {
|
||||
uint8_t aux_data_index = AUX_DATA_UNUSED);
|
||||
// Add item to the front of the batch (for high priority messages like ping)
|
||||
void add_item_front(EntityBase *entity, uint8_t message_type, uint8_t estimated_size);
|
||||
// Single push_back site to avoid duplicate _M_realloc_insert instantiation
|
||||
void push_item(const BatchItem &item);
|
||||
|
||||
// Clear all items
|
||||
void clear() {
|
||||
@@ -548,8 +550,8 @@ class APIConnection final : public APIServerConnectionBase {
|
||||
batch_start_time = 0;
|
||||
}
|
||||
|
||||
// Remove processed items from the front
|
||||
void remove_front(size_t count) { items.erase(items.begin(), items.begin() + count); }
|
||||
// Remove processed items from the front — noinline to keep memmove out of warm callers
|
||||
void remove_front(size_t count) __attribute__((noinline)) { items.erase(items.begin(), items.begin() + count); }
|
||||
|
||||
bool empty() const { return items.empty(); }
|
||||
size_t size() const { return items.size(); }
|
||||
@@ -621,6 +623,8 @@ class APIConnection final : public APIServerConnectionBase {
|
||||
|
||||
bool schedule_batch_();
|
||||
void process_batch_();
|
||||
void process_batch_multi_(std::vector<uint8_t> &shared_buf, size_t num_items, uint8_t header_padding,
|
||||
uint8_t footer_size) __attribute__((noinline));
|
||||
void clear_batch_() {
|
||||
this->deferred_batch_.clear();
|
||||
this->flags_.batch_scheduled = false;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace esphome::api {
|
||||
|
||||
static const char *const TAG = "api.noise";
|
||||
#ifdef USE_ESP8266
|
||||
static const char PROLOGUE_INIT[] PROGMEM = "NoiseAPIInit";
|
||||
static constexpr char PROLOGUE_INIT[] PROGMEM = "NoiseAPIInit";
|
||||
#else
|
||||
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
|
||||
#endif
|
||||
@@ -138,10 +138,12 @@ APIError APINoiseFrameHelper::handle_noise_error_(int err, const LogString *func
|
||||
|
||||
/// Run through handshake messages (if in that phase)
|
||||
APIError APINoiseFrameHelper::loop() {
|
||||
// During handshake phase, process as many actions as possible until we can't progress
|
||||
// socket_->ready() stays true until next main loop, but state_action() will return
|
||||
// WOULD_BLOCK when no more data is available to read
|
||||
while (state_ != State::DATA && this->socket_->ready()) {
|
||||
// Cache ready() outside the loop. On ESP8266 LWIP raw TCP, ready() returns false once
|
||||
// the rx buffer is consumed. Re-checking each iteration would block handshake writes
|
||||
// that must follow reads, deadlocking the handshake. state_action() will return
|
||||
// WOULD_BLOCK when no more data is available to read.
|
||||
bool socket_ready = this->socket_->ready();
|
||||
while (state_ != State::DATA && socket_ready) {
|
||||
APIError err = state_action_();
|
||||
if (err == APIError::WOULD_BLOCK) {
|
||||
break;
|
||||
@@ -472,7 +474,7 @@ APIError APINoiseFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffer, s
|
||||
// buf_start[1], buf_start[2] to be set after encryption
|
||||
|
||||
// Write message header (to be encrypted)
|
||||
const uint8_t msg_offset = 3;
|
||||
constexpr uint8_t msg_offset = 3;
|
||||
buf_start[msg_offset] = static_cast<uint8_t>(msg.message_type >> 8); // type high byte
|
||||
buf_start[msg_offset + 1] = static_cast<uint8_t>(msg.message_type); // type low byte
|
||||
buf_start[msg_offset + 2] = static_cast<uint8_t>(msg.payload_size >> 8); // data_len high byte
|
||||
|
||||
@@ -295,9 +295,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffe
|
||||
buf_start[header_offset] = 0x00; // indicator
|
||||
|
||||
// Encode varints directly into buffer
|
||||
ProtoVarInt(msg.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
||||
ProtoVarInt(msg.message_type)
|
||||
.encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
||||
encode_varint_to_buffer(msg.payload_size, buf_start + header_offset + 1);
|
||||
encode_varint_to_buffer(msg.message_type, buf_start + header_offset + 1 + size_varint_len);
|
||||
|
||||
// Add iovec for this message (header + payload)
|
||||
size_t msg_len = static_cast<size_t>(total_header_len + msg.payload_size);
|
||||
|
||||
@@ -31,7 +31,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void HelloResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void HelloResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, this->api_version_major);
|
||||
buffer.encode_uint32(2, this->api_version_minor);
|
||||
buffer.encode_string(3, this->server_info);
|
||||
@@ -44,7 +44,7 @@ void HelloResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_length(1, this->name.size());
|
||||
}
|
||||
#ifdef USE_AREAS
|
||||
void AreaInfo::encode(ProtoWriteBuffer buffer) const {
|
||||
void AreaInfo::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, this->area_id);
|
||||
buffer.encode_string(2, this->name);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ void AreaInfo::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DEVICES
|
||||
void DeviceInfo::encode(ProtoWriteBuffer buffer) const {
|
||||
void DeviceInfo::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, this->device_id);
|
||||
buffer.encode_string(2, this->name);
|
||||
buffer.encode_uint32(3, this->area_id);
|
||||
@@ -65,7 +65,7 @@ void DeviceInfo::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->area_id);
|
||||
}
|
||||
#endif
|
||||
void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void DeviceInfoResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(2, this->name);
|
||||
buffer.encode_string(3, this->mac_address);
|
||||
buffer.encode_string(4, this->esphome_version);
|
||||
@@ -111,7 +111,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_AREAS
|
||||
buffer.encode_message(22, this->area);
|
||||
buffer.encode_message(22, this->area, false);
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
buffer.encode_uint32(23, this->zwave_proxy_feature_flags);
|
||||
@@ -176,7 +176,7 @@ void DeviceInfoResponse::calculate_size(ProtoSize &size) const {
|
||||
#endif
|
||||
}
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -206,7 +206,7 @@ void ListEntitiesBinarySensorResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BinarySensorStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
@@ -224,7 +224,7 @@ void BinarySensorStateResponse::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -260,7 +260,7 @@ void ListEntitiesCoverResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void CoverStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(3, this->position);
|
||||
buffer.encode_float(4, this->tilt);
|
||||
@@ -317,7 +317,7 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -359,7 +359,7 @@ void ListEntitiesFanResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void FanStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
buffer.encode_bool(3, this->oscillating);
|
||||
@@ -443,7 +443,7 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -489,7 +489,7 @@ void ListEntitiesLightResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(2, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void LightStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
buffer.encode_float(3, this->brightness);
|
||||
@@ -635,7 +635,7 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -671,7 +671,7 @@ void ListEntitiesSensorResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void SensorStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
@@ -689,7 +689,7 @@ void SensorStateResponse::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -719,7 +719,7 @@ void ListEntitiesSwitchResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void SwitchStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
#ifdef USE_DEVICES
|
||||
@@ -760,7 +760,7 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -788,7 +788,7 @@ void ListEntitiesTextSensorResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void TextSensorStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
@@ -818,7 +818,7 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void SubscribeLogsResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, static_cast<uint32_t>(this->level));
|
||||
buffer.encode_bytes(3, this->message_ptr_, this->message_len_);
|
||||
}
|
||||
@@ -839,11 +839,11 @@ bool NoiseEncryptionSetKeyRequest::decode_length(uint32_t field_id, ProtoLengthD
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void NoiseEncryptionSetKeyResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); }
|
||||
void NoiseEncryptionSetKeyResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_bool(1, this->success); }
|
||||
void NoiseEncryptionSetKeyResponse::calculate_size(ProtoSize &size) const { size.add_bool(1, this->success); }
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_SERVICES
|
||||
void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
|
||||
void HomeassistantServiceMap::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->key);
|
||||
buffer.encode_string(2, this->value);
|
||||
}
|
||||
@@ -851,7 +851,7 @@ void HomeassistantServiceMap::calculate_size(ProtoSize &size) const {
|
||||
size.add_length(1, this->key.size());
|
||||
size.add_length(1, this->value.size());
|
||||
}
|
||||
void HomeassistantActionRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
void HomeassistantActionRequest::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->service);
|
||||
for (auto &it : this->data) {
|
||||
buffer.encode_message(2, it);
|
||||
@@ -924,7 +924,7 @@ bool HomeassistantActionResponse::decode_length(uint32_t field_id, ProtoLengthDe
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->entity_id);
|
||||
buffer.encode_string(2, this->attribute);
|
||||
buffer.encode_bool(3, this->once);
|
||||
@@ -976,7 +976,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
return true;
|
||||
}
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->name);
|
||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->type));
|
||||
}
|
||||
@@ -984,7 +984,7 @@ void ListEntitiesServicesArgument::calculate_size(ProtoSize &size) const {
|
||||
size.add_length(1, this->name.size());
|
||||
size.add_uint32(1, static_cast<uint32_t>(this->type));
|
||||
}
|
||||
void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesServicesResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->name);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
for (auto &it : this->args) {
|
||||
@@ -1103,7 +1103,7 @@ void ExecuteServiceRequest::decode(const uint8_t *buffer, size_t length) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
void ExecuteServiceResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ExecuteServiceResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, this->call_id);
|
||||
buffer.encode_bool(2, this->success);
|
||||
buffer.encode_string(3, this->error_message);
|
||||
@@ -1121,7 +1121,7 @@ void ExecuteServiceResponse::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1147,7 +1147,7 @@ void ListEntitiesCameraResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void CameraImageResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bytes(2, this->data_ptr_, this->data_len_);
|
||||
buffer.encode_bool(3, this->done);
|
||||
@@ -1178,7 +1178,7 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1276,7 +1276,7 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const {
|
||||
#endif
|
||||
size.add_uint32(2, this->feature_flags);
|
||||
}
|
||||
void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ClimateStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->mode));
|
||||
buffer.encode_float(3, this->current_temperature);
|
||||
@@ -1407,7 +1407,7 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1449,7 +1449,7 @@ void ListEntitiesWaterHeaterResponse::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
size.add_uint32(1, this->supported_features);
|
||||
}
|
||||
void WaterHeaterStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void WaterHeaterStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(2, this->current_temperature);
|
||||
buffer.encode_float(3, this->target_temperature);
|
||||
@@ -1515,7 +1515,7 @@ bool WaterHeaterCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1553,7 +1553,7 @@ void ListEntitiesNumberResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void NumberStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
@@ -1596,7 +1596,7 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1630,7 +1630,7 @@ void ListEntitiesSelectResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void SelectStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
@@ -1681,7 +1681,7 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SIREN
|
||||
void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1719,7 +1719,7 @@ void ListEntitiesSirenResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void SirenStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void SirenStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
#ifdef USE_DEVICES
|
||||
@@ -1789,7 +1789,7 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1823,7 +1823,7 @@ void ListEntitiesLockResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void LockStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void LockStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->state));
|
||||
#ifdef USE_DEVICES
|
||||
@@ -1878,7 +1878,7 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1930,7 +1930,7 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const {
|
||||
void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->format);
|
||||
buffer.encode_uint32(2, this->sample_rate);
|
||||
buffer.encode_uint32(3, this->num_channels);
|
||||
@@ -1944,7 +1944,7 @@ void MediaPlayerSupportedFormat::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, static_cast<uint32_t>(this->purpose));
|
||||
size.add_uint32(1, this->sample_bytes);
|
||||
}
|
||||
void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -1978,7 +1978,7 @@ void ListEntitiesMediaPlayerResponse::calculate_size(ProtoSize &size) const {
|
||||
#endif
|
||||
size.add_uint32(1, this->feature_flags);
|
||||
}
|
||||
void MediaPlayerStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void MediaPlayerStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->state));
|
||||
buffer.encode_float(3, this->volume);
|
||||
@@ -2062,7 +2062,7 @@ bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id,
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_sint32(2, this->rssi);
|
||||
buffer.encode_uint32(3, this->address_type);
|
||||
@@ -2074,7 +2074,7 @@ void BluetoothLERawAdvertisement::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->address_type);
|
||||
size.add_length(1, this->data_len);
|
||||
}
|
||||
void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
for (uint16_t i = 0; i < this->advertisements_len; i++) {
|
||||
buffer.encode_message(1, this->advertisements[i]);
|
||||
}
|
||||
@@ -2103,7 +2103,7 @@ bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void BluetoothDeviceConnectionResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothDeviceConnectionResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_bool(2, this->connected);
|
||||
buffer.encode_uint32(3, this->mtu);
|
||||
@@ -2125,7 +2125,7 @@ bool BluetoothGATTGetServicesRequest::decode_varint(uint32_t field_id, ProtoVarI
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void BluetoothGATTDescriptor::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTDescriptor::encode(ProtoWriteBuffer &buffer) const {
|
||||
if (this->uuid[0] != 0 || this->uuid[1] != 0) {
|
||||
buffer.encode_uint64(1, this->uuid[0], true);
|
||||
buffer.encode_uint64(1, this->uuid[1], true);
|
||||
@@ -2141,7 +2141,7 @@ void BluetoothGATTDescriptor::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->handle);
|
||||
size.add_uint32(1, this->short_uuid);
|
||||
}
|
||||
void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer &buffer) const {
|
||||
if (this->uuid[0] != 0 || this->uuid[1] != 0) {
|
||||
buffer.encode_uint64(1, this->uuid[0], true);
|
||||
buffer.encode_uint64(1, this->uuid[1], true);
|
||||
@@ -2163,7 +2163,7 @@ void BluetoothGATTCharacteristic::calculate_size(ProtoSize &size) const {
|
||||
size.add_repeated_message(1, this->descriptors);
|
||||
size.add_uint32(1, this->short_uuid);
|
||||
}
|
||||
void BluetoothGATTService::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTService::encode(ProtoWriteBuffer &buffer) const {
|
||||
if (this->uuid[0] != 0 || this->uuid[1] != 0) {
|
||||
buffer.encode_uint64(1, this->uuid[0], true);
|
||||
buffer.encode_uint64(1, this->uuid[1], true);
|
||||
@@ -2183,7 +2183,7 @@ void BluetoothGATTService::calculate_size(ProtoSize &size) const {
|
||||
size.add_repeated_message(1, this->characteristics);
|
||||
size.add_uint32(1, this->short_uuid);
|
||||
}
|
||||
void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
for (auto &it : this->services) {
|
||||
buffer.encode_message(2, it);
|
||||
@@ -2193,7 +2193,7 @@ void BluetoothGATTGetServicesResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint64(1, this->address);
|
||||
size.add_repeated_message(1, this->services);
|
||||
}
|
||||
void BluetoothGATTGetServicesDoneResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTGetServicesDoneResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
}
|
||||
void BluetoothGATTGetServicesDoneResponse::calculate_size(ProtoSize &size) const { size.add_uint64(1, this->address); }
|
||||
@@ -2210,7 +2210,7 @@ bool BluetoothGATTReadRequest::decode_varint(uint32_t field_id, ProtoVarInt valu
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void BluetoothGATTReadResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTReadResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_uint32(2, this->handle);
|
||||
buffer.encode_bytes(3, this->data_ptr_, this->data_len_);
|
||||
@@ -2302,7 +2302,7 @@ bool BluetoothGATTNotifyRequest::decode_varint(uint32_t field_id, ProtoVarInt va
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_uint32(2, this->handle);
|
||||
buffer.encode_bytes(3, this->data_ptr_, this->data_len_);
|
||||
@@ -2312,7 +2312,7 @@ void BluetoothGATTNotifyDataResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->handle);
|
||||
size.add_length(1, this->data_len_);
|
||||
}
|
||||
void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, this->free);
|
||||
buffer.encode_uint32(2, this->limit);
|
||||
for (const auto &it : this->allocated) {
|
||||
@@ -2330,7 +2330,7 @@ void BluetoothConnectionsFreeResponse::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
}
|
||||
}
|
||||
void BluetoothGATTErrorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTErrorResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_uint32(2, this->handle);
|
||||
buffer.encode_int32(3, this->error);
|
||||
@@ -2340,7 +2340,7 @@ void BluetoothGATTErrorResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->handle);
|
||||
size.add_int32(1, this->error);
|
||||
}
|
||||
void BluetoothGATTWriteResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTWriteResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_uint32(2, this->handle);
|
||||
}
|
||||
@@ -2348,7 +2348,7 @@ void BluetoothGATTWriteResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint64(1, this->address);
|
||||
size.add_uint32(1, this->handle);
|
||||
}
|
||||
void BluetoothGATTNotifyResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothGATTNotifyResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_uint32(2, this->handle);
|
||||
}
|
||||
@@ -2356,7 +2356,7 @@ void BluetoothGATTNotifyResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint64(1, this->address);
|
||||
size.add_uint32(1, this->handle);
|
||||
}
|
||||
void BluetoothDevicePairingResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothDevicePairingResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_bool(2, this->paired);
|
||||
buffer.encode_int32(3, this->error);
|
||||
@@ -2366,7 +2366,7 @@ void BluetoothDevicePairingResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_bool(1, this->paired);
|
||||
size.add_int32(1, this->error);
|
||||
}
|
||||
void BluetoothDeviceUnpairingResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothDeviceUnpairingResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_bool(2, this->success);
|
||||
buffer.encode_int32(3, this->error);
|
||||
@@ -2376,7 +2376,7 @@ void BluetoothDeviceUnpairingResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_bool(1, this->success);
|
||||
size.add_int32(1, this->error);
|
||||
}
|
||||
void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_bool(2, this->success);
|
||||
buffer.encode_int32(3, this->error);
|
||||
@@ -2386,7 +2386,7 @@ void BluetoothDeviceClearCacheResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_bool(1, this->success);
|
||||
size.add_int32(1, this->error);
|
||||
}
|
||||
void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void BluetoothScannerStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, static_cast<uint32_t>(this->state));
|
||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->mode));
|
||||
buffer.encode_uint32(3, static_cast<uint32_t>(this->configured_mode));
|
||||
@@ -2421,7 +2421,7 @@ bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarIn
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const {
|
||||
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, this->noise_suppression_level);
|
||||
buffer.encode_uint32(2, this->auto_gain);
|
||||
buffer.encode_float(3, this->volume_multiplier);
|
||||
@@ -2431,11 +2431,11 @@ void VoiceAssistantAudioSettings::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->auto_gain);
|
||||
size.add_float(1, this->volume_multiplier);
|
||||
}
|
||||
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
void VoiceAssistantRequest::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_bool(1, this->start);
|
||||
buffer.encode_string(2, this->conversation_id);
|
||||
buffer.encode_uint32(3, this->flags);
|
||||
buffer.encode_message(4, this->audio_settings);
|
||||
buffer.encode_message(4, this->audio_settings, false);
|
||||
buffer.encode_string(5, this->wake_word_phrase);
|
||||
}
|
||||
void VoiceAssistantRequest::calculate_size(ProtoSize &size) const {
|
||||
@@ -2516,7 +2516,7 @@ bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const {
|
||||
void VoiceAssistantAudio::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_bytes(1, this->data, this->data_len);
|
||||
buffer.encode_bool(2, this->end);
|
||||
}
|
||||
@@ -2587,9 +2587,9 @@ bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLength
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); }
|
||||
void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer &buffer) const { buffer.encode_bool(1, this->success); }
|
||||
void VoiceAssistantAnnounceFinished::calculate_size(ProtoSize &size) const { size.add_bool(1, this->success); }
|
||||
void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const {
|
||||
void VoiceAssistantWakeWord::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->id);
|
||||
buffer.encode_string(2, this->wake_word);
|
||||
for (auto &it : this->trained_languages) {
|
||||
@@ -2656,7 +2656,7 @@ bool VoiceAssistantConfigurationRequest::decode_length(uint32_t field_id, ProtoL
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
for (auto &it : this->available_wake_words) {
|
||||
buffer.encode_message(1, it);
|
||||
}
|
||||
@@ -2686,7 +2686,7 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -2718,7 +2718,7 @@ void ListEntitiesAlarmControlPanelResponse::calculate_size(ProtoSize &size) cons
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->state));
|
||||
#ifdef USE_DEVICES
|
||||
@@ -2770,7 +2770,7 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -2804,7 +2804,7 @@ void ListEntitiesTextResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void TextStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void TextStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
@@ -2855,7 +2855,7 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -2881,7 +2881,7 @@ void ListEntitiesDateResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void DateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void DateStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->missing_state);
|
||||
buffer.encode_uint32(3, this->year);
|
||||
@@ -2934,7 +2934,7 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -2960,7 +2960,7 @@ void ListEntitiesTimeResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void TimeStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void TimeStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->missing_state);
|
||||
buffer.encode_uint32(3, this->hour);
|
||||
@@ -3013,7 +3013,7 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -3049,7 +3049,7 @@ void ListEntitiesEventResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void EventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void EventResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->event_type);
|
||||
#ifdef USE_DEVICES
|
||||
@@ -3065,7 +3065,7 @@ void EventResponse::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -3099,7 +3099,7 @@ void ListEntitiesValveResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void ValveStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ValveStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(2, this->position);
|
||||
buffer.encode_uint32(3, static_cast<uint32_t>(this->current_operation));
|
||||
@@ -3148,7 +3148,7 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -3174,7 +3174,7 @@ void ListEntitiesDateTimeResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void DateTimeStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void DateTimeStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->missing_state);
|
||||
buffer.encode_fixed32(3, this->epoch_seconds);
|
||||
@@ -3217,7 +3217,7 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -3245,7 +3245,7 @@ void ListEntitiesUpdateResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_uint32(1, this->device_id);
|
||||
#endif
|
||||
}
|
||||
void UpdateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void UpdateStateResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->missing_state);
|
||||
buffer.encode_bool(3, this->in_progress);
|
||||
@@ -3314,7 +3314,7 @@ bool ZWaveProxyFrame::decode_length(uint32_t field_id, ProtoLengthDelimited valu
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void ZWaveProxyFrame::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(1, this->data, this->data_len); }
|
||||
void ZWaveProxyFrame::encode(ProtoWriteBuffer &buffer) const { buffer.encode_bytes(1, this->data, this->data_len); }
|
||||
void ZWaveProxyFrame::calculate_size(ProtoSize &size) const { size.add_length(1, this->data_len); }
|
||||
bool ZWaveProxyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
@@ -3338,7 +3338,7 @@ bool ZWaveProxyRequest::decode_length(uint32_t field_id, ProtoLengthDelimited va
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void ZWaveProxyRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
void ZWaveProxyRequest::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_uint32(1, static_cast<uint32_t>(this->type));
|
||||
buffer.encode_bytes(2, this->data, this->data_len);
|
||||
}
|
||||
@@ -3348,7 +3348,7 @@ void ZWaveProxyRequest::calculate_size(ProtoSize &size) const {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_INFRARED
|
||||
void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
@@ -3419,7 +3419,7 @@ bool InfraredRFTransmitRawTimingsRequest::decode_32bit(uint32_t field_id, Proto3
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void InfraredRFReceiveEvent::encode(ProtoWriteBuffer buffer) const {
|
||||
void InfraredRFReceiveEvent::encode(ProtoWriteBuffer &buffer) const {
|
||||
#ifdef USE_DEVICES
|
||||
buffer.encode_uint32(1, this->device_id);
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// See script/api_protobuf/api_protobuf.py
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
#include "proto.h"
|
||||
@@ -117,6 +116,7 @@ enum ClimateAction : uint32_t {
|
||||
CLIMATE_ACTION_IDLE = 4,
|
||||
CLIMATE_ACTION_DRYING = 5,
|
||||
CLIMATE_ACTION_FAN = 6,
|
||||
CLIMATE_ACTION_DEFROSTING = 7,
|
||||
};
|
||||
enum ClimatePreset : uint32_t {
|
||||
CLIMATE_PRESET_NONE = 0,
|
||||
@@ -382,7 +382,7 @@ class HelloResponse final : public ProtoMessage {
|
||||
uint32_t api_version_minor{0};
|
||||
StringRef server_info{};
|
||||
StringRef name{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -447,7 +447,7 @@ class AreaInfo final : public ProtoMessage {
|
||||
public:
|
||||
uint32_t area_id{0};
|
||||
StringRef name{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -462,7 +462,7 @@ class DeviceInfo final : public ProtoMessage {
|
||||
uint32_t device_id{0};
|
||||
StringRef name{};
|
||||
uint32_t area_id{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -527,7 +527,7 @@ class DeviceInfoResponse final : public ProtoMessage {
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
uint32_t zwave_home_id{0};
|
||||
#endif
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -558,7 +558,7 @@ class ListEntitiesBinarySensorResponse final : public InfoResponseProtoMessage {
|
||||
#endif
|
||||
StringRef device_class{};
|
||||
bool is_status_binary_sensor{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -575,7 +575,7 @@ class BinarySensorStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
bool state{false};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -597,7 +597,7 @@ class ListEntitiesCoverResponse final : public InfoResponseProtoMessage {
|
||||
bool supports_tilt{false};
|
||||
StringRef device_class{};
|
||||
bool supports_stop{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -615,7 +615,7 @@ class CoverStateResponse final : public StateResponseProtoMessage {
|
||||
float position{0.0f};
|
||||
float tilt{0.0f};
|
||||
enums::CoverOperation current_operation{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -657,7 +657,7 @@ class ListEntitiesFanResponse final : public InfoResponseProtoMessage {
|
||||
bool supports_direction{false};
|
||||
int32_t supported_speed_count{0};
|
||||
const std::vector<const char *> *supported_preset_modes{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -677,7 +677,7 @@ class FanStateResponse final : public StateResponseProtoMessage {
|
||||
enums::FanDirection direction{};
|
||||
int32_t speed_level{0};
|
||||
StringRef preset_mode{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -724,7 +724,7 @@ class ListEntitiesLightResponse final : public InfoResponseProtoMessage {
|
||||
float min_mireds{0.0f};
|
||||
float max_mireds{0.0f};
|
||||
const FixedVector<const char *> *effects{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -751,7 +751,7 @@ class LightStateResponse final : public StateResponseProtoMessage {
|
||||
float cold_white{0.0f};
|
||||
float warm_white{0.0f};
|
||||
StringRef effect{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -815,7 +815,7 @@ class ListEntitiesSensorResponse final : public InfoResponseProtoMessage {
|
||||
bool force_update{false};
|
||||
StringRef device_class{};
|
||||
enums::SensorStateClass state_class{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -832,7 +832,7 @@ class SensorStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
float state{0.0f};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -851,7 +851,7 @@ class ListEntitiesSwitchResponse final : public InfoResponseProtoMessage {
|
||||
#endif
|
||||
bool assumed_state{false};
|
||||
StringRef device_class{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -867,7 +867,7 @@ class SwitchStateResponse final : public StateResponseProtoMessage {
|
||||
const char *message_name() const override { return "switch_state_response"; }
|
||||
#endif
|
||||
bool state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -901,7 +901,7 @@ class ListEntitiesTextSensorResponse final : public InfoResponseProtoMessage {
|
||||
const char *message_name() const override { return "list_entities_text_sensor_response"; }
|
||||
#endif
|
||||
StringRef device_class{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -918,7 +918,7 @@ class TextSensorStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
StringRef state{};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -957,7 +957,7 @@ class SubscribeLogsResponse final : public ProtoMessage {
|
||||
this->message_ptr_ = data;
|
||||
this->message_len_ = len;
|
||||
}
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -990,7 +990,7 @@ class NoiseEncryptionSetKeyResponse final : public ProtoMessage {
|
||||
const char *message_name() const override { return "noise_encryption_set_key_response"; }
|
||||
#endif
|
||||
bool success{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1004,7 +1004,7 @@ class HomeassistantServiceMap final : public ProtoMessage {
|
||||
public:
|
||||
StringRef key{};
|
||||
StringRef value{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1033,7 +1033,7 @@ class HomeassistantActionRequest final : public ProtoMessage {
|
||||
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
|
||||
StringRef response_template{};
|
||||
#endif
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1077,7 +1077,7 @@ class SubscribeHomeAssistantStateResponse final : public ProtoMessage {
|
||||
StringRef entity_id{};
|
||||
StringRef attribute{};
|
||||
bool once{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1138,7 +1138,7 @@ class ListEntitiesServicesArgument final : public ProtoMessage {
|
||||
public:
|
||||
StringRef name{};
|
||||
enums::ServiceArgType type{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1157,7 +1157,7 @@ class ListEntitiesServicesResponse final : public ProtoMessage {
|
||||
uint32_t key{0};
|
||||
FixedVector<ListEntitiesServicesArgument> args{};
|
||||
enums::SupportsResponseType supports_response{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1227,7 +1227,7 @@ class ExecuteServiceResponse final : public ProtoMessage {
|
||||
const uint8_t *response_data{nullptr};
|
||||
uint16_t response_data_len{0};
|
||||
#endif
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1244,7 +1244,7 @@ class ListEntitiesCameraResponse final : public InfoResponseProtoMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "list_entities_camera_response"; }
|
||||
#endif
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1266,7 +1266,7 @@ class CameraImageResponse final : public StateResponseProtoMessage {
|
||||
this->data_len_ = len;
|
||||
}
|
||||
bool done{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1317,7 +1317,7 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage {
|
||||
float visual_min_humidity{0.0f};
|
||||
float visual_max_humidity{0.0f};
|
||||
uint32_t feature_flags{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1345,7 +1345,7 @@ class ClimateStateResponse final : public StateResponseProtoMessage {
|
||||
StringRef custom_preset{};
|
||||
float current_humidity{0.0f};
|
||||
float target_humidity{0.0f};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1403,7 +1403,7 @@ class ListEntitiesWaterHeaterResponse final : public InfoResponseProtoMessage {
|
||||
float target_temperature_step{0.0f};
|
||||
const water_heater::WaterHeaterModeMask *supported_modes{};
|
||||
uint32_t supported_features{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1424,7 +1424,7 @@ class WaterHeaterStateResponse final : public StateResponseProtoMessage {
|
||||
uint32_t state{0};
|
||||
float target_temperature_low{0.0f};
|
||||
float target_temperature_high{0.0f};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1468,7 +1468,7 @@ class ListEntitiesNumberResponse final : public InfoResponseProtoMessage {
|
||||
StringRef unit_of_measurement{};
|
||||
enums::NumberMode mode{};
|
||||
StringRef device_class{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1485,7 +1485,7 @@ class NumberStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
float state{0.0f};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1519,7 +1519,7 @@ class ListEntitiesSelectResponse final : public InfoResponseProtoMessage {
|
||||
const char *message_name() const override { return "list_entities_select_response"; }
|
||||
#endif
|
||||
const FixedVector<const char *> *options{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1536,7 +1536,7 @@ class SelectStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
StringRef state{};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1573,7 +1573,7 @@ class ListEntitiesSirenResponse final : public InfoResponseProtoMessage {
|
||||
const FixedVector<const char *> *tones{};
|
||||
bool supports_duration{false};
|
||||
bool supports_volume{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1589,7 +1589,7 @@ class SirenStateResponse final : public StateResponseProtoMessage {
|
||||
const char *message_name() const override { return "siren_state_response"; }
|
||||
#endif
|
||||
bool state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1634,7 +1634,7 @@ class ListEntitiesLockResponse final : public InfoResponseProtoMessage {
|
||||
bool supports_open{false};
|
||||
bool requires_code{false};
|
||||
StringRef code_format{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1650,7 +1650,7 @@ class LockStateResponse final : public StateResponseProtoMessage {
|
||||
const char *message_name() const override { return "lock_state_response"; }
|
||||
#endif
|
||||
enums::LockState state{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1687,7 +1687,7 @@ class ListEntitiesButtonResponse final : public InfoResponseProtoMessage {
|
||||
const char *message_name() const override { return "list_entities_button_response"; }
|
||||
#endif
|
||||
StringRef device_class{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1719,7 +1719,7 @@ class MediaPlayerSupportedFormat final : public ProtoMessage {
|
||||
uint32_t num_channels{0};
|
||||
enums::MediaPlayerFormatPurpose purpose{};
|
||||
uint32_t sample_bytes{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1737,7 +1737,7 @@ class ListEntitiesMediaPlayerResponse final : public InfoResponseProtoMessage {
|
||||
bool supports_pause{false};
|
||||
std::vector<MediaPlayerSupportedFormat> supported_formats{};
|
||||
uint32_t feature_flags{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1755,7 +1755,7 @@ class MediaPlayerStateResponse final : public StateResponseProtoMessage {
|
||||
enums::MediaPlayerState state{};
|
||||
float volume{0.0f};
|
||||
bool muted{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1811,7 +1811,7 @@ class BluetoothLERawAdvertisement final : public ProtoMessage {
|
||||
uint32_t address_type{0};
|
||||
uint8_t data[62]{};
|
||||
uint8_t data_len{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1828,7 +1828,7 @@ class BluetoothLERawAdvertisementsResponse final : public ProtoMessage {
|
||||
#endif
|
||||
std::array<BluetoothLERawAdvertisement, BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE> advertisements{};
|
||||
uint16_t advertisements_len{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1865,7 +1865,7 @@ class BluetoothDeviceConnectionResponse final : public ProtoMessage {
|
||||
bool connected{false};
|
||||
uint32_t mtu{0};
|
||||
int32_t error{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1893,7 +1893,7 @@ class BluetoothGATTDescriptor final : public ProtoMessage {
|
||||
std::array<uint64_t, 2> uuid{};
|
||||
uint32_t handle{0};
|
||||
uint32_t short_uuid{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1908,7 +1908,7 @@ class BluetoothGATTCharacteristic final : public ProtoMessage {
|
||||
uint32_t properties{0};
|
||||
FixedVector<BluetoothGATTDescriptor> descriptors{};
|
||||
uint32_t short_uuid{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1922,7 +1922,7 @@ class BluetoothGATTService final : public ProtoMessage {
|
||||
uint32_t handle{0};
|
||||
FixedVector<BluetoothGATTCharacteristic> characteristics{};
|
||||
uint32_t short_uuid{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1939,7 +1939,7 @@ class BluetoothGATTGetServicesResponse final : public ProtoMessage {
|
||||
#endif
|
||||
uint64_t address{0};
|
||||
std::vector<BluetoothGATTService> services{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1955,7 +1955,7 @@ class BluetoothGATTGetServicesDoneResponse final : public ProtoMessage {
|
||||
const char *message_name() const override { return "bluetooth_gatt_get_services_done_response"; }
|
||||
#endif
|
||||
uint64_t address{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -1994,7 +1994,7 @@ class BluetoothGATTReadResponse final : public ProtoMessage {
|
||||
this->data_ptr_ = data;
|
||||
this->data_len_ = len;
|
||||
}
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2089,7 +2089,7 @@ class BluetoothGATTNotifyDataResponse final : public ProtoMessage {
|
||||
this->data_ptr_ = data;
|
||||
this->data_len_ = len;
|
||||
}
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2107,7 +2107,7 @@ class BluetoothConnectionsFreeResponse final : public ProtoMessage {
|
||||
uint32_t free{0};
|
||||
uint32_t limit{0};
|
||||
std::array<uint64_t, BLUETOOTH_PROXY_MAX_CONNECTIONS> allocated{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2125,7 +2125,7 @@ class BluetoothGATTErrorResponse final : public ProtoMessage {
|
||||
uint64_t address{0};
|
||||
uint32_t handle{0};
|
||||
int32_t error{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2142,7 +2142,7 @@ class BluetoothGATTWriteResponse final : public ProtoMessage {
|
||||
#endif
|
||||
uint64_t address{0};
|
||||
uint32_t handle{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2159,7 +2159,7 @@ class BluetoothGATTNotifyResponse final : public ProtoMessage {
|
||||
#endif
|
||||
uint64_t address{0};
|
||||
uint32_t handle{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2177,7 +2177,7 @@ class BluetoothDevicePairingResponse final : public ProtoMessage {
|
||||
uint64_t address{0};
|
||||
bool paired{false};
|
||||
int32_t error{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2195,7 +2195,7 @@ class BluetoothDeviceUnpairingResponse final : public ProtoMessage {
|
||||
uint64_t address{0};
|
||||
bool success{false};
|
||||
int32_t error{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2213,7 +2213,7 @@ class BluetoothDeviceClearCacheResponse final : public ProtoMessage {
|
||||
uint64_t address{0};
|
||||
bool success{false};
|
||||
int32_t error{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2231,7 +2231,7 @@ class BluetoothScannerStateResponse final : public ProtoMessage {
|
||||
enums::BluetoothScannerState state{};
|
||||
enums::BluetoothScannerMode mode{};
|
||||
enums::BluetoothScannerMode configured_mode{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2277,7 +2277,7 @@ class VoiceAssistantAudioSettings final : public ProtoMessage {
|
||||
uint32_t noise_suppression_level{0};
|
||||
uint32_t auto_gain{0};
|
||||
float volume_multiplier{0.0f};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2297,7 +2297,7 @@ class VoiceAssistantRequest final : public ProtoMessage {
|
||||
uint32_t flags{0};
|
||||
VoiceAssistantAudioSettings audio_settings{};
|
||||
StringRef wake_word_phrase{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2359,7 +2359,7 @@ class VoiceAssistantAudio final : public ProtoDecodableMessage {
|
||||
const uint8_t *data{nullptr};
|
||||
uint16_t data_len{0};
|
||||
bool end{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2417,7 +2417,7 @@ class VoiceAssistantAnnounceFinished final : public ProtoMessage {
|
||||
const char *message_name() const override { return "voice_assistant_announce_finished"; }
|
||||
#endif
|
||||
bool success{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2430,7 +2430,7 @@ class VoiceAssistantWakeWord final : public ProtoMessage {
|
||||
StringRef id{};
|
||||
StringRef wake_word{};
|
||||
std::vector<std::string> trained_languages{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2480,7 +2480,7 @@ class VoiceAssistantConfigurationResponse final : public ProtoMessage {
|
||||
std::vector<VoiceAssistantWakeWord> available_wake_words{};
|
||||
const std::vector<std::string> *active_wake_words{};
|
||||
uint32_t max_active_wake_words{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2515,7 +2515,7 @@ class ListEntitiesAlarmControlPanelResponse final : public InfoResponseProtoMess
|
||||
uint32_t supported_features{0};
|
||||
bool requires_code{false};
|
||||
bool requires_code_to_arm{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2531,7 +2531,7 @@ class AlarmControlPanelStateResponse final : public StateResponseProtoMessage {
|
||||
const char *message_name() const override { return "alarm_control_panel_state_response"; }
|
||||
#endif
|
||||
enums::AlarmControlPanelState state{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2570,7 +2570,7 @@ class ListEntitiesTextResponse final : public InfoResponseProtoMessage {
|
||||
uint32_t max_length{0};
|
||||
StringRef pattern{};
|
||||
enums::TextMode mode{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2587,7 +2587,7 @@ class TextStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
StringRef state{};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2621,7 +2621,7 @@ class ListEntitiesDateResponse final : public InfoResponseProtoMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "list_entities_date_response"; }
|
||||
#endif
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2640,7 +2640,7 @@ class DateStateResponse final : public StateResponseProtoMessage {
|
||||
uint32_t year{0};
|
||||
uint32_t month{0};
|
||||
uint32_t day{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2675,7 +2675,7 @@ class ListEntitiesTimeResponse final : public InfoResponseProtoMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "list_entities_time_response"; }
|
||||
#endif
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2694,7 +2694,7 @@ class TimeStateResponse final : public StateResponseProtoMessage {
|
||||
uint32_t hour{0};
|
||||
uint32_t minute{0};
|
||||
uint32_t second{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2731,7 +2731,7 @@ class ListEntitiesEventResponse final : public InfoResponseProtoMessage {
|
||||
#endif
|
||||
StringRef device_class{};
|
||||
const FixedVector<const char *> *event_types{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2747,7 +2747,7 @@ class EventResponse final : public StateResponseProtoMessage {
|
||||
const char *message_name() const override { return "event_response"; }
|
||||
#endif
|
||||
StringRef event_type{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2768,7 +2768,7 @@ class ListEntitiesValveResponse final : public InfoResponseProtoMessage {
|
||||
bool assumed_state{false};
|
||||
bool supports_position{false};
|
||||
bool supports_stop{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2785,7 +2785,7 @@ class ValveStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
float position{0.0f};
|
||||
enums::ValveOperation current_operation{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2820,7 +2820,7 @@ class ListEntitiesDateTimeResponse final : public InfoResponseProtoMessage {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "list_entities_date_time_response"; }
|
||||
#endif
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2837,7 +2837,7 @@ class DateTimeStateResponse final : public StateResponseProtoMessage {
|
||||
#endif
|
||||
bool missing_state{false};
|
||||
uint32_t epoch_seconds{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2871,7 +2871,7 @@ class ListEntitiesUpdateResponse final : public InfoResponseProtoMessage {
|
||||
const char *message_name() const override { return "list_entities_update_response"; }
|
||||
#endif
|
||||
StringRef device_class{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2895,7 +2895,7 @@ class UpdateStateResponse final : public StateResponseProtoMessage {
|
||||
StringRef title{};
|
||||
StringRef release_summary{};
|
||||
StringRef release_url{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2930,7 +2930,7 @@ class ZWaveProxyFrame final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
const uint8_t *data{nullptr};
|
||||
uint16_t data_len{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2949,7 +2949,7 @@ class ZWaveProxyRequest final : public ProtoDecodableMessage {
|
||||
enums::ZWaveProxyRequestType type{};
|
||||
const uint8_t *data{nullptr};
|
||||
uint16_t data_len{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -2969,7 +2969,7 @@ class ListEntitiesInfraredResponse final : public InfoResponseProtoMessage {
|
||||
const char *message_name() const override { return "list_entities_infrared_response"; }
|
||||
#endif
|
||||
uint32_t capabilities{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
@@ -3016,7 +3016,7 @@ class InfraredRFReceiveEvent final : public ProtoMessage {
|
||||
#endif
|
||||
uint32_t key{0};
|
||||
const std::vector<int32_t> *timings{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void encode(ProtoWriteBuffer &buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *dump_to(DumpBuffer &out) const override;
|
||||
|
||||
12
esphome/components/api/api_pb2_defines.h
Normal file
12
esphome/components/api/api_pb2_defines.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// This file was automatically generated with a tool.
|
||||
// See script/api_protobuf/api_protobuf.py
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
#ifndef USE_API_VARINT64
|
||||
#define USE_API_VARINT64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace esphome::api {} // namespace esphome::api
|
||||
@@ -321,6 +321,8 @@ template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::Climate
|
||||
return "CLIMATE_ACTION_DRYING";
|
||||
case enums::CLIMATE_ACTION_FAN:
|
||||
return "CLIMATE_ACTION_FAN";
|
||||
case enums::CLIMATE_ACTION_DEFROSTING:
|
||||
return "CLIMATE_ACTION_DEFROSTING";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
@@ -28,10 +28,12 @@ static const char *const TAG = "api";
|
||||
// APIServer
|
||||
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
APIServer::APIServer() {
|
||||
global_api_server = this;
|
||||
// Pre-allocate shared write buffer
|
||||
shared_write_buffer_.reserve(64);
|
||||
APIServer::APIServer() { global_api_server = this; }
|
||||
|
||||
void APIServer::socket_failed_(const LogString *msg) {
|
||||
ESP_LOGW(TAG, "Socket %s: errno %d", LOG_STR_ARG(msg), errno);
|
||||
this->destroy_socket_();
|
||||
this->mark_failed();
|
||||
}
|
||||
|
||||
void APIServer::setup() {
|
||||
@@ -52,22 +54,20 @@ void APIServer::setup() {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
|
||||
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0).release(); // monitored for incoming connections
|
||||
if (this->socket_ == nullptr) {
|
||||
ESP_LOGW(TAG, "Could not create socket");
|
||||
this->mark_failed();
|
||||
this->socket_failed_(LOG_STR("creation"));
|
||||
return;
|
||||
}
|
||||
int enable = 1;
|
||||
int err = this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
||||
ESP_LOGW(TAG, "Socket reuseaddr: errno %d", errno);
|
||||
// we can still continue
|
||||
}
|
||||
err = this->socket_->setblocking(false);
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
||||
this->mark_failed();
|
||||
this->socket_failed_(LOG_STR("nonblocking"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,28 +75,28 @@ void APIServer::setup() {
|
||||
|
||||
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
|
||||
if (sl == 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
|
||||
this->mark_failed();
|
||||
this->socket_failed_(LOG_STR("set sockaddr"));
|
||||
return;
|
||||
}
|
||||
|
||||
err = this->socket_->bind((struct sockaddr *) &server, sl);
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
|
||||
this->mark_failed();
|
||||
this->socket_failed_(LOG_STR("bind"));
|
||||
return;
|
||||
}
|
||||
|
||||
err = this->socket_->listen(this->listen_backlog_);
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
|
||||
this->mark_failed();
|
||||
this->socket_failed_(LOG_STR("listen"));
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
if (logger::global_logger != nullptr) {
|
||||
logger::global_logger->add_log_listener(this);
|
||||
logger::global_logger->add_log_callback(
|
||||
this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
|
||||
static_cast<APIServer *>(self)->on_log(level, tag, message, message_len);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -117,37 +117,7 @@ void APIServer::setup() {
|
||||
void APIServer::loop() {
|
||||
// Accept new clients only if the socket exists and has incoming connections
|
||||
if (this->socket_ && this->socket_->ready()) {
|
||||
while (true) {
|
||||
struct sockaddr_storage source_addr;
|
||||
socklen_t addr_len = sizeof(source_addr);
|
||||
|
||||
auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
|
||||
if (!sock)
|
||||
break;
|
||||
|
||||
char peername[socket::SOCKADDR_STR_LEN];
|
||||
sock->getpeername_to(peername);
|
||||
|
||||
// Check if we're at the connection limit
|
||||
if (this->clients_.size() >= this->max_connections_) {
|
||||
ESP_LOGW(TAG, "Max connections (%d), rejecting %s", this->max_connections_, peername);
|
||||
// Immediately close - socket destructor will handle cleanup
|
||||
sock.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Accept %s", peername);
|
||||
|
||||
auto *conn = new APIConnection(std::move(sock), this);
|
||||
this->clients_.emplace_back(conn);
|
||||
conn->start();
|
||||
|
||||
// First client connected - clear warning and update timestamp
|
||||
if (this->clients_.size() == 1 && this->reboot_timeout_ != 0) {
|
||||
this->status_clear_warning();
|
||||
this->last_connected_ = App.get_loop_component_start_time();
|
||||
}
|
||||
}
|
||||
this->accept_new_connections_();
|
||||
}
|
||||
|
||||
if (this->clients_.empty()) {
|
||||
@@ -178,46 +148,88 @@ void APIServer::loop() {
|
||||
while (client_index < this->clients_.size()) {
|
||||
auto &client = this->clients_[client_index];
|
||||
|
||||
// Common case: process active client
|
||||
if (!client->flags_.remove) {
|
||||
// Common case: process active client
|
||||
client->loop();
|
||||
}
|
||||
// Handle disconnection promptly - close socket to free LWIP PCB
|
||||
// resources and prevent retransmit crashes on ESP8266.
|
||||
if (client->flags_.remove) {
|
||||
// Rare case: handle disconnection (don't increment - swapped element needs processing)
|
||||
this->remove_client_(client_index);
|
||||
} else {
|
||||
client_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APIServer::remove_client_(size_t client_index) {
|
||||
auto &client = this->clients_[client_index];
|
||||
|
||||
#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());
|
||||
|
||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||
// Save client info before closing socket and removal for the trigger
|
||||
char peername_buf[socket::SOCKADDR_STR_LEN];
|
||||
std::string client_name(client->get_name());
|
||||
std::string client_peername(client->get_peername_to(peername_buf));
|
||||
#endif
|
||||
|
||||
// Close socket now (was deferred from on_fatal_error to allow getpeername)
|
||||
client->helper_->close();
|
||||
|
||||
// Swap with the last element and pop (avoids expensive vector shifts)
|
||||
if (client_index < this->clients_.size() - 1) {
|
||||
std::swap(this->clients_[client_index], this->clients_.back());
|
||||
}
|
||||
this->clients_.pop_back();
|
||||
|
||||
// Last client disconnected - set warning and start tracking for reboot timeout
|
||||
if (this->clients_.empty() && this->reboot_timeout_ != 0) {
|
||||
this->status_set_warning();
|
||||
this->last_connected_ = App.get_loop_component_start_time();
|
||||
}
|
||||
|
||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||
// Fire trigger after client is removed so api.connected reflects the true state
|
||||
this->client_disconnected_trigger_.trigger(client_name, client_peername);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __attribute__((flatten)) APIServer::accept_new_connections_() {
|
||||
while (true) {
|
||||
struct sockaddr_storage source_addr;
|
||||
socklen_t addr_len = sizeof(source_addr);
|
||||
|
||||
auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
|
||||
if (!sock)
|
||||
break;
|
||||
|
||||
char peername[socket::SOCKADDR_STR_LEN];
|
||||
sock->getpeername_to(peername);
|
||||
|
||||
// Check if we're at the connection limit
|
||||
if (this->clients_.size() >= this->max_connections_) {
|
||||
ESP_LOGW(TAG, "Max connections (%d), rejecting %s", this->max_connections_, peername);
|
||||
// Immediately close - socket destructor will handle cleanup
|
||||
sock.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Rare case: handle disconnection
|
||||
#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_LOGD(TAG, "Accept %s", peername);
|
||||
|
||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||
// Save client info before closing socket and removal for the trigger
|
||||
char peername_buf[socket::SOCKADDR_STR_LEN];
|
||||
std::string client_name(client->get_name());
|
||||
std::string client_peername(client->get_peername_to(peername_buf));
|
||||
#endif
|
||||
auto *conn = new APIConnection(std::move(sock), this);
|
||||
this->clients_.emplace_back(conn);
|
||||
conn->start();
|
||||
|
||||
// Close socket now (was deferred from on_fatal_error to allow getpeername)
|
||||
client->helper_->close();
|
||||
|
||||
// Swap with the last element and pop (avoids expensive vector shifts)
|
||||
if (client_index < this->clients_.size() - 1) {
|
||||
std::swap(this->clients_[client_index], this->clients_.back());
|
||||
}
|
||||
this->clients_.pop_back();
|
||||
|
||||
// Last client disconnected - set warning and start tracking for reboot timeout
|
||||
if (this->clients_.empty() && this->reboot_timeout_ != 0) {
|
||||
this->status_set_warning();
|
||||
// First client connected - clear warning and update timestamp
|
||||
if (this->clients_.size() == 1 && this->reboot_timeout_ != 0) {
|
||||
this->status_clear_warning();
|
||||
this->last_connected_ = App.get_loop_component_start_time();
|
||||
}
|
||||
|
||||
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||
// Fire trigger after client is removed so api.connected reflects the true state
|
||||
this->client_disconnected_trigger_.trigger(client_name, client_peername);
|
||||
#endif
|
||||
// Don't increment client_index since we need to process the swapped element
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,8 +433,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(StringRef)> &&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)
|
||||
@@ -431,7 +443,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(StringRef)> &&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));
|
||||
@@ -451,29 +463,29 @@ 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(StringRef)> &&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(StringRef)> &&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) {
|
||||
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) {
|
||||
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) {
|
||||
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()); }),
|
||||
@@ -482,12 +494,12 @@ void APIServer::add_state_subscription_(std::string entity_id, optional<std::str
|
||||
|
||||
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string)
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f) {
|
||||
std::function<void(const std::string &)> &&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(const std::string &)> f) {
|
||||
std::function<void(const std::string &)> &&f) {
|
||||
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), true);
|
||||
}
|
||||
|
||||
@@ -611,10 +623,7 @@ void APIServer::on_shutdown() {
|
||||
this->shutting_down_ = true;
|
||||
|
||||
// Close the listening socket to prevent new connections
|
||||
if (this->socket_) {
|
||||
this->socket_->close();
|
||||
this->socket_ = nullptr;
|
||||
}
|
||||
this->destroy_socket_();
|
||||
|
||||
// Change batch delay to 5ms for quick flushing during shutdown
|
||||
this->batch_delay_ = 5;
|
||||
|
||||
@@ -37,10 +37,6 @@ struct SavedNoisePsk {
|
||||
|
||||
class APIServer : public Component,
|
||||
public Controller
|
||||
#ifdef USE_LOGGER
|
||||
,
|
||||
public logger::LogListener
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
,
|
||||
public camera::CameraListener
|
||||
@@ -56,7 +52,7 @@ class APIServer : public Component,
|
||||
void on_shutdown() override;
|
||||
bool teardown() override;
|
||||
#ifdef USE_LOGGER
|
||||
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
|
||||
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len);
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
void on_camera_image(const std::shared_ptr<camera::CameraImage> &image) override;
|
||||
@@ -205,20 +201,20 @@ 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(StringRef)> &&f);
|
||||
void get_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> &&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);
|
||||
std::function<void(StringRef)> &&f);
|
||||
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(StringRef)> f);
|
||||
std::function<void(StringRef)> &&f);
|
||||
|
||||
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string for callback)
|
||||
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f);
|
||||
std::function<void(const std::string &)> &&f);
|
||||
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(const std::string &)> f);
|
||||
std::function<void(const std::string &)> &&f);
|
||||
|
||||
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
||||
#endif
|
||||
@@ -234,22 +230,34 @@ class APIServer : public Component,
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Accept incoming socket connections. Only called when socket has pending connections.
|
||||
void __attribute__((noinline)) accept_new_connections_();
|
||||
// Remove a disconnected client by index. Swaps with last element and pops.
|
||||
void __attribute__((noinline)) remove_client_(size_t client_index);
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
bool update_noise_psk_(const SavedNoisePsk &new_psk, const LogString *save_log_msg, const LogString *fail_log_msg,
|
||||
const psk_t &active_psk, bool make_active);
|
||||
#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,
|
||||
bool once);
|
||||
void add_state_subscription_(std::string entity_id, optional<std::string> attribute, std::function<void(StringRef)> f,
|
||||
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> &&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);
|
||||
std::function<void(const std::string &)> &&f, bool once);
|
||||
#endif // USE_API_HOMEASSISTANT_STATES
|
||||
// No explicit close() needed — listen sockets have no active connections on
|
||||
// failure/shutdown. Destructor handles fd cleanup (close or abort per platform).
|
||||
inline void destroy_socket_() {
|
||||
delete this->socket_;
|
||||
this->socket_ = nullptr;
|
||||
}
|
||||
void socket_failed_(const LogString *msg);
|
||||
// Pointers and pointer-like types first (4 bytes each)
|
||||
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
||||
socket::Socket *socket_{nullptr};
|
||||
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
||||
Trigger<std::string, std::string> client_connected_trigger_;
|
||||
#endif
|
||||
@@ -263,7 +271,11 @@ class APIServer : public Component,
|
||||
|
||||
// Vectors and strings (12 bytes each on 32-bit)
|
||||
std::vector<std::unique_ptr<APIConnection>> clients_;
|
||||
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
|
||||
// Shared proto write buffer for all connections.
|
||||
// Not pre-allocated: all send paths call prepare_first_message_buffer() which
|
||||
// reserves the exact needed size. Pre-allocating here would cause heap fragmentation
|
||||
// since the buffer would almost always reallocate on first use.
|
||||
std::vector<uint8_t> shared_write_buffer_;
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
std::vector<HomeAssistantStateSubscription> state_subs_;
|
||||
#endif
|
||||
|
||||
@@ -36,6 +36,8 @@ template<typename... X> class TemplatableStringValue : public TemplatableValue<s
|
||||
static std::string value_to_string(const char *val) { return std::string(val); } // For lambdas returning .c_str()
|
||||
static std::string value_to_string(const std::string &val) { return val; }
|
||||
static std::string value_to_string(std::string &&val) { return std::move(val); }
|
||||
static std::string value_to_string(const StringRef &val) { return val.str(); }
|
||||
static std::string value_to_string(StringRef &&val) { return val.str(); }
|
||||
|
||||
public:
|
||||
TemplatableStringValue() : TemplatableValue<std::string, X...>() {}
|
||||
|
||||
@@ -15,7 +15,7 @@ class APIConnection;
|
||||
return this->client_->schedule_message_(entity, ResponseType::MESSAGE_TYPE, ResponseType::ESTIMATED_SIZE); \
|
||||
}
|
||||
|
||||
class ListEntitiesIterator : public ComponentIterator {
|
||||
class ListEntitiesIterator final : public ComponentIterator {
|
||||
public:
|
||||
ListEntitiesIterator(APIConnection *client);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
|
||||
@@ -7,6 +7,23 @@ namespace esphome::api {
|
||||
|
||||
static const char *const TAG = "api.proto";
|
||||
|
||||
#ifdef USE_API_VARINT64
|
||||
optional<ProtoVarInt> ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed,
|
||||
uint32_t result32) {
|
||||
uint64_t result64 = result32;
|
||||
uint32_t limit = std::min(len, uint32_t(10));
|
||||
for (uint32_t i = 4; i < limit; i++) {
|
||||
uint8_t val = buffer[i];
|
||||
result64 |= uint64_t(val & 0x7F) << (i * 7);
|
||||
if ((val & 0x80) == 0) {
|
||||
*consumed = i + 1;
|
||||
return ProtoVarInt(result64);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size_t length, uint32_t target_field_id) {
|
||||
uint32_t count = 0;
|
||||
const uint8_t *ptr = buffer;
|
||||
@@ -70,6 +87,21 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef ESPHOME_DEBUG_API
|
||||
void ProtoWriteBuffer::debug_check_bounds_(size_t bytes, const char *caller) {
|
||||
if (this->pos_ + bytes > this->buffer_->data() + this->buffer_->size()) {
|
||||
ESP_LOGE(TAG, "ProtoWriteBuffer bounds check failed in %s: bytes=%zu offset=%td buf_size=%zu", caller, bytes,
|
||||
this->pos_ - this->buffer_->data(), this->buffer_->size());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
void ProtoWriteBuffer::debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual) {
|
||||
ESP_LOGE(TAG, "encode_message: size mismatch for field %" PRIu32 ": calculated=%" PRIu32 " actual=%td", field_id,
|
||||
expected, actual);
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
const uint8_t *ptr = buffer;
|
||||
const uint8_t *end = buffer + length;
|
||||
@@ -133,7 +165,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGV(TAG, "Invalid field type %u at offset %ld", field_type, (long) (ptr - buffer));
|
||||
ESP_LOGV(TAG, "Invalid field type %" PRIu32 " at offset %ld", field_type, (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "api_pb2_defines.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -57,6 +58,16 @@ inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Encode a varint directly into a pre-allocated buffer.
|
||||
/// Caller must ensure buffer has space (use ProtoSize::varint() to calculate).
|
||||
inline void encode_varint_to_buffer(uint32_t val, uint8_t *buffer) {
|
||||
while (val > 0x7F) {
|
||||
*buffer++ = static_cast<uint8_t>(val | 0x80);
|
||||
val >>= 7;
|
||||
}
|
||||
*buffer = static_cast<uint8_t>(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* StringRef Ownership Model for API Protocol Messages
|
||||
* ===================================================
|
||||
@@ -93,113 +104,85 @@ class ProtoVarInt {
|
||||
ProtoVarInt() : value_(0) {}
|
||||
explicit ProtoVarInt(uint64_t value) : value_(value) {}
|
||||
|
||||
/// Parse a varint from buffer. consumed must be a valid pointer (not null).
|
||||
static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
|
||||
if (len == 0) {
|
||||
if (consumed != nullptr)
|
||||
*consumed = 0;
|
||||
#ifdef ESPHOME_DEBUG_API
|
||||
assert(consumed != nullptr);
|
||||
#endif
|
||||
if (len == 0)
|
||||
return {};
|
||||
}
|
||||
|
||||
// Most common case: single-byte varint (values 0-127)
|
||||
// Fast path: single-byte varints (0-127) are the most common case
|
||||
// (booleans, small enums, field tags). Avoid loop overhead entirely.
|
||||
if ((buffer[0] & 0x80) == 0) {
|
||||
if (consumed != nullptr)
|
||||
*consumed = 1;
|
||||
*consumed = 1;
|
||||
return ProtoVarInt(buffer[0]);
|
||||
}
|
||||
|
||||
// General case for multi-byte varints
|
||||
// Since we know buffer[0]'s high bit is set, initialize with its value
|
||||
uint64_t result = buffer[0] & 0x7F;
|
||||
uint8_t bitpos = 7;
|
||||
|
||||
// A 64-bit varint is at most 10 bytes (ceil(64/7)). Reject overlong encodings
|
||||
// to avoid undefined behavior from shifting uint64_t by >= 64 bits.
|
||||
uint32_t max_len = std::min(len, uint32_t(10));
|
||||
|
||||
// Start from the second byte since we've already processed the first
|
||||
for (uint32_t i = 1; i < max_len; i++) {
|
||||
// 32-bit phase: process remaining bytes with native 32-bit shifts.
|
||||
// Without USE_API_VARINT64: cover bytes 1-4 (shifts 7, 14, 21, 28) — the uint32_t
|
||||
// shift at byte 4 (shift by 28) may lose bits 32-34, but those are always zero for valid uint32 values.
|
||||
// With USE_API_VARINT64: cover bytes 1-3 (shifts 7, 14, 21) so parse_wide handles
|
||||
// byte 4+ with full 64-bit arithmetic (avoids truncating values > UINT32_MAX).
|
||||
uint32_t result32 = buffer[0] & 0x7F;
|
||||
#ifdef USE_API_VARINT64
|
||||
uint32_t limit = std::min(len, uint32_t(4));
|
||||
#else
|
||||
uint32_t limit = std::min(len, uint32_t(5));
|
||||
#endif
|
||||
for (uint32_t i = 1; i < limit; i++) {
|
||||
uint8_t val = buffer[i];
|
||||
result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
|
||||
bitpos += 7;
|
||||
result32 |= uint32_t(val & 0x7F) << (i * 7);
|
||||
if ((val & 0x80) == 0) {
|
||||
if (consumed != nullptr)
|
||||
*consumed = i + 1;
|
||||
return ProtoVarInt(result);
|
||||
*consumed = i + 1;
|
||||
return ProtoVarInt(result32);
|
||||
}
|
||||
}
|
||||
|
||||
if (consumed != nullptr)
|
||||
*consumed = 0;
|
||||
return {}; // Incomplete or invalid varint
|
||||
// 64-bit phase for remaining bytes (BLE addresses etc.)
|
||||
#ifdef USE_API_VARINT64
|
||||
return parse_wide(buffer, len, consumed, result32);
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_API_VARINT64
|
||||
protected:
|
||||
/// Continue parsing varint bytes 4-9 with 64-bit arithmetic.
|
||||
/// Separated to keep 64-bit shift code (__ashldi3 on 32-bit platforms) out of the common path.
|
||||
static optional<ProtoVarInt> parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed, uint32_t result32)
|
||||
__attribute__((noinline));
|
||||
|
||||
public:
|
||||
#endif
|
||||
|
||||
constexpr uint16_t as_uint16() const { return this->value_; }
|
||||
constexpr uint32_t as_uint32() const { return this->value_; }
|
||||
constexpr uint64_t as_uint64() const { return this->value_; }
|
||||
constexpr bool as_bool() const { return this->value_; }
|
||||
constexpr int32_t as_int32() const {
|
||||
// Not ZigZag encoded
|
||||
return static_cast<int32_t>(this->as_int64());
|
||||
}
|
||||
constexpr int64_t as_int64() const {
|
||||
// Not ZigZag encoded
|
||||
return static_cast<int64_t>(this->value_);
|
||||
return static_cast<int32_t>(this->value_);
|
||||
}
|
||||
constexpr int32_t as_sint32() const {
|
||||
// with ZigZag encoding
|
||||
return decode_zigzag32(static_cast<uint32_t>(this->value_));
|
||||
}
|
||||
#ifdef USE_API_VARINT64
|
||||
constexpr uint64_t as_uint64() const { return this->value_; }
|
||||
constexpr int64_t as_int64() const {
|
||||
// Not ZigZag encoded
|
||||
return static_cast<int64_t>(this->value_);
|
||||
}
|
||||
constexpr int64_t as_sint64() const {
|
||||
// with ZigZag encoding
|
||||
return decode_zigzag64(this->value_);
|
||||
}
|
||||
/**
|
||||
* Encode the varint value to a pre-allocated buffer without bounds checking.
|
||||
*
|
||||
* @param buffer The pre-allocated buffer to write the encoded varint to
|
||||
* @param len The size of the buffer in bytes
|
||||
*
|
||||
* @note The caller is responsible for ensuring the buffer is large enough
|
||||
* to hold the encoded value. Use ProtoSize::varint() to calculate
|
||||
* the exact size needed before calling this method.
|
||||
* @note No bounds checking is performed for performance reasons.
|
||||
*/
|
||||
void encode_to_buffer_unchecked(uint8_t *buffer, size_t len) {
|
||||
uint64_t val = this->value_;
|
||||
if (val <= 0x7F) {
|
||||
buffer[0] = val;
|
||||
return;
|
||||
}
|
||||
size_t i = 0;
|
||||
while (val && i < len) {
|
||||
uint8_t temp = val & 0x7F;
|
||||
val >>= 7;
|
||||
if (val) {
|
||||
buffer[i++] = temp | 0x80;
|
||||
} else {
|
||||
buffer[i++] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
void encode(std::vector<uint8_t> &out) {
|
||||
uint64_t val = this->value_;
|
||||
if (val <= 0x7F) {
|
||||
out.push_back(val);
|
||||
return;
|
||||
}
|
||||
while (val) {
|
||||
uint8_t temp = val & 0x7F;
|
||||
val >>= 7;
|
||||
if (val) {
|
||||
out.push_back(temp | 0x80);
|
||||
} else {
|
||||
out.push_back(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
#ifdef USE_API_VARINT64
|
||||
uint64_t value_;
|
||||
#else
|
||||
uint32_t value_;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Forward declarations for decode_to_message, encode_message and encode_packed_sint32
|
||||
@@ -254,10 +237,27 @@ class Proto32Bit {
|
||||
|
||||
class ProtoWriteBuffer {
|
||||
public:
|
||||
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
|
||||
void write(uint8_t value) { this->buffer_->push_back(value); }
|
||||
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
|
||||
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
|
||||
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer), pos_(buffer->data() + buffer->size()) {}
|
||||
ProtoWriteBuffer(std::vector<uint8_t> *buffer, size_t write_pos)
|
||||
: buffer_(buffer), pos_(buffer->data() + write_pos) {}
|
||||
void encode_varint_raw(uint32_t value) {
|
||||
while (value > 0x7F) {
|
||||
this->debug_check_bounds_(1);
|
||||
*this->pos_++ = static_cast<uint8_t>(value | 0x80);
|
||||
value >>= 7;
|
||||
}
|
||||
this->debug_check_bounds_(1);
|
||||
*this->pos_++ = static_cast<uint8_t>(value);
|
||||
}
|
||||
void encode_varint_raw_64(uint64_t value) {
|
||||
while (value > 0x7F) {
|
||||
this->debug_check_bounds_(1);
|
||||
*this->pos_++ = static_cast<uint8_t>(value | 0x80);
|
||||
value >>= 7;
|
||||
}
|
||||
this->debug_check_bounds_(1);
|
||||
*this->pos_++ = static_cast<uint8_t>(value);
|
||||
}
|
||||
/**
|
||||
* Encode a field key (tag/wire type combination).
|
||||
*
|
||||
@@ -270,23 +270,18 @@ class ProtoWriteBuffer {
|
||||
*
|
||||
* Following https://protobuf.dev/programming-guides/encoding/#structure
|
||||
*/
|
||||
void encode_field_raw(uint32_t field_id, uint32_t type) {
|
||||
uint32_t val = (field_id << 3) | (type & WIRE_TYPE_MASK);
|
||||
this->encode_varint_raw(val);
|
||||
}
|
||||
void encode_field_raw(uint32_t field_id, uint32_t type) { this->encode_varint_raw((field_id << 3) | type); }
|
||||
void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
|
||||
if (len == 0 && !force)
|
||||
return;
|
||||
|
||||
this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
|
||||
this->encode_varint_raw(len);
|
||||
|
||||
// Using resize + memcpy instead of insert provides significant performance improvement:
|
||||
// ~10-11x faster for 16-32 byte strings, ~3x faster for 64-byte strings
|
||||
// as it avoids iterator checks and potential element moves that insert performs
|
||||
size_t old_size = this->buffer_->size();
|
||||
this->buffer_->resize(old_size + len);
|
||||
std::memcpy(this->buffer_->data() + old_size, string, len);
|
||||
// Direct memcpy into pre-sized buffer — avoids push_back() per-byte capacity checks
|
||||
// and vector::insert() iterator overhead. ~10-11x faster for 16-32 byte strings.
|
||||
this->debug_check_bounds_(len);
|
||||
std::memcpy(this->pos_, string, len);
|
||||
this->pos_ += len;
|
||||
}
|
||||
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
|
||||
this->encode_string(field_id, value.data(), value.size(), force);
|
||||
@@ -307,23 +302,32 @@ class ProtoWriteBuffer {
|
||||
if (value == 0 && !force)
|
||||
return;
|
||||
this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
|
||||
this->encode_varint_raw(ProtoVarInt(value));
|
||||
this->encode_varint_raw_64(value);
|
||||
}
|
||||
void encode_bool(uint32_t field_id, bool value, bool force = false) {
|
||||
if (!value && !force)
|
||||
return;
|
||||
this->encode_field_raw(field_id, 0); // type 0: Varint - bool
|
||||
this->write(0x01);
|
||||
this->debug_check_bounds_(1);
|
||||
*this->pos_++ = value ? 0x01 : 0x00;
|
||||
}
|
||||
void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
|
||||
// noinline: 51 call sites; inlining causes net code growth vs a single out-of-line copy
|
||||
__attribute__((noinline)) void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
|
||||
if (value == 0 && !force)
|
||||
return;
|
||||
|
||||
this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
|
||||
this->write((value >> 0) & 0xFF);
|
||||
this->write((value >> 8) & 0xFF);
|
||||
this->write((value >> 16) & 0xFF);
|
||||
this->write((value >> 24) & 0xFF);
|
||||
this->debug_check_bounds_(4);
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
// Protobuf fixed32 is little-endian, so direct copy works
|
||||
std::memcpy(this->pos_, &value, 4);
|
||||
this->pos_ += 4;
|
||||
#else
|
||||
*this->pos_++ = (value >> 0) & 0xFF;
|
||||
*this->pos_++ = (value >> 8) & 0xFF;
|
||||
*this->pos_++ = (value >> 16) & 0xFF;
|
||||
*this->pos_++ = (value >> 24) & 0xFF;
|
||||
#endif
|
||||
}
|
||||
// NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
|
||||
// not supported to reduce overhead on embedded systems. All ESPHome devices are
|
||||
@@ -359,11 +363,20 @@ class ProtoWriteBuffer {
|
||||
}
|
||||
/// Encode a packed repeated sint32 field (zero-copy from vector)
|
||||
void encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values);
|
||||
void encode_message(uint32_t field_id, const ProtoMessage &value);
|
||||
/// Encode a nested message field (force=true for repeated, false for singular)
|
||||
void encode_message(uint32_t field_id, const ProtoMessage &value, bool force = true);
|
||||
std::vector<uint8_t> *get_buffer() const { return buffer_; }
|
||||
|
||||
protected:
|
||||
#ifdef ESPHOME_DEBUG_API
|
||||
void debug_check_bounds_(size_t bytes, const char *caller = __builtin_FUNCTION());
|
||||
void debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual);
|
||||
#else
|
||||
void debug_check_bounds_([[maybe_unused]] size_t bytes) {}
|
||||
#endif
|
||||
|
||||
std::vector<uint8_t> *buffer_;
|
||||
uint8_t *pos_;
|
||||
};
|
||||
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
@@ -441,9 +454,11 @@ class ProtoMessage {
|
||||
public:
|
||||
virtual ~ProtoMessage() = default;
|
||||
// Default implementation for messages with no fields
|
||||
virtual void encode(ProtoWriteBuffer buffer) const {}
|
||||
virtual void encode(ProtoWriteBuffer &buffer) const {}
|
||||
// Default implementation for messages with no fields
|
||||
virtual void calculate_size(ProtoSize &size) const {}
|
||||
// Convenience: calculate and return size directly (defined after ProtoSize)
|
||||
uint32_t calculated_size() const;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
virtual const char *dump_to(DumpBuffer &out) const = 0;
|
||||
virtual const char *message_name() const { return "unknown"; }
|
||||
@@ -902,6 +917,14 @@ class ProtoSize {
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation of methods that depend on ProtoSize being fully defined
|
||||
|
||||
inline uint32_t ProtoMessage::calculated_size() const {
|
||||
ProtoSize size;
|
||||
this->calculate_size(size);
|
||||
return size.get_size();
|
||||
}
|
||||
|
||||
// Implementation of encode_packed_sint32 - must be after ProtoSize is defined
|
||||
inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values) {
|
||||
if (values.empty())
|
||||
@@ -922,29 +945,31 @@ inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std:
|
||||
}
|
||||
|
||||
// Implementation of encode_message - must be after ProtoMessage is defined
|
||||
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value) {
|
||||
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
|
||||
|
||||
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) {
|
||||
// Calculate the message size first
|
||||
ProtoSize msg_size;
|
||||
value.calculate_size(msg_size);
|
||||
uint32_t msg_length_bytes = msg_size.get_size();
|
||||
|
||||
// Calculate how many bytes the length varint needs
|
||||
uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
|
||||
// Skip empty singular messages (matches add_message_field which skips when nested_size == 0)
|
||||
// Repeated messages (force=true) are always encoded since an empty item is meaningful
|
||||
if (msg_length_bytes == 0 && !force)
|
||||
return;
|
||||
|
||||
// Reserve exact space for the length varint
|
||||
size_t begin = this->buffer_->size();
|
||||
this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
|
||||
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
|
||||
|
||||
// Write the length varint directly
|
||||
ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes);
|
||||
// Write the length varint directly through pos_
|
||||
this->encode_varint_raw(msg_length_bytes);
|
||||
|
||||
// Now encode the message content - it will append to the buffer
|
||||
// Encode nested message - pos_ advances directly through the reference
|
||||
#ifdef ESPHOME_DEBUG_API
|
||||
uint8_t *start = this->pos_;
|
||||
value.encode(*this);
|
||||
|
||||
// Verify that the encoded size matches what we calculated
|
||||
assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
|
||||
if (static_cast<uint32_t>(this->pos_ - start) != msg_length_bytes)
|
||||
this->debug_check_encode_size_(field_id, msg_length_bytes, this->pos_ - start);
|
||||
#else
|
||||
value.encode(*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
|
||||
|
||||
@@ -16,7 +16,7 @@ class APIConnection;
|
||||
return this->client_->send_##entity_type##_state(entity); \
|
||||
}
|
||||
|
||||
class InitialStateIterator : public ComponentIterator {
|
||||
class InitialStateIterator final : public ComponentIterator {
|
||||
public:
|
||||
InitialStateIterator(APIConnection *client);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
|
||||
@@ -230,7 +230,7 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
|
||||
void set_is_optional_mode(bool is_optional) { this->is_optional_mode_ = is_optional; }
|
||||
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
void set_data(std::function<void(Ts..., JsonObject)> func) {
|
||||
void set_data(std::function<void(Ts..., JsonObject)> &&func) {
|
||||
this->json_builder_ = std::move(func);
|
||||
this->has_data_ = true;
|
||||
}
|
||||
@@ -264,9 +264,9 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
|
||||
// Build and send JSON response
|
||||
json::JsonBuilder builder;
|
||||
this->json_builder_(x..., builder.root());
|
||||
std::string json_str = builder.serialize();
|
||||
auto json_buf = builder.serialize();
|
||||
this->parent_->send_action_response(call_id, success, StringRef(error_message),
|
||||
reinterpret_cast<const uint8_t *>(json_str.data()), json_str.size());
|
||||
reinterpret_cast<const uint8_t *>(json_buf.data()), json_buf.size());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -307,9 +307,9 @@ void AS3935Component::tune_antenna() {
|
||||
uint8_t tune_val = this->read_capacitance();
|
||||
ESP_LOGI(TAG,
|
||||
"Starting antenna tuning\n"
|
||||
"Division Ratio is set to: %d\n"
|
||||
"Internal Capacitor is set to: %d\n"
|
||||
"Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio",
|
||||
" Division Ratio is set to: %d\n"
|
||||
" Internal Capacitor is set to: %d\n"
|
||||
" Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio",
|
||||
div_ratio, tune_val);
|
||||
this->display_oscillator(true, ANTFREQ);
|
||||
}
|
||||
|
||||
@@ -77,14 +77,14 @@ void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); }
|
||||
bool AT581XComponent::i2c_write_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"Writing new config for AT581X\n"
|
||||
"Frequency: %dMHz\n"
|
||||
"Sensing distance: %d\n"
|
||||
"Power: %dµA\n"
|
||||
"Gain: %d\n"
|
||||
"Trigger base time: %dms\n"
|
||||
"Trigger keep time: %dms\n"
|
||||
"Protect time: %dms\n"
|
||||
"Self check time: %dms",
|
||||
" Frequency: %dMHz\n"
|
||||
" Sensing distance: %d\n"
|
||||
" Power: %dµA\n"
|
||||
" Gain: %d\n"
|
||||
" Trigger base time: %dms\n"
|
||||
" Trigger keep time: %dms\n"
|
||||
" Protect time: %dms\n"
|
||||
" Self check time: %dms",
|
||||
this->freq_, this->delta_, this->power_, this->gain_, this->trigger_base_time_ms_,
|
||||
this->trigger_keep_time_ms_, this->protect_time_ms_, this->self_check_time_ms_);
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import add_idf_component, include_builtin_idf_component
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_NUM_CHANNELS, CONF_SAMPLE_RATE
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
|
||||
CODEOWNERS = ["@kahrendt"]
|
||||
DOMAIN = "audio"
|
||||
audio_ns = cg.esphome_ns.namespace("audio")
|
||||
|
||||
AudioFile = audio_ns.struct("AudioFile")
|
||||
@@ -14,9 +18,38 @@ AUDIO_FILE_TYPE_ENUM = {
|
||||
"WAV": AudioFileType.WAV,
|
||||
"MP3": AudioFileType.MP3,
|
||||
"FLAC": AudioFileType.FLAC,
|
||||
"OPUS": AudioFileType.OPUS,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class AudioData:
|
||||
flac_support: bool = False
|
||||
mp3_support: bool = False
|
||||
opus_support: bool = False
|
||||
|
||||
|
||||
def _get_data() -> AudioData:
|
||||
if DOMAIN not in CORE.data:
|
||||
CORE.data[DOMAIN] = AudioData()
|
||||
return CORE.data[DOMAIN]
|
||||
|
||||
|
||||
def request_flac_support() -> None:
|
||||
"""Request FLAC codec support for audio decoding."""
|
||||
_get_data().flac_support = True
|
||||
|
||||
|
||||
def request_mp3_support() -> None:
|
||||
"""Request MP3 codec support for audio decoding."""
|
||||
_get_data().mp3_support = True
|
||||
|
||||
|
||||
def request_opus_support() -> None:
|
||||
"""Request Opus codec support for audio decoding."""
|
||||
_get_data().opus_support = True
|
||||
|
||||
|
||||
CONF_MIN_BITS_PER_SAMPLE = "min_bits_per_sample"
|
||||
CONF_MAX_BITS_PER_SAMPLE = "max_bits_per_sample"
|
||||
CONF_MIN_CHANNELS = "min_channels"
|
||||
@@ -173,3 +206,12 @@ async def to_code(config):
|
||||
name="esphome/esp-audio-libs",
|
||||
ref="2.0.3",
|
||||
)
|
||||
|
||||
data = _get_data()
|
||||
if data.flac_support:
|
||||
cg.add_define("USE_AUDIO_FLAC_SUPPORT")
|
||||
if data.mp3_support:
|
||||
cg.add_define("USE_AUDIO_MP3_SUPPORT")
|
||||
if data.opus_support:
|
||||
cg.add_define("USE_AUDIO_OPUS_SUPPORT")
|
||||
add_idf_component(name="esphome/micro-opus", ref="0.3.3")
|
||||
|
||||
@@ -46,6 +46,10 @@ const char *audio_file_type_to_string(AudioFileType file_type) {
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
case AudioFileType::MP3:
|
||||
return "MP3";
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
case AudioFileType::OPUS:
|
||||
return "OPUS";
|
||||
#endif
|
||||
case AudioFileType::WAV:
|
||||
return "WAV";
|
||||
|
||||
@@ -112,6 +112,9 @@ enum class AudioFileType : uint8_t {
|
||||
#endif
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
MP3,
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
OPUS,
|
||||
#endif
|
||||
WAV,
|
||||
};
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace audio {
|
||||
|
||||
static const char *const TAG = "audio.decoder";
|
||||
|
||||
static const uint32_t DECODING_TIMEOUT_MS = 50; // The decode function will yield after this duration
|
||||
static const uint32_t READ_WRITE_TIMEOUT_MS = 20; // Timeout for transferring audio data
|
||||
|
||||
static const uint32_t MAX_POTENTIALLY_FAILED_COUNT = 10;
|
||||
|
||||
AudioDecoder::AudioDecoder(size_t input_buffer_size, size_t output_buffer_size) {
|
||||
this->input_transfer_buffer_ = AudioSourceTransferBuffer::create(input_buffer_size);
|
||||
AudioDecoder::AudioDecoder(size_t input_buffer_size, size_t output_buffer_size)
|
||||
: input_buffer_size_(input_buffer_size) {
|
||||
this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(output_buffer_size);
|
||||
}
|
||||
|
||||
@@ -26,11 +29,20 @@ AudioDecoder::~AudioDecoder() {
|
||||
}
|
||||
|
||||
esp_err_t AudioDecoder::add_source(std::weak_ptr<RingBuffer> &input_ring_buffer) {
|
||||
if (this->input_transfer_buffer_ != nullptr) {
|
||||
this->input_transfer_buffer_->set_source(input_ring_buffer);
|
||||
return ESP_OK;
|
||||
auto source = AudioSourceTransferBuffer::create(this->input_buffer_size_);
|
||||
if (source == nullptr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
source->set_source(input_ring_buffer);
|
||||
this->input_buffer_ = std::move(source);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t AudioDecoder::add_source(const uint8_t *data_pointer, size_t length) {
|
||||
auto source = make_unique<ConstAudioSourceBuffer>();
|
||||
source->set_data(data_pointer, length);
|
||||
this->input_buffer_ = std::move(source);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t AudioDecoder::add_sink(std::weak_ptr<RingBuffer> &output_ring_buffer) {
|
||||
@@ -51,8 +63,16 @@ esp_err_t AudioDecoder::add_sink(speaker::Speaker *speaker) {
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t AudioDecoder::add_sink(AudioSinkCallback *callback) {
|
||||
if (this->output_transfer_buffer_ != nullptr) {
|
||||
this->output_transfer_buffer_->set_sink(callback);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
|
||||
if ((this->input_transfer_buffer_ == nullptr) || (this->output_transfer_buffer_ == nullptr)) {
|
||||
if (this->output_transfer_buffer_ == nullptr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -65,6 +85,10 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
|
||||
#ifdef USE_AUDIO_FLAC_SUPPORT
|
||||
case AudioFileType::FLAC:
|
||||
this->flac_decoder_ = make_unique<esp_audio_libs::flac::FLACDecoder>();
|
||||
// CRC check slows down decoding by 15-20% on an ESP32-S3. FLAC sources in ESPHome are either from an http source
|
||||
// or built into the firmware, so the data integrity is already verified by the time it gets to the decoder,
|
||||
// making the CRC check unnecessary.
|
||||
this->flac_decoder_->set_crc_check_enabled(false);
|
||||
this->free_buffer_required_ =
|
||||
this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
|
||||
break;
|
||||
@@ -79,6 +103,14 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
|
||||
// Always reallocate the output transfer buffer to the smallest necessary size
|
||||
this->output_transfer_buffer_->reallocate(this->free_buffer_required_);
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
case AudioFileType::OPUS:
|
||||
this->opus_decoder_ = make_unique<micro_opus::OggOpusDecoder>();
|
||||
this->free_buffer_required_ =
|
||||
this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
|
||||
this->decoder_buffers_internally_ = true;
|
||||
break;
|
||||
#endif
|
||||
case AudioFileType::WAV:
|
||||
this->wav_decoder_ = make_unique<esp_audio_libs::wav_decoder::WAVDecoder>();
|
||||
@@ -101,6 +133,10 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
|
||||
}
|
||||
|
||||
AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
if (this->input_buffer_ == nullptr) {
|
||||
return AudioDecoderState::FAILED;
|
||||
}
|
||||
|
||||
if (stop_gracefully) {
|
||||
if (this->output_transfer_buffer_->available() == 0) {
|
||||
if (this->end_of_file_) {
|
||||
@@ -108,7 +144,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
return AudioDecoderState::FINISHED;
|
||||
}
|
||||
|
||||
if (!this->input_transfer_buffer_->has_buffered_data()) {
|
||||
if (!this->input_buffer_->has_buffered_data()) {
|
||||
// If all the internal buffers are empty, the decoding is done
|
||||
return AudioDecoderState::FINISHED;
|
||||
}
|
||||
@@ -158,10 +194,11 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
// Decode more audio
|
||||
|
||||
// Only shift data on the first loop iteration to avoid unnecessary, slow moves
|
||||
size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS),
|
||||
first_loop_iteration);
|
||||
// If the decoder buffers internally, then never shift
|
||||
size_t bytes_read = this->input_buffer_->fill(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS),
|
||||
first_loop_iteration && !this->decoder_buffers_internally_);
|
||||
|
||||
if (!first_loop_iteration && (this->input_transfer_buffer_->available() < bytes_processed)) {
|
||||
if (!first_loop_iteration && (this->input_buffer_->available() < bytes_processed)) {
|
||||
// Less data is available than what was processed in last iteration, so don't attempt to decode.
|
||||
// This attempts to avoid the decoder from consistently trying to decode an incomplete frame. The transfer buffer
|
||||
// will shift the remaining data to the start and copy more from the source the next time the decode function is
|
||||
@@ -169,19 +206,21 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_available_before_processing = this->input_transfer_buffer_->available();
|
||||
bytes_available_before_processing = this->input_buffer_->available();
|
||||
|
||||
if ((this->potentially_failed_count_ > 0) && (bytes_read == 0)) {
|
||||
// Failed to decode in last attempt and there is no new data
|
||||
|
||||
if ((this->input_transfer_buffer_->free() == 0) && first_loop_iteration) {
|
||||
// The input buffer is full. Since it previously failed on the exact same data, we can never recover
|
||||
if ((this->input_buffer_->free() == 0) && first_loop_iteration) {
|
||||
// The input buffer is full (or read-only, e.g. const flash source). Since it previously failed on the exact
|
||||
// same data, we can never recover. For const sources this is correct: the entire file is already available, so
|
||||
// a decode failure is genuine, not a transient out-of-data condition.
|
||||
state = FileDecoderState::FAILED;
|
||||
} else {
|
||||
// Attempt to get more data next time
|
||||
state = FileDecoderState::IDLE;
|
||||
}
|
||||
} else if (this->input_transfer_buffer_->available() == 0) {
|
||||
} else if (this->input_buffer_->available() == 0) {
|
||||
// No data to decode, attempt to get more data next time
|
||||
state = FileDecoderState::IDLE;
|
||||
} else {
|
||||
@@ -195,6 +234,11 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
case AudioFileType::MP3:
|
||||
state = this->decode_mp3_();
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
case AudioFileType::OPUS:
|
||||
state = this->decode_opus_();
|
||||
break;
|
||||
#endif
|
||||
case AudioFileType::WAV:
|
||||
state = this->decode_wav_();
|
||||
@@ -207,7 +251,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
}
|
||||
|
||||
first_loop_iteration = false;
|
||||
bytes_processed = bytes_available_before_processing - this->input_transfer_buffer_->available();
|
||||
bytes_processed = bytes_available_before_processing - this->input_buffer_->available();
|
||||
|
||||
if (state == FileDecoderState::POTENTIALLY_FAILED) {
|
||||
++this->potentially_failed_count_;
|
||||
@@ -226,8 +270,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
||||
FileDecoderState AudioDecoder::decode_flac_() {
|
||||
if (!this->audio_stream_info_.has_value()) {
|
||||
// Header hasn't been read
|
||||
auto result = this->flac_decoder_->read_header(this->input_transfer_buffer_->get_buffer_start(),
|
||||
this->input_transfer_buffer_->available());
|
||||
auto result = this->flac_decoder_->read_header(this->input_buffer_->data(), this->input_buffer_->available());
|
||||
|
||||
if (result > esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
|
||||
// Serrious error reading FLAC header, there is no recovery
|
||||
@@ -235,7 +278,7 @@ FileDecoderState AudioDecoder::decode_flac_() {
|
||||
}
|
||||
|
||||
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
|
||||
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
|
||||
this->input_buffer_->consume(bytes_consumed);
|
||||
|
||||
if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
|
||||
return FileDecoderState::MORE_TO_PROCESS;
|
||||
@@ -256,8 +299,7 @@ FileDecoderState AudioDecoder::decode_flac_() {
|
||||
}
|
||||
|
||||
uint32_t output_samples = 0;
|
||||
auto result = this->flac_decoder_->decode_frame(this->input_transfer_buffer_->get_buffer_start(),
|
||||
this->input_transfer_buffer_->available(),
|
||||
auto result = this->flac_decoder_->decode_frame(this->input_buffer_->data(), this->input_buffer_->available(),
|
||||
this->output_transfer_buffer_->get_buffer_end(), &output_samples);
|
||||
|
||||
if (result == esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
|
||||
@@ -266,7 +308,7 @@ FileDecoderState AudioDecoder::decode_flac_() {
|
||||
}
|
||||
|
||||
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
|
||||
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
|
||||
this->input_buffer_->consume(bytes_consumed);
|
||||
|
||||
if (result > esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
|
||||
// Corrupted frame, don't retry with current buffer content, wait for new sync
|
||||
@@ -288,26 +330,25 @@ FileDecoderState AudioDecoder::decode_flac_() {
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
FileDecoderState AudioDecoder::decode_mp3_() {
|
||||
// Look for the next sync word
|
||||
int buffer_length = (int) this->input_transfer_buffer_->available();
|
||||
int32_t offset =
|
||||
esp_audio_libs::helix_decoder::MP3FindSyncWord(this->input_transfer_buffer_->get_buffer_start(), buffer_length);
|
||||
int buffer_length = (int) this->input_buffer_->available();
|
||||
int32_t offset = esp_audio_libs::helix_decoder::MP3FindSyncWord(this->input_buffer_->data(), buffer_length);
|
||||
|
||||
if (offset < 0) {
|
||||
// New data may have the sync word
|
||||
this->input_transfer_buffer_->decrease_buffer_length(buffer_length);
|
||||
this->input_buffer_->consume(buffer_length);
|
||||
return FileDecoderState::POTENTIALLY_FAILED;
|
||||
}
|
||||
|
||||
// Advance read pointer to match the offset for the syncword
|
||||
this->input_transfer_buffer_->decrease_buffer_length(offset);
|
||||
const uint8_t *buffer_start = this->input_transfer_buffer_->get_buffer_start();
|
||||
this->input_buffer_->consume(offset);
|
||||
const uint8_t *buffer_start = this->input_buffer_->data();
|
||||
|
||||
buffer_length = (int) this->input_transfer_buffer_->available();
|
||||
buffer_length = (int) this->input_buffer_->available();
|
||||
int err = esp_audio_libs::helix_decoder::MP3Decode(this->mp3_decoder_, &buffer_start, &buffer_length,
|
||||
(int16_t *) this->output_transfer_buffer_->get_buffer_end(), 0);
|
||||
|
||||
size_t consumed = this->input_transfer_buffer_->available() - buffer_length;
|
||||
this->input_transfer_buffer_->decrease_buffer_length(consumed);
|
||||
size_t consumed = this->input_buffer_->available() - buffer_length;
|
||||
this->input_buffer_->consume(consumed);
|
||||
|
||||
if (err) {
|
||||
switch (err) {
|
||||
@@ -339,15 +380,53 @@ FileDecoderState AudioDecoder::decode_mp3_() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
FileDecoderState AudioDecoder::decode_opus_() {
|
||||
bool processed_header = this->opus_decoder_->is_initialized();
|
||||
|
||||
size_t bytes_consumed, samples_decoded;
|
||||
|
||||
micro_opus::OggOpusResult result = this->opus_decoder_->decode(
|
||||
this->input_buffer_->data(), this->input_buffer_->available(), this->output_transfer_buffer_->get_buffer_end(),
|
||||
this->output_transfer_buffer_->free(), bytes_consumed, samples_decoded);
|
||||
|
||||
if (result == micro_opus::OGG_OPUS_OK) {
|
||||
if (!processed_header && this->opus_decoder_->is_initialized()) {
|
||||
// Header processed and stream info is available
|
||||
this->audio_stream_info_ =
|
||||
audio::AudioStreamInfo(this->opus_decoder_->get_bit_depth(), this->opus_decoder_->get_channels(),
|
||||
this->opus_decoder_->get_sample_rate());
|
||||
}
|
||||
if (samples_decoded > 0 && this->audio_stream_info_.has_value()) {
|
||||
// Some audio was processed
|
||||
this->output_transfer_buffer_->increase_buffer_length(
|
||||
this->audio_stream_info_.value().frames_to_bytes(samples_decoded));
|
||||
}
|
||||
this->input_buffer_->consume(bytes_consumed);
|
||||
} else if (result == micro_opus::OGG_OPUS_OUTPUT_BUFFER_TOO_SMALL) {
|
||||
// Reallocate to decode the packet on the next call
|
||||
this->free_buffer_required_ = this->opus_decoder_->get_required_output_buffer_size();
|
||||
if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) {
|
||||
// Couldn't reallocate output buffer
|
||||
return FileDecoderState::FAILED;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Opus decoder failed: %" PRId8, result);
|
||||
return FileDecoderState::POTENTIALLY_FAILED;
|
||||
}
|
||||
return FileDecoderState::MORE_TO_PROCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
FileDecoderState AudioDecoder::decode_wav_() {
|
||||
if (!this->audio_stream_info_.has_value()) {
|
||||
// Header hasn't been processed
|
||||
|
||||
esp_audio_libs::wav_decoder::WAVDecoderResult result = this->wav_decoder_->decode_header(
|
||||
this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available());
|
||||
esp_audio_libs::wav_decoder::WAVDecoderResult result =
|
||||
this->wav_decoder_->decode_header(this->input_buffer_->data(), this->input_buffer_->available());
|
||||
|
||||
if (result == esp_audio_libs::wav_decoder::WAV_DECODER_SUCCESS_IN_DATA) {
|
||||
this->input_transfer_buffer_->decrease_buffer_length(this->wav_decoder_->bytes_processed());
|
||||
this->input_buffer_->consume(this->wav_decoder_->bytes_processed());
|
||||
|
||||
this->audio_stream_info_ = audio::AudioStreamInfo(
|
||||
this->wav_decoder_->bits_per_sample(), this->wav_decoder_->num_channels(), this->wav_decoder_->sample_rate());
|
||||
@@ -363,7 +442,7 @@ FileDecoderState AudioDecoder::decode_wav_() {
|
||||
}
|
||||
} else {
|
||||
if (!this->wav_has_known_end_ || (this->wav_bytes_left_ > 0)) {
|
||||
size_t bytes_to_copy = this->input_transfer_buffer_->available();
|
||||
size_t bytes_to_copy = this->input_buffer_->available();
|
||||
|
||||
if (this->wav_has_known_end_) {
|
||||
bytes_to_copy = std::min(bytes_to_copy, this->wav_bytes_left_);
|
||||
@@ -372,9 +451,8 @@ FileDecoderState AudioDecoder::decode_wav_() {
|
||||
bytes_to_copy = std::min(bytes_to_copy, this->output_transfer_buffer_->free());
|
||||
|
||||
if (bytes_to_copy > 0) {
|
||||
std::memcpy(this->output_transfer_buffer_->get_buffer_end(), this->input_transfer_buffer_->get_buffer_start(),
|
||||
bytes_to_copy);
|
||||
this->input_transfer_buffer_->decrease_buffer_length(bytes_to_copy);
|
||||
std::memcpy(this->output_transfer_buffer_->get_buffer_end(), this->input_buffer_->data(), bytes_to_copy);
|
||||
this->input_buffer_->consume(bytes_to_copy);
|
||||
this->output_transfer_buffer_->increase_buffer_length(bytes_to_copy);
|
||||
if (this->wav_has_known_end_) {
|
||||
this->wav_bytes_left_ -= bytes_to_copy;
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
#endif
|
||||
#include <wav_decoder.h>
|
||||
|
||||
// micro-opus
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
#include <micro_opus/ogg_opus_decoder.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace audio {
|
||||
|
||||
@@ -45,17 +50,17 @@ enum class FileDecoderState : uint8_t {
|
||||
class AudioDecoder {
|
||||
/*
|
||||
* @brief Class that facilitates decoding an audio file.
|
||||
* The audio file is read from a ring buffer source, decoded, and sent to an audio sink (ring buffer or speaker
|
||||
* component).
|
||||
* Supports wav, flac, and mp3 formats.
|
||||
* The audio file is read from a source (ring buffer or const data pointer), decoded, and sent to an audio sink
|
||||
* (ring buffer, speaker component, or callback).
|
||||
* Supports wav, flac, mp3, and ogg opus formats.
|
||||
*/
|
||||
public:
|
||||
/// @brief Allocates the input and output transfer buffers
|
||||
/// @brief Allocates the output transfer buffer and stores the input buffer size for later use by add_source()
|
||||
/// @param input_buffer_size Size of the input transfer buffer in bytes.
|
||||
/// @param output_buffer_size Size of the output transfer buffer in bytes.
|
||||
AudioDecoder(size_t input_buffer_size, size_t output_buffer_size);
|
||||
|
||||
/// @brief Deallocates the MP3 decoder (the flac and wav decoders are deallocated automatically)
|
||||
/// @brief Deallocates the MP3 decoder (the flac, opus, and wav decoders are deallocated automatically)
|
||||
~AudioDecoder();
|
||||
|
||||
/// @brief Adds a source ring buffer for raw file data. Takes ownership of the ring buffer in a shared_ptr.
|
||||
@@ -75,6 +80,17 @@ class AudioDecoder {
|
||||
esp_err_t add_sink(speaker::Speaker *speaker);
|
||||
#endif
|
||||
|
||||
/// @brief Adds a const data pointer as the source for raw file data. Does not allocate a transfer buffer.
|
||||
/// @param data_pointer Pointer to the const audio data (e.g., stored in flash memory)
|
||||
/// @param length Size of the data in bytes
|
||||
/// @return ESP_OK
|
||||
esp_err_t add_source(const uint8_t *data_pointer, size_t length);
|
||||
|
||||
/// @brief Adds a callback as the sink for decoded audio.
|
||||
/// @param callback Pointer to the AudioSinkCallback implementation
|
||||
/// @return ESP_OK if successful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated
|
||||
esp_err_t add_sink(AudioSinkCallback *callback);
|
||||
|
||||
/// @brief Sets up decoding the file
|
||||
/// @param audio_file_type AudioFileType of the file
|
||||
/// @return ESP_OK if successful, ESP_ERR_NO_MEM if the transfer buffers fail to allocate, or ESP_ERR_NOT_SUPPORTED if
|
||||
@@ -108,26 +124,33 @@ class AudioDecoder {
|
||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||
FileDecoderState decode_mp3_();
|
||||
esp_audio_libs::helix_decoder::HMP3Decoder mp3_decoder_;
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
FileDecoderState decode_opus_();
|
||||
std::unique_ptr<micro_opus::OggOpusDecoder> opus_decoder_;
|
||||
#endif
|
||||
FileDecoderState decode_wav_();
|
||||
|
||||
std::unique_ptr<AudioSourceTransferBuffer> input_transfer_buffer_;
|
||||
std::unique_ptr<AudioReadableBuffer> input_buffer_;
|
||||
std::unique_ptr<AudioSinkTransferBuffer> output_transfer_buffer_;
|
||||
|
||||
AudioFileType audio_file_type_{AudioFileType::NONE};
|
||||
optional<AudioStreamInfo> audio_stream_info_{};
|
||||
|
||||
size_t input_buffer_size_{0};
|
||||
size_t free_buffer_required_{0};
|
||||
size_t wav_bytes_left_{0};
|
||||
|
||||
uint32_t potentially_failed_count_{0};
|
||||
uint32_t accumulated_frames_written_{0};
|
||||
uint32_t playback_ms_{0};
|
||||
|
||||
bool end_of_file_{false};
|
||||
bool wav_has_known_end_{false};
|
||||
|
||||
bool pause_output_{false};
|
||||
bool decoder_buffers_internally_{false};
|
||||
|
||||
uint32_t accumulated_frames_written_{0};
|
||||
uint32_t playback_ms_{0};
|
||||
bool pause_output_{false};
|
||||
};
|
||||
} // namespace audio
|
||||
} // namespace esphome
|
||||
|
||||
@@ -197,6 +197,11 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
|
||||
else if (str_endswith_ignore_case(url, ".flac")) {
|
||||
file_type = AudioFileType::FLAC;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
else if (str_endswith_ignore_case(url, ".opus")) {
|
||||
file_type = AudioFileType::OPUS;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
file_type = AudioFileType::NONE;
|
||||
@@ -241,6 +246,14 @@ AudioFileType AudioReader::get_audio_type(const char *content_type) {
|
||||
if (strcasecmp(content_type, "audio/flac") == 0 || strcasecmp(content_type, "audio/x-flac") == 0) {
|
||||
return AudioFileType::FLAC;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||
// Match "audio/ogg" with a codecs parameter containing "opus"
|
||||
// Valid forms: audio/ogg;codecs=opus, audio/ogg; codecs="opus", etc.
|
||||
// Plain "audio/ogg" without a codecs parameter is not matched, as those are almost always Ogg Vorbis streams
|
||||
if (strncasecmp(content_type, "audio/ogg", 9) == 0 && strcasestr(content_type + 9, "opus") != nullptr) {
|
||||
return AudioFileType::OPUS;
|
||||
}
|
||||
#endif
|
||||
return AudioFileType::NONE;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -75,12 +77,32 @@ bool AudioTransferBuffer::has_buffered_data() const {
|
||||
}
|
||||
|
||||
bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
|
||||
if (this->buffer_length_ > 0) {
|
||||
// Buffer currently has data, so reallocation is impossible
|
||||
if (this->buffer_ == nullptr) {
|
||||
return this->allocate_buffer_(new_buffer_size);
|
||||
}
|
||||
|
||||
if (new_buffer_size < this->buffer_length_) {
|
||||
// New size is too small to hold existing data
|
||||
return false;
|
||||
}
|
||||
this->deallocate_buffer_();
|
||||
return this->allocate_buffer_(new_buffer_size);
|
||||
|
||||
// Shift existing data to the start of the buffer so realloc preserves it
|
||||
if ((this->buffer_length_ > 0) && (this->data_start_ != this->buffer_)) {
|
||||
std::memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||
this->data_start_ = this->buffer_;
|
||||
}
|
||||
|
||||
RAMAllocator<uint8_t> allocator;
|
||||
uint8_t *new_buffer = allocator.reallocate(this->buffer_, new_buffer_size);
|
||||
if (new_buffer == nullptr) {
|
||||
// Reallocation failed, but the original buffer is still valid
|
||||
return false;
|
||||
}
|
||||
|
||||
this->buffer_ = new_buffer;
|
||||
this->data_start_ = this->buffer_;
|
||||
this->buffer_size_ = new_buffer_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
|
||||
@@ -115,12 +137,12 @@ size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_
|
||||
if (pre_shift) {
|
||||
// Shift data in buffer to start
|
||||
if (this->buffer_length_ > 0) {
|
||||
memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||
std::memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||
}
|
||||
this->data_start_ = this->buffer_;
|
||||
}
|
||||
|
||||
size_t bytes_to_read = this->free();
|
||||
size_t bytes_to_read = AudioTransferBuffer::free();
|
||||
size_t bytes_read = 0;
|
||||
if (bytes_to_read > 0) {
|
||||
if (this->ring_buffer_.use_count() > 0) {
|
||||
@@ -143,6 +165,8 @@ size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait,
|
||||
if (this->ring_buffer_.use_count() > 0) {
|
||||
bytes_written =
|
||||
this->ring_buffer_->write_without_replacement((void *) this->data_start_, this->available(), ticks_to_wait);
|
||||
} else if (this->sink_callback_ != nullptr) {
|
||||
bytes_written = this->sink_callback_->audio_sink_write(this->data_start_, this->available(), ticks_to_wait);
|
||||
}
|
||||
|
||||
this->decrease_buffer_length(bytes_written);
|
||||
@@ -150,7 +174,7 @@ size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait,
|
||||
|
||||
if (post_shift) {
|
||||
// Shift unwritten data to the start of the buffer
|
||||
memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||
std::memmove(this->buffer_, this->data_start_, this->buffer_length_);
|
||||
this->data_start_ = this->buffer_;
|
||||
}
|
||||
|
||||
@@ -169,6 +193,21 @@ bool AudioSinkTransferBuffer::has_buffered_data() const {
|
||||
return (this->available() > 0);
|
||||
}
|
||||
|
||||
size_t AudioSourceTransferBuffer::free() const { return AudioTransferBuffer::free(); }
|
||||
|
||||
bool AudioSourceTransferBuffer::has_buffered_data() const { return AudioTransferBuffer::has_buffered_data(); }
|
||||
|
||||
void ConstAudioSourceBuffer::set_data(const uint8_t *data, size_t length) {
|
||||
this->data_start_ = data;
|
||||
this->length_ = length;
|
||||
}
|
||||
|
||||
void ConstAudioSourceBuffer::consume(size_t bytes) {
|
||||
bytes = std::min(bytes, this->length_);
|
||||
this->length_ -= bytes;
|
||||
this->data_start_ += bytes;
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
namespace esphome {
|
||||
namespace audio {
|
||||
|
||||
/// @brief Abstract interface for writing decoded audio data to a sink.
|
||||
class AudioSinkCallback {
|
||||
public:
|
||||
virtual size_t audio_sink_write(uint8_t *data, size_t length, TickType_t ticks_to_wait) = 0;
|
||||
};
|
||||
|
||||
class AudioTransferBuffer {
|
||||
/*
|
||||
* @brief Class that facilitates tranferring data between a buffer and an audio source or sink.
|
||||
@@ -26,7 +32,7 @@ class AudioTransferBuffer {
|
||||
/// @brief Destructor that deallocates the transfer buffer
|
||||
~AudioTransferBuffer();
|
||||
|
||||
/// @brief Returns a pointer to the start of the transfer buffer where available() bytes of exisiting data can be read
|
||||
/// @brief Returns a pointer to the start of the transfer buffer where available() bytes of existing data can be read
|
||||
uint8_t *get_buffer_start() const { return this->data_start_; }
|
||||
|
||||
/// @brief Returns a pointer to the end of the transfer buffer where free() bytes of new data can be written
|
||||
@@ -56,6 +62,9 @@ class AudioTransferBuffer {
|
||||
/// @return True if there is data, false otherwise.
|
||||
virtual bool has_buffered_data() const;
|
||||
|
||||
/// @brief Reallocates the transfer buffer, preserving any existing data.
|
||||
/// @param new_buffer_size The new size in bytes. Must be at least as large as available().
|
||||
/// @return True if successful, false otherwise. On failure, the original buffer remains valid.
|
||||
bool reallocate(size_t new_buffer_size);
|
||||
|
||||
protected:
|
||||
@@ -105,6 +114,10 @@ class AudioSinkTransferBuffer : public AudioTransferBuffer {
|
||||
void set_sink(speaker::Speaker *speaker) { this->speaker_ = speaker; }
|
||||
#endif
|
||||
|
||||
/// @brief Adds a callback as the transfer buffer's sink.
|
||||
/// @param callback Pointer to the AudioSinkCallback implementation
|
||||
void set_sink(AudioSinkCallback *callback) { this->sink_callback_ = callback; }
|
||||
|
||||
void clear_buffered_data() override;
|
||||
|
||||
bool has_buffered_data() const override;
|
||||
@@ -113,12 +126,44 @@ class AudioSinkTransferBuffer : public AudioTransferBuffer {
|
||||
#ifdef USE_SPEAKER
|
||||
speaker::Speaker *speaker_{nullptr};
|
||||
#endif
|
||||
AudioSinkCallback *sink_callback_{nullptr};
|
||||
};
|
||||
|
||||
class AudioSourceTransferBuffer : public AudioTransferBuffer {
|
||||
/// @brief Abstract interface for reading audio data from a buffer.
|
||||
/// Provides a common read interface for both mutable transfer buffers and read-only const buffers.
|
||||
class AudioReadableBuffer {
|
||||
public:
|
||||
virtual ~AudioReadableBuffer() = default;
|
||||
|
||||
/// @brief Returns a pointer to the start of readable data
|
||||
virtual const uint8_t *data() const = 0;
|
||||
|
||||
/// @brief Returns the number of bytes available to read
|
||||
virtual size_t available() const = 0;
|
||||
|
||||
/// @brief Returns the number of free bytes available to write. Defaults to 0 for read-only buffers.
|
||||
virtual size_t free() const { return 0; }
|
||||
|
||||
/// @brief Advances past consumed data
|
||||
/// @param bytes Number of bytes consumed
|
||||
virtual void consume(size_t bytes) = 0;
|
||||
|
||||
/// @brief Tests if there is any buffered data
|
||||
virtual bool has_buffered_data() const = 0;
|
||||
|
||||
/// @brief Refills the buffer from its source. No-op by default for read-only buffers.
|
||||
/// @param ticks_to_wait FreeRTOS ticks to block while waiting for data
|
||||
/// @param pre_shift If true, shifts existing data to the start of the buffer before reading
|
||||
/// @return Number of bytes read
|
||||
virtual size_t fill(TickType_t ticks_to_wait, bool pre_shift) { return 0; }
|
||||
size_t fill(TickType_t ticks_to_wait) { return this->fill(ticks_to_wait, true); }
|
||||
};
|
||||
|
||||
class AudioSourceTransferBuffer : public AudioTransferBuffer, public AudioReadableBuffer {
|
||||
/*
|
||||
* @brief A class that implements a transfer buffer for audio sources.
|
||||
* Supports reading audio data from a ring buffer into the transfer buffer for processing.
|
||||
* Implements AudioReadableBuffer for use by consumers that only need read access.
|
||||
*/
|
||||
public:
|
||||
/// @brief Creates a new source transfer buffer.
|
||||
@@ -126,7 +171,7 @@ class AudioSourceTransferBuffer : public AudioTransferBuffer {
|
||||
/// @return unique_ptr if successfully allocated, nullptr otherwise
|
||||
static std::unique_ptr<AudioSourceTransferBuffer> create(size_t buffer_size);
|
||||
|
||||
/// @brief Reads any available data from the sink into the transfer buffer.
|
||||
/// @brief Reads any available data from the source into the transfer buffer.
|
||||
/// @param ticks_to_wait FreeRTOS ticks to block while waiting for the source to have enough data
|
||||
/// @param pre_shift If true, any unwritten data is moved to the start of the buffer before transferring from the
|
||||
/// source. Defaults to true.
|
||||
@@ -136,6 +181,36 @@ class AudioSourceTransferBuffer : public AudioTransferBuffer {
|
||||
/// @brief Adds a ring buffer as the transfer buffer's source.
|
||||
/// @param ring_buffer weak_ptr to the allocated ring buffer
|
||||
void set_source(const std::weak_ptr<RingBuffer> &ring_buffer) { this->ring_buffer_ = ring_buffer.lock(); };
|
||||
|
||||
// AudioReadableBuffer interface
|
||||
const uint8_t *data() const override { return this->data_start_; }
|
||||
size_t available() const override { return this->buffer_length_; }
|
||||
size_t free() const override;
|
||||
void consume(size_t bytes) override { this->decrease_buffer_length(bytes); }
|
||||
bool has_buffered_data() const override;
|
||||
size_t fill(TickType_t ticks_to_wait, bool pre_shift) override {
|
||||
return this->transfer_data_from_source(ticks_to_wait, pre_shift);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief A lightweight read-only audio buffer for const data sources (e.g., flash memory).
|
||||
/// Does not allocate memory or transfer data from external sources.
|
||||
class ConstAudioSourceBuffer : public AudioReadableBuffer {
|
||||
public:
|
||||
/// @brief Sets the data pointer and length for the buffer
|
||||
/// @param data Pointer to the const audio data
|
||||
/// @param length Size of the data in bytes
|
||||
void set_data(const uint8_t *data, size_t length);
|
||||
|
||||
// AudioReadableBuffer interface
|
||||
const uint8_t *data() const override { return this->data_start_; }
|
||||
size_t available() const override { return this->length_; }
|
||||
void consume(size_t bytes) override;
|
||||
bool has_buffered_data() const override { return this->length_ > 0; }
|
||||
|
||||
protected:
|
||||
const uint8_t *data_start_{nullptr};
|
||||
size_t length_{0};
|
||||
};
|
||||
|
||||
} // namespace audio
|
||||
|
||||
@@ -562,6 +562,7 @@ async def setup_binary_sensor_core_(var, config):
|
||||
if inverted := config.get(CONF_INVERTED):
|
||||
cg.add(var.set_inverted(inverted))
|
||||
if filters_config := config.get(CONF_FILTERS):
|
||||
cg.add_define("USE_BINARY_SENSOR_FILTER")
|
||||
filters = await cg.build_registry_list(FILTER_REGISTRY, filters_config)
|
||||
cg.add(var.add_filters(filters))
|
||||
|
||||
|
||||
@@ -29,10 +29,8 @@ void MultiClickTrigger::on_state_(bool state) {
|
||||
// Start matching
|
||||
MultiClickTriggerEvent evt = this->timing_[0];
|
||||
if (evt.state == state) {
|
||||
ESP_LOGV(TAG,
|
||||
"START min=%" PRIu32 " max=%" PRIu32 "\n"
|
||||
"Multi Click: Starting multi click action!",
|
||||
evt.min_length, evt.max_length);
|
||||
ESP_LOGV(TAG, "START min=%" PRIu32 " max=%" PRIu32, evt.min_length, evt.max_length);
|
||||
ESP_LOGV(TAG, "Multi Click: Starting multi click action!");
|
||||
this->at_index_ = 1;
|
||||
if (this->timing_.size() == 1 && evt.max_length == 4294967294UL) {
|
||||
this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
|
||||
|
||||
@@ -18,11 +18,15 @@ void log_binary_sensor(const char *tag, const char *prefix, const char *type, Bi
|
||||
}
|
||||
|
||||
void BinarySensor::publish_state(bool new_state) {
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
if (this->filter_list_ == nullptr) {
|
||||
#endif
|
||||
this->send_state_internal(new_state);
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
} else {
|
||||
this->filter_list_->input(new_state);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void BinarySensor::publish_initial_state(bool new_state) {
|
||||
this->invalidate_state();
|
||||
@@ -47,6 +51,7 @@ bool BinarySensor::set_new_state(const optional<bool> &new_state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
void BinarySensor::add_filter(Filter *filter) {
|
||||
filter->parent_ = this;
|
||||
if (this->filter_list_ == nullptr) {
|
||||
@@ -63,6 +68,7 @@ void BinarySensor::add_filters(std::initializer_list<Filter *> filters) {
|
||||
this->add_filter(filter);
|
||||
}
|
||||
}
|
||||
#endif // USE_BINARY_SENSOR_FILTER
|
||||
bool BinarySensor::is_status_binary_sensor() const { return false; }
|
||||
|
||||
} // namespace esphome::binary_sensor
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
#include "esphome/components/binary_sensor/filter.h"
|
||||
#endif
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
@@ -45,8 +47,10 @@ class BinarySensor : public StatefulEntityBase<bool>, public EntityBase_DeviceCl
|
||||
*/
|
||||
void publish_initial_state(bool new_state);
|
||||
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
void add_filter(Filter *filter);
|
||||
void add_filters(std::initializer_list<Filter *> filters);
|
||||
#endif
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
@@ -60,7 +64,9 @@ class BinarySensor : public StatefulEntityBase<bool>, public EntityBase_DeviceCl
|
||||
bool state{};
|
||||
|
||||
protected:
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
Filter *filter_list_{nullptr};
|
||||
#endif
|
||||
|
||||
bool set_new_state(const optional<bool> &new_state) override;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
|
||||
#include "filter.h"
|
||||
|
||||
#include "binary_sensor.h"
|
||||
@@ -142,3 +145,5 @@ optional<bool> SettleFilter::new_value(bool value) {
|
||||
float SettleFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
} // namespace esphome::binary_sensor
|
||||
|
||||
#endif // USE_BINARY_SENSOR_FILTER
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_BINARY_SENSOR_FILTER
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -138,3 +141,5 @@ class SettleFilter : public Filter, public Component {
|
||||
};
|
||||
|
||||
} // namespace esphome::binary_sensor
|
||||
|
||||
#endif // USE_BINARY_SENSOR_FILTER
|
||||
|
||||
@@ -182,7 +182,10 @@ void BL0940::recalibrate_() {
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Recalibrated reference values:\n"
|
||||
"Voltage: %f\n, Current: %f\n, Power: %f\n, Energy: %f\n",
|
||||
" Voltage: %f\n"
|
||||
" Current: %f\n"
|
||||
" Power: %f\n"
|
||||
" Energy: %f",
|
||||
this->voltage_reference_cal_, this->current_reference_cal_, this->power_reference_cal_,
|
||||
this->energy_reference_cal_);
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ void BL0942::loop() {
|
||||
return;
|
||||
}
|
||||
if (avail < sizeof(buffer)) {
|
||||
if (!this->rx_start_) {
|
||||
if (!this->rx_start_.has_value()) {
|
||||
this->rx_start_ = millis();
|
||||
} else if (millis() > this->rx_start_ + PKT_TIMEOUT_MS) {
|
||||
} else if (millis() - *this->rx_start_ > PKT_TIMEOUT_MS) {
|
||||
ESP_LOGW(TAG, "Junk on wire. Throwing away partial message (%zu bytes)", avail);
|
||||
this->read_array((uint8_t *) &buffer, avail);
|
||||
this->rx_start_ = 0;
|
||||
this->rx_start_.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -67,7 +67,7 @@ void BL0942::loop() {
|
||||
this->received_package_(&buffer);
|
||||
}
|
||||
}
|
||||
this->rx_start_ = 0;
|
||||
this->rx_start_.reset();
|
||||
}
|
||||
|
||||
bool BL0942::validate_checksum_(DataPacket *data) {
|
||||
|
||||
@@ -59,10 +59,10 @@ namespace bl0942 {
|
||||
//
|
||||
// Which makes BL0952_EREF = BL0942_PREF * 3600000 / 419430.4
|
||||
|
||||
static const float BL0942_PREF = 596; // taken from tasmota
|
||||
static const float BL0942_UREF = 15873.35944299; // should be 73989/1.218
|
||||
static const float BL0942_IREF = 251213.46469622; // 305978/1.218
|
||||
static const float BL0942_EREF = 3304.61127328; // Measured
|
||||
static const float BL0942_PREF = 623.0270705; // calculated using UREF and IREF
|
||||
static const float BL0942_UREF = 15883.34116; // calculated for (390k x 5 / 510R) voltage divider
|
||||
static const float BL0942_IREF = 251065.6814; // calculated for 1mR shunt
|
||||
static const float BL0942_EREF = 5347.484240; // calculated using UREF and IREF
|
||||
|
||||
struct DataPacket {
|
||||
uint8_t frame_header;
|
||||
@@ -86,11 +86,11 @@ enum LineFrequency : uint8_t {
|
||||
|
||||
class BL0942 : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
||||
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
|
||||
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
|
||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { this->voltage_sensor_ = voltage_sensor; }
|
||||
void set_current_sensor(sensor::Sensor *current_sensor) { this->current_sensor_ = current_sensor; }
|
||||
void set_power_sensor(sensor::Sensor *power_sensor) { this->power_sensor_ = power_sensor; }
|
||||
void set_energy_sensor(sensor::Sensor *energy_sensor) { this->energy_sensor_ = energy_sensor; }
|
||||
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { this->frequency_sensor_ = frequency_sensor; }
|
||||
void set_line_freq(LineFrequency freq) { this->line_freq_ = freq; }
|
||||
void set_address(uint8_t address) { this->address_ = address; }
|
||||
void set_reset(bool reset) { this->reset_ = reset; }
|
||||
@@ -140,7 +140,7 @@ class BL0942 : public PollingComponent, public uart::UARTDevice {
|
||||
uint8_t address_ = 0;
|
||||
bool reset_ = false;
|
||||
LineFrequency line_freq_ = LINE_FREQUENCY_50HZ;
|
||||
uint32_t rx_start_ = 0;
|
||||
optional<uint32_t> rx_start_{};
|
||||
uint32_t prev_cf_cnt_ = 0;
|
||||
|
||||
bool validate_checksum_(DataPacket *data);
|
||||
|
||||
@@ -87,7 +87,10 @@ void BLENUS::setup() {
|
||||
global_ble_nus = this;
|
||||
#ifdef USE_LOGGER
|
||||
if (logger::global_logger != nullptr && this->expose_log_) {
|
||||
logger::global_logger->add_log_listener(this);
|
||||
logger::global_logger->add_log_callback(
|
||||
this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
|
||||
static_cast<BLENUS *>(self)->on_log(level, tag, message, message_len);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
|
||||
namespace esphome::ble_nus {
|
||||
|
||||
class BLENUS : public Component
|
||||
#ifdef USE_LOGGER
|
||||
,
|
||||
public logger::LogListener
|
||||
#endif
|
||||
{
|
||||
class BLENUS : public Component {
|
||||
enum TxStatus {
|
||||
TX_DISABLED,
|
||||
TX_ENABLED,
|
||||
@@ -29,7 +24,7 @@ class BLENUS : public Component
|
||||
size_t write_array(const uint8_t *data, size_t len);
|
||||
void set_expose_log(bool expose_log) { this->expose_log_ = expose_log; }
|
||||
#ifdef USE_LOGGER
|
||||
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
|
||||
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
@@ -101,7 +101,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if (this->found_ && this->last_seen_ + this->timeout_ < millis())
|
||||
if (this->found_ && millis() - this->last_seen_ > this->timeout_)
|
||||
this->set_found_(false);
|
||||
}
|
||||
void dump_config() override;
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
namespace esphome::bluetooth_proxy {
|
||||
|
||||
static const esp_err_t ESP_GATT_NOT_CONNECTED = -1;
|
||||
static const int DONE_SENDING_SERVICES = -2;
|
||||
static const int INIT_SENDING_SERVICES = -3;
|
||||
static constexpr esp_err_t ESP_GATT_NOT_CONNECTED = -1;
|
||||
static constexpr int DONE_SENDING_SERVICES = -2;
|
||||
static constexpr int INIT_SENDING_SERVICES = -3;
|
||||
|
||||
using namespace esp32_ble_client;
|
||||
|
||||
@@ -35,8 +35,8 @@ using namespace esp32_ble_client;
|
||||
// Version 3: New connection API
|
||||
// Version 4: Pairing support
|
||||
// Version 5: Cache clear support
|
||||
static const uint32_t LEGACY_ACTIVE_CONNECTIONS_VERSION = 5;
|
||||
static const uint32_t LEGACY_PASSIVE_ONLY_VERSION = 1;
|
||||
static constexpr uint32_t LEGACY_ACTIVE_CONNECTIONS_VERSION = 5;
|
||||
static constexpr uint32_t LEGACY_PASSIVE_ONLY_VERSION = 1;
|
||||
|
||||
enum BluetoothProxyFeature : uint32_t {
|
||||
FEATURE_PASSIVE_SCAN = 1 << 0,
|
||||
|
||||
@@ -22,11 +22,11 @@ static const uint8_t BME680_REGISTER_CHIPID = 0xD0;
|
||||
|
||||
static const uint8_t BME680_REGISTER_FIELD0 = 0x1D;
|
||||
|
||||
const float BME680_GAS_LOOKUP_TABLE_1[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8,
|
||||
0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0};
|
||||
constexpr float BME680_GAS_LOOKUP_TABLE_1[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8,
|
||||
0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0};
|
||||
|
||||
const float BME680_GAS_LOOKUP_TABLE_2[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8,
|
||||
-0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
constexpr float BME680_GAS_LOOKUP_TABLE_2[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8,
|
||||
-0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
|
||||
[[maybe_unused]] static const char *oversampling_to_str(BME680Oversampling oversampling) {
|
||||
switch (oversampling) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
#ifdef USE_BSEC
|
||||
#include <bsec.h>
|
||||
|
||||
@@ -178,8 +178,11 @@ async def to_code_base(config):
|
||||
bsec2_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||
cg.add(var.set_bsec2_configuration(bsec2_arr, len(rhs)))
|
||||
|
||||
# Although this component does not use SPI, the BSEC2 Arduino library requires the SPI library
|
||||
# The BSEC2 and BME68x Arduino libraries unconditionally include Wire.h and
|
||||
# SPI.h in their source files, so these libraries must be available even though
|
||||
# ESPHome uses its own I2C/SPI abstractions instead of the Arduino ones.
|
||||
if core.CORE.using_arduino:
|
||||
cg.add_library("Wire", None)
|
||||
cg.add_library("SPI", None)
|
||||
cg.add_library(
|
||||
"BME68x Sensor library",
|
||||
|
||||
@@ -3,18 +3,22 @@
|
||||
namespace esphome::camera {
|
||||
|
||||
BufferImpl::BufferImpl(size_t size) {
|
||||
this->data_ = this->allocator_.allocate(size);
|
||||
RAMAllocator<uint8_t> allocator;
|
||||
this->data_ = allocator.allocate(size);
|
||||
this->size_ = size;
|
||||
}
|
||||
|
||||
BufferImpl::BufferImpl(CameraImageSpec *spec) {
|
||||
this->data_ = this->allocator_.allocate(spec->bytes_per_image());
|
||||
RAMAllocator<uint8_t> allocator;
|
||||
this->data_ = allocator.allocate(spec->bytes_per_image());
|
||||
this->size_ = spec->bytes_per_image();
|
||||
}
|
||||
|
||||
BufferImpl::~BufferImpl() {
|
||||
if (this->data_ != nullptr)
|
||||
this->allocator_.deallocate(this->data_, this->size_);
|
||||
if (this->data_ != nullptr) {
|
||||
RAMAllocator<uint8_t> allocator;
|
||||
allocator.deallocate(this->data_, this->size_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::camera
|
||||
|
||||
@@ -18,7 +18,6 @@ class BufferImpl : public Buffer {
|
||||
~BufferImpl() override;
|
||||
|
||||
protected:
|
||||
RAMAllocator<uint8_t> allocator_;
|
||||
size_t size_{};
|
||||
uint8_t *data_{};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ namespace esphome::camera_encoder {
|
||||
|
||||
bool EncoderBufferImpl::set_buffer_size(size_t size) {
|
||||
if (size > this->capacity_) {
|
||||
uint8_t *p = this->allocator_.reallocate(this->data_, size);
|
||||
RAMAllocator<uint8_t> allocator;
|
||||
uint8_t *p = allocator.reallocate(this->data_, size);
|
||||
if (p == nullptr)
|
||||
return false;
|
||||
|
||||
@@ -16,8 +17,10 @@ bool EncoderBufferImpl::set_buffer_size(size_t size) {
|
||||
}
|
||||
|
||||
EncoderBufferImpl::~EncoderBufferImpl() {
|
||||
if (this->data_ != nullptr)
|
||||
this->allocator_.deallocate(this->data_, this->capacity_);
|
||||
if (this->data_ != nullptr) {
|
||||
RAMAllocator<uint8_t> allocator;
|
||||
allocator.deallocate(this->data_, this->capacity_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::camera_encoder
|
||||
|
||||
@@ -16,7 +16,6 @@ class EncoderBufferImpl : public camera::EncoderBuffer {
|
||||
~EncoderBufferImpl() override;
|
||||
|
||||
protected:
|
||||
RAMAllocator<uint8_t> allocator_;
|
||||
size_t capacity_{};
|
||||
size_t size_{};
|
||||
uint8_t *data_{};
|
||||
|
||||
@@ -76,13 +76,15 @@ def _final_validate(config: ConfigType) -> ConfigType:
|
||||
|
||||
# Register socket needs for DNS server and additional HTTP connections
|
||||
# - 1 UDP socket for DNS server
|
||||
# - 3 additional TCP sockets for captive portal detection probes + configuration requests
|
||||
# - 3 TCP sockets for captive portal detection probes + configuration requests
|
||||
# OS captive portal detection makes multiple probe requests that stay in TIME_WAIT.
|
||||
# Need headroom for actual user configuration requests.
|
||||
# LRU purging will reclaim idle sockets to prevent exhaustion from repeated attempts.
|
||||
# The listening socket is registered by web_server_base (shared HTTP server).
|
||||
from esphome.components import socket
|
||||
|
||||
socket.consume_sockets(4, "captive_portal")(config)
|
||||
socket.consume_sockets(3, "captive_portal")(config)
|
||||
socket.consume_sockets(1, "captive_portal", socket.SocketType.UDP)(config)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome::captive_portal {
|
||||
|
||||
#ifdef USE_CAPTIVE_PORTAL_GZIP
|
||||
const uint8_t INDEX_GZ[] PROGMEM = {
|
||||
constexpr uint8_t INDEX_GZ[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x95, 0x16, 0x6b, 0x8f, 0xdb, 0x36, 0xf2, 0x7b, 0x7e,
|
||||
0x05, 0x8f, 0x49, 0xbb, 0x52, 0xb3, 0x7a, 0x7a, 0xed, 0x6c, 0x24, 0x51, 0x45, 0x9a, 0xbb, 0xa2, 0x05, 0x9a, 0x36,
|
||||
0xc0, 0x6e, 0x73, 0x1f, 0x82, 0x00, 0x4b, 0x53, 0x23, 0x8b, 0x31, 0x45, 0xea, 0x48, 0xca, 0x8f, 0x18, 0xbe, 0xdf,
|
||||
@@ -86,7 +86,7 @@ const uint8_t INDEX_GZ[] PROGMEM = {
|
||||
0xfc, 0xda, 0xd1, 0xf8, 0xe9, 0xa3, 0xe1, 0xa6, 0xfb, 0x1f, 0x53, 0x58, 0x46, 0xb2, 0xf9, 0x0a, 0x00, 0x00};
|
||||
|
||||
#else // Brotli (default, smaller)
|
||||
const uint8_t INDEX_BR[] PROGMEM = {
|
||||
constexpr uint8_t INDEX_BR[] PROGMEM = {
|
||||
0x1b, 0xf8, 0x0a, 0x00, 0x64, 0x5a, 0xd3, 0xfa, 0xe7, 0xf3, 0x62, 0xd8, 0x06, 0x1b, 0xe9, 0x6a, 0x8a, 0x81, 0x2b,
|
||||
0xb5, 0x49, 0x14, 0x37, 0xdc, 0x9e, 0x1a, 0xcb, 0x56, 0x87, 0xfb, 0xff, 0xf7, 0x73, 0x75, 0x12, 0x0a, 0xd6, 0x48,
|
||||
0x84, 0xc6, 0x21, 0xa4, 0x6d, 0xb5, 0x71, 0xef, 0x13, 0xbe, 0x4e, 0x54, 0xf1, 0x64, 0x8f, 0x3f, 0xcc, 0x9a, 0x78,
|
||||
|
||||
@@ -47,8 +47,8 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
|
||||
request->send(stream);
|
||||
}
|
||||
void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
||||
std::string ssid = request->arg("ssid").c_str(); // NOLINT(readability-redundant-string-cstr)
|
||||
std::string psk = request->arg("psk").c_str(); // NOLINT(readability-redundant-string-cstr)
|
||||
const auto &ssid = request->arg("ssid");
|
||||
const auto &psk = request->arg("psk");
|
||||
ESP_LOGI(TAG,
|
||||
"Requested WiFi Settings Change:\n"
|
||||
" SSID='%s'\n"
|
||||
@@ -56,10 +56,10 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
||||
ssid.c_str(), psk.c_str());
|
||||
#ifdef USE_ESP8266
|
||||
// ESP8266 is single-threaded, call directly
|
||||
wifi::global_wifi_component->save_wifi_sta(ssid, psk);
|
||||
wifi::global_wifi_component->save_wifi_sta(ssid.c_str(), psk.c_str());
|
||||
#else
|
||||
// Defer save to main loop thread to avoid NVS operations from HTTP thread
|
||||
this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid, psk); });
|
||||
this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid.c_str(), psk.c_str()); });
|
||||
#endif
|
||||
request->redirect(ESPHOME_F("/?save"));
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ void DNSServer::start(const network::IPAddress &ip) {
|
||||
#endif
|
||||
|
||||
// Create loop-monitored UDP socket
|
||||
this->socket_ = socket::socket_ip_loop_monitored(SOCK_DGRAM, IPPROTO_UDP);
|
||||
this->socket_ = socket::socket_ip_loop_monitored(SOCK_DGRAM, IPPROTO_UDP).release();
|
||||
if (this->socket_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Socket create failed");
|
||||
return;
|
||||
@@ -70,17 +70,14 @@ void DNSServer::start(const network::IPAddress &ip) {
|
||||
int err = this->socket_->bind((struct sockaddr *) &server_addr, addr_len);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Bind failed: %d", errno);
|
||||
this->socket_ = nullptr;
|
||||
this->destroy_socket_();
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "Bound to port %d", DNS_PORT);
|
||||
}
|
||||
|
||||
void DNSServer::stop() {
|
||||
if (this->socket_ != nullptr) {
|
||||
this->socket_->close();
|
||||
this->socket_ = nullptr;
|
||||
}
|
||||
this->destroy_socket_();
|
||||
ESP_LOGV(TAG, "Stopped");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <memory>
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#include "esphome/components/socket/socket.h"
|
||||
@@ -15,9 +14,15 @@ class DNSServer {
|
||||
void process_next_request();
|
||||
|
||||
protected:
|
||||
// No explicit close() needed — listen sockets have no active connections on
|
||||
// failure/shutdown. Destructor handles fd cleanup (close or abort per platform).
|
||||
inline void destroy_socket_() {
|
||||
delete this->socket_;
|
||||
this->socket_ = nullptr;
|
||||
}
|
||||
static constexpr size_t DNS_BUFFER_SIZE = 192;
|
||||
|
||||
std::unique_ptr<socket::Socket> socket_{nullptr};
|
||||
socket::Socket *socket_{nullptr};
|
||||
network::IPAddress server_ip_;
|
||||
uint8_t buffer_[DNS_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ from esphome.const import (
|
||||
CONF_DATA,
|
||||
CONF_FREQUENCY,
|
||||
CONF_ID,
|
||||
CONF_OUTPUT_POWER,
|
||||
CONF_VALUE,
|
||||
CONF_WAIT_TIME,
|
||||
)
|
||||
from esphome.core import ID
|
||||
@@ -21,7 +23,6 @@ ns = cg.esphome_ns.namespace("cc1101")
|
||||
CC1101Component = ns.class_("CC1101Component", cg.Component, spi.SPIDevice)
|
||||
|
||||
# Config keys
|
||||
CONF_OUTPUT_POWER = "output_power"
|
||||
CONF_RX_ATTENUATION = "rx_attenuation"
|
||||
CONF_DC_BLOCKING_FILTER = "dc_blocking_filter"
|
||||
CONF_IF_FREQUENCY = "if_frequency"
|
||||
@@ -333,3 +334,94 @@ async def send_packet_action_to_code(config, action_id, template_arg, args):
|
||||
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data))
|
||||
cg.add(var.set_data_static(arr, len(data)))
|
||||
return var
|
||||
|
||||
|
||||
# Setter action definitions: (setter_name, validator, template_type, enum_map)
|
||||
_SETTER_ACTIONS = [
|
||||
(
|
||||
"set_frequency",
|
||||
cv.All(cv.frequency, cv.float_range(min=300.0e6, max=928.0e6)),
|
||||
float,
|
||||
None,
|
||||
),
|
||||
("set_output_power", cv.float_range(min=-30.0, max=11.0), float, None),
|
||||
("set_modulation_type", cv.enum(MODULATION, upper=False), Modulation, MODULATION),
|
||||
("set_symbol_rate", cv.float_range(min=600, max=500000), float, None),
|
||||
(
|
||||
"set_rx_attenuation",
|
||||
cv.enum(RX_ATTENUATION, upper=False),
|
||||
RxAttenuation,
|
||||
RX_ATTENUATION,
|
||||
),
|
||||
("set_dc_blocking_filter", cv.boolean, bool, None),
|
||||
("set_manchester", cv.boolean, bool, None),
|
||||
(
|
||||
"set_filter_bandwidth",
|
||||
cv.All(cv.frequency, cv.float_range(min=58000, max=812000)),
|
||||
float,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"set_fsk_deviation",
|
||||
cv.All(cv.frequency, cv.float_range(min=1500, max=381000)),
|
||||
float,
|
||||
None,
|
||||
),
|
||||
("set_msk_deviation", cv.int_range(min=1, max=8), cg.uint8, None),
|
||||
("set_channel", cv.uint8_t, cg.uint8, None),
|
||||
(
|
||||
"set_channel_spacing",
|
||||
cv.All(cv.frequency, cv.float_range(min=25000, max=405000)),
|
||||
float,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"set_if_frequency",
|
||||
cv.All(cv.frequency, cv.float_range(min=25000, max=788000)),
|
||||
float,
|
||||
None,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def _register_setter_actions():
|
||||
for setter_name, validator, templ_type, enum_map in _SETTER_ACTIONS:
|
||||
class_name = (
|
||||
"".join(word.capitalize() for word in setter_name.split("_")) + "Action"
|
||||
)
|
||||
action_cls = ns.class_(
|
||||
class_name, automation.Action, cg.Parented.template(CC1101Component)
|
||||
)
|
||||
schema = cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(CC1101Component),
|
||||
cv.Required(CONF_VALUE): cv.templatable(validator),
|
||||
},
|
||||
key=CONF_VALUE,
|
||||
)
|
||||
|
||||
async def _setter_action_to_code(
|
||||
config,
|
||||
action_id,
|
||||
template_arg,
|
||||
args,
|
||||
_setter=setter_name,
|
||||
_type=templ_type,
|
||||
_map=enum_map,
|
||||
):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
data = config[CONF_VALUE]
|
||||
if cg.is_template(data):
|
||||
templ_ = await cg.templatable(data, args, _type)
|
||||
cg.add(getattr(var, _setter)(templ_))
|
||||
else:
|
||||
cg.add(getattr(var, _setter)(_map[data] if _map else data))
|
||||
return var
|
||||
|
||||
automation.register_action(f"cc1101.{setter_name}", action_cls, schema)(
|
||||
_setter_action_to_code
|
||||
)
|
||||
|
||||
|
||||
_register_setter_actions()
|
||||
|
||||
@@ -242,6 +242,9 @@ void CC1101Component::begin_tx() {
|
||||
if (this->gdo0_pin_ != nullptr) {
|
||||
this->gdo0_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||
}
|
||||
// Transition through IDLE to bypass CCA (Clear Channel Assessment) which can
|
||||
// block TX entry when strobing from RX, and to ensure FS_AUTOCAL calibration
|
||||
this->enter_idle_();
|
||||
if (!this->enter_tx_()) {
|
||||
ESP_LOGW(TAG, "Failed to enter TX state!");
|
||||
}
|
||||
@@ -252,6 +255,8 @@ void CC1101Component::begin_rx() {
|
||||
if (this->gdo0_pin_ != nullptr) {
|
||||
this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
}
|
||||
// Transition through IDLE to ensure FS_AUTOCAL calibration occurs
|
||||
this->enter_idle_();
|
||||
if (!this->enter_rx_()) {
|
||||
ESP_LOGW(TAG, "Failed to enter RX state!");
|
||||
}
|
||||
|
||||
@@ -161,4 +161,82 @@ template<typename... Ts> class SendPacketAction : public Action<Ts...>, public P
|
||||
size_t data_static_len_{0};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetSymbolRateAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, symbol_rate)
|
||||
void play(const Ts &...x) override { this->parent_->set_symbol_rate(this->symbol_rate_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetFrequencyAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, frequency)
|
||||
void play(const Ts &...x) override { this->parent_->set_frequency(this->frequency_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetOutputPowerAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, output_power)
|
||||
void play(const Ts &...x) override { this->parent_->set_output_power(this->output_power_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetModulationTypeAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(Modulation, modulation_type)
|
||||
void play(const Ts &...x) override { this->parent_->set_modulation_type(this->modulation_type_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetRxAttenuationAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(RxAttenuation, rx_attenuation)
|
||||
void play(const Ts &...x) override { this->parent_->set_rx_attenuation(this->rx_attenuation_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetDcBlockingFilterAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, dc_blocking_filter)
|
||||
void play(const Ts &...x) override { this->parent_->set_dc_blocking_filter(this->dc_blocking_filter_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetManchesterAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, manchester)
|
||||
void play(const Ts &...x) override { this->parent_->set_manchester(this->manchester_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetFilterBandwidthAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, filter_bandwidth)
|
||||
void play(const Ts &...x) override { this->parent_->set_filter_bandwidth(this->filter_bandwidth_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetFskDeviationAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, fsk_deviation)
|
||||
void play(const Ts &...x) override { this->parent_->set_fsk_deviation(this->fsk_deviation_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetMskDeviationAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, msk_deviation)
|
||||
void play(const Ts &...x) override { this->parent_->set_msk_deviation(this->msk_deviation_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetChannelAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
void play(const Ts &...x) override { this->parent_->set_channel(this->channel_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetChannelSpacingAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, channel_spacing)
|
||||
void play(const Ts &...x) override { this->parent_->set_channel_spacing(this->channel_spacing_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetIfFrequencyAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, if_frequency)
|
||||
void play(const Ts &...x) override { this->parent_->set_if_frequency(this->if_frequency_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::cc1101
|
||||
|
||||
@@ -124,9 +124,11 @@ bool CH422GComponent::write_outputs_() {
|
||||
|
||||
float CH422GComponent::get_setup_priority() const { return setup_priority::IO; }
|
||||
|
||||
#ifdef USE_LOOP_PRIORITY
|
||||
// Run our loop() method very early in the loop, so that we cache read values
|
||||
// before other components call our digital_read() method.
|
||||
float CH422GComponent::get_loop_priority() const { return 9.0f; } // Just after WIFI
|
||||
#endif
|
||||
|
||||
void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
||||
bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) ^ this->inverted_; }
|
||||
|
||||
@@ -23,7 +23,9 @@ class CH422GComponent : public Component, public i2c::I2CDevice {
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
float get_setup_priority() const override;
|
||||
#ifdef USE_LOOP_PRIORITY
|
||||
float get_loop_priority() const override;
|
||||
#endif
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -129,9 +129,11 @@ bool CH423Component::write_outputs_() {
|
||||
|
||||
float CH423Component::get_setup_priority() const { return setup_priority::IO; }
|
||||
|
||||
#ifdef USE_LOOP_PRIORITY
|
||||
// Run our loop() method very early in the loop, so that we cache read values
|
||||
// before other components call our digital_read() method.
|
||||
float CH423Component::get_loop_priority() const { return 9.0f; } // Just after WIFI
|
||||
#endif
|
||||
|
||||
void CH423GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
||||
bool CH423GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) ^ this->inverted_; }
|
||||
|
||||
@@ -22,7 +22,9 @@ class CH423Component : public Component, public i2c::I2CDevice {
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
float get_setup_priority() const override;
|
||||
#ifdef USE_LOOP_PRIORITY
|
||||
float get_loop_priority() const override;
|
||||
#endif
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -10,8 +10,10 @@ const LogString *climate_mode_to_string(ClimateMode mode) {
|
||||
return ClimateModeStrings::get_log_str(static_cast<uint8_t>(mode), ClimateModeStrings::LAST_INDEX);
|
||||
}
|
||||
|
||||
// Climate action strings indexed by ClimateAction enum (0,2-6): OFF, (gap), COOLING, HEATING, IDLE, DRYING, FAN
|
||||
PROGMEM_STRING_TABLE(ClimateActionStrings, "OFF", "UNKNOWN", "COOLING", "HEATING", "IDLE", "DRYING", "FAN", "UNKNOWN");
|
||||
// Climate action strings indexed by ClimateAction enum (0,2-7): OFF, (gap), COOLING, HEATING, IDLE, DRYING, FAN,
|
||||
// DEFROSTING
|
||||
PROGMEM_STRING_TABLE(ClimateActionStrings, "OFF", "UNKNOWN", "COOLING", "HEATING", "IDLE", "DRYING", "FAN",
|
||||
"DEFROSTING", "UNKNOWN");
|
||||
|
||||
const LogString *climate_action_to_string(ClimateAction action) {
|
||||
return ClimateActionStrings::get_log_str(static_cast<uint8_t>(action), ClimateActionStrings::LAST_INDEX);
|
||||
|
||||
@@ -41,6 +41,8 @@ enum ClimateAction : uint8_t {
|
||||
CLIMATE_ACTION_DRYING = 5,
|
||||
/// The climate device is in fan only mode
|
||||
CLIMATE_ACTION_FAN = 6,
|
||||
/// The climate device is defrosting
|
||||
CLIMATE_ACTION_DEFROSTING = 7,
|
||||
};
|
||||
|
||||
/// NOTE: If adding values, update ClimateFanModeMask in climate_traits.h to use the new last value
|
||||
|
||||
@@ -126,7 +126,7 @@ void LinearCombinationComponent::setup() {
|
||||
}
|
||||
|
||||
void LinearCombinationComponent::handle_new_value(float value) {
|
||||
// Multiplies each sensor state by a configured coeffecient and then sums
|
||||
// Multiplies each sensor state by a configured coefficient and then sums
|
||||
|
||||
if (!std::isfinite(value))
|
||||
return;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import logging
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
@@ -15,6 +17,8 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core.entity_helpers import inherit_property_from
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CODEOWNERS = ["@Cat-Ion", "@kahrendt"]
|
||||
|
||||
combination_ns = cg.esphome_ns.namespace("combination")
|
||||
@@ -47,7 +51,8 @@ SumCombinationComponent = combination_ns.class_(
|
||||
"SumCombinationComponent", cg.Component, sensor.Sensor
|
||||
)
|
||||
|
||||
CONF_COEFFECIENT = "coeffecient"
|
||||
CONF_COEFFICIENT = "coefficient"
|
||||
CONF_COEFFECIENT = "coeffecient" # Deprecated, remove before 2026.12.0
|
||||
CONF_ERROR = "error"
|
||||
CONF_KALMAN = "kalman"
|
||||
CONF_LINEAR = "linear"
|
||||
@@ -68,11 +73,34 @@ KALMAN_SOURCE_SCHEMA = cv.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
LINEAR_SOURCE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor),
|
||||
cv.Required(CONF_COEFFECIENT): cv.templatable(cv.float_),
|
||||
}
|
||||
|
||||
def _migrate_coeffecient(config):
|
||||
"""Migrate deprecated 'coeffecient' spelling to 'coefficient'."""
|
||||
if CONF_COEFFECIENT in config:
|
||||
if CONF_COEFFICIENT in config:
|
||||
raise cv.Invalid(
|
||||
f"Cannot specify both '{CONF_COEFFICIENT}' and '{CONF_COEFFECIENT}'"
|
||||
)
|
||||
_LOGGER.warning(
|
||||
"'%s' is deprecated, use '%s' instead. Will be removed in 2026.12.0",
|
||||
CONF_COEFFECIENT,
|
||||
CONF_COEFFICIENT,
|
||||
)
|
||||
config[CONF_COEFFICIENT] = config.pop(CONF_COEFFECIENT)
|
||||
elif CONF_COEFFICIENT not in config:
|
||||
raise cv.Invalid(f"'{CONF_COEFFICIENT}' is a required option")
|
||||
return config
|
||||
|
||||
|
||||
LINEAR_SOURCE_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_SOURCE): cv.use_id(sensor.Sensor),
|
||||
cv.Optional(CONF_COEFFICIENT): cv.templatable(cv.float_),
|
||||
cv.Optional(CONF_COEFFECIENT): cv.templatable(cv.float_),
|
||||
}
|
||||
),
|
||||
_migrate_coeffecient,
|
||||
)
|
||||
|
||||
SENSOR_ONLY_SOURCE_SCHEMA = cv.Schema(
|
||||
@@ -162,12 +190,12 @@ async def to_code(config):
|
||||
)
|
||||
cg.add(var.add_source(source, error))
|
||||
elif config[CONF_TYPE] == CONF_LINEAR:
|
||||
coeffecient = await cg.templatable(
|
||||
source_conf[CONF_COEFFECIENT],
|
||||
coefficient = await cg.templatable(
|
||||
source_conf[CONF_COEFFICIENT],
|
||||
[(float, "x")],
|
||||
cg.float_,
|
||||
)
|
||||
cg.add(var.add_source(source, coeffecient))
|
||||
cg.add(var.add_source(source, coefficient))
|
||||
else:
|
||||
cg.add(var.add_source(source))
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ from esphome.const import (
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_MQTT_JSON_STATE_PAYLOAD,
|
||||
CONF_ON_IDLE,
|
||||
CONF_ON_OPEN,
|
||||
CONF_POSITION,
|
||||
@@ -119,6 +120,9 @@ _COVER_SCHEMA = (
|
||||
.extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
|
||||
cv.Optional(CONF_MQTT_JSON_STATE_PAYLOAD): cv.All(
|
||||
cv.requires_component("mqtt"), cv.boolean
|
||||
),
|
||||
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
|
||||
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||
@@ -148,6 +152,22 @@ _COVER_SCHEMA = (
|
||||
_COVER_SCHEMA.add_extra(entity_duplicate_validator("cover"))
|
||||
|
||||
|
||||
def _validate_mqtt_state_topics(config):
|
||||
if config.get(CONF_MQTT_JSON_STATE_PAYLOAD):
|
||||
if CONF_POSITION_STATE_TOPIC in config:
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_POSITION_STATE_TOPIC}' cannot be used with '{CONF_MQTT_JSON_STATE_PAYLOAD}: true'"
|
||||
)
|
||||
if CONF_TILT_STATE_TOPIC in config:
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_TILT_STATE_TOPIC}' cannot be used with '{CONF_MQTT_JSON_STATE_PAYLOAD}: true'"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
_COVER_SCHEMA.add_extra(_validate_mqtt_state_topics)
|
||||
|
||||
|
||||
def cover_schema(
|
||||
class_: MockObjClass,
|
||||
*,
|
||||
@@ -195,6 +215,9 @@ async def setup_cover_core_(var, config):
|
||||
position_command_topic := config.get(CONF_POSITION_COMMAND_TOPIC)
|
||||
) is not None:
|
||||
cg.add(mqtt_.set_custom_position_command_topic(position_command_topic))
|
||||
if config.get(CONF_MQTT_JSON_STATE_PAYLOAD):
|
||||
cg.add_define("USE_MQTT_COVER_JSON")
|
||||
cg.add(mqtt_.set_use_json_format(True))
|
||||
if (tilt_state_topic := config.get(CONF_TILT_STATE_TOPIC)) is not None:
|
||||
cg.add(mqtt_.set_custom_tilt_state_topic(tilt_state_topic))
|
||||
if (tilt_command_topic := config.get(CONF_TILT_COMMAND_TOPIC)) is not None:
|
||||
|
||||
@@ -15,29 +15,29 @@ static const char *const TAG = "cse7761";
|
||||
* https://github.com/arendst/Tasmota/blob/development/tasmota/xnrg_19_cse7761.ino
|
||||
\*********************************************************************************************/
|
||||
|
||||
static const int CSE7761_UREF = 42563; // RmsUc
|
||||
static const int CSE7761_IREF = 52241; // RmsIAC
|
||||
static const int CSE7761_PREF = 44513; // PowerPAC
|
||||
static constexpr int CSE7761_UREF = 42563; // RmsUc
|
||||
static constexpr int CSE7761_IREF = 52241; // RmsIAC
|
||||
static constexpr int CSE7761_PREF = 44513; // PowerPAC
|
||||
|
||||
static const uint8_t CSE7761_REG_SYSCON = 0x00; // (2) System Control Register (0x0A04)
|
||||
static const uint8_t CSE7761_REG_EMUCON = 0x01; // (2) Metering control register (0x0000)
|
||||
static const uint8_t CSE7761_REG_EMUCON2 = 0x13; // (2) Metering control register 2 (0x0001)
|
||||
static const uint8_t CSE7761_REG_PULSE1SEL = 0x1D; // (2) Pin function output select register (0x3210)
|
||||
static constexpr uint8_t CSE7761_REG_SYSCON = 0x00; // (2) System Control Register (0x0A04)
|
||||
static constexpr uint8_t CSE7761_REG_EMUCON = 0x01; // (2) Metering control register (0x0000)
|
||||
static constexpr uint8_t CSE7761_REG_EMUCON2 = 0x13; // (2) Metering control register 2 (0x0001)
|
||||
static constexpr uint8_t CSE7761_REG_PULSE1SEL = 0x1D; // (2) Pin function output select register (0x3210)
|
||||
|
||||
static const uint8_t CSE7761_REG_RMSIA = 0x24; // (3) The effective value of channel A current (0x000000)
|
||||
static const uint8_t CSE7761_REG_RMSIB = 0x25; // (3) The effective value of channel B current (0x000000)
|
||||
static const uint8_t CSE7761_REG_RMSU = 0x26; // (3) Voltage RMS (0x000000)
|
||||
static const uint8_t CSE7761_REG_POWERPA = 0x2C; // (4) Channel A active power, update rate 27.2Hz (0x00000000)
|
||||
static const uint8_t CSE7761_REG_POWERPB = 0x2D; // (4) Channel B active power, update rate 27.2Hz (0x00000000)
|
||||
static const uint8_t CSE7761_REG_SYSSTATUS = 0x43; // (1) System status register
|
||||
static constexpr uint8_t CSE7761_REG_RMSIA = 0x24; // (3) The effective value of channel A current (0x000000)
|
||||
static constexpr uint8_t CSE7761_REG_RMSIB = 0x25; // (3) The effective value of channel B current (0x000000)
|
||||
static constexpr uint8_t CSE7761_REG_RMSU = 0x26; // (3) Voltage RMS (0x000000)
|
||||
static constexpr uint8_t CSE7761_REG_POWERPA = 0x2C; // (4) Channel A active power, update rate 27.2Hz (0x00000000)
|
||||
static constexpr uint8_t CSE7761_REG_POWERPB = 0x2D; // (4) Channel B active power, update rate 27.2Hz (0x00000000)
|
||||
static constexpr uint8_t CSE7761_REG_SYSSTATUS = 0x43; // (1) System status register
|
||||
|
||||
static const uint8_t CSE7761_REG_COEFFCHKSUM = 0x6F; // (2) Coefficient checksum
|
||||
static const uint8_t CSE7761_REG_RMSIAC = 0x70; // (2) Channel A effective current conversion coefficient
|
||||
static constexpr uint8_t CSE7761_REG_COEFFCHKSUM = 0x6F; // (2) Coefficient checksum
|
||||
static constexpr uint8_t CSE7761_REG_RMSIAC = 0x70; // (2) Channel A effective current conversion coefficient
|
||||
|
||||
static const uint8_t CSE7761_SPECIAL_COMMAND = 0xEA; // Start special command
|
||||
static const uint8_t CSE7761_CMD_RESET = 0x96; // Reset command, after receiving the command, the chip resets
|
||||
static const uint8_t CSE7761_CMD_CLOSE_WRITE = 0xDC; // Close write operation
|
||||
static const uint8_t CSE7761_CMD_ENABLE_WRITE = 0xE5; // Enable write operation
|
||||
static constexpr uint8_t CSE7761_SPECIAL_COMMAND = 0xEA; // Start special command
|
||||
static constexpr uint8_t CSE7761_CMD_RESET = 0x96; // Reset command, after receiving the command, the chip resets
|
||||
static constexpr uint8_t CSE7761_CMD_CLOSE_WRITE = 0xDC; // Close write operation
|
||||
static constexpr uint8_t CSE7761_CMD_ENABLE_WRITE = 0xE5; // Enable write operation
|
||||
|
||||
enum CSE7761 { RMS_IAC, RMS_IBC, RMS_UC, POWER_PAC, POWER_PBC, POWER_SC, ENERGY_AC, ENERGY_BC };
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace cse7766 {
|
||||
namespace esphome::cse7766 {
|
||||
|
||||
static const char *const TAG = "cse7766";
|
||||
|
||||
@@ -258,5 +257,4 @@ void CSE7766Component::dump_config() {
|
||||
this->check_uart_settings(4800, 1, uart::UART_CONFIG_PARITY_EVEN);
|
||||
}
|
||||
|
||||
} // namespace cse7766
|
||||
} // namespace esphome
|
||||
} // namespace esphome::cse7766
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace cse7766 {
|
||||
namespace esphome::cse7766 {
|
||||
|
||||
static constexpr size_t CSE7766_RAW_DATA_SIZE = 24;
|
||||
|
||||
@@ -49,5 +48,4 @@ class CSE7766Component : public Component, public uart::UARTDevice {
|
||||
uint16_t cf_pulses_last_{0};
|
||||
};
|
||||
|
||||
} // namespace cse7766
|
||||
} // namespace esphome
|
||||
} // namespace esphome::cse7766
|
||||
|
||||
@@ -148,14 +148,14 @@ void CurrentBasedCover::dump_config() {
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Close Duration: %.1fs\n"
|
||||
"Obstacle Rollback: %.1f%%",
|
||||
" Obstacle Rollback: %.1f%%",
|
||||
this->close_duration_ / 1e3f, this->obstacle_rollback_ * 100);
|
||||
if (this->max_duration_ != UINT32_MAX) {
|
||||
ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f);
|
||||
ESP_LOGCONFIG(TAG, " Maximum duration: %.1fs", this->max_duration_ / 1e3f);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"Start sensing delay: %.1fs\n"
|
||||
"Malfunction detection: %s",
|
||||
" Start sensing delay: %.1fs\n"
|
||||
" Malfunction detection: %s",
|
||||
this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFE
|
||||
} else {
|
||||
snprintf(buf, size, "unknown source");
|
||||
}
|
||||
ESP_LOGD(TAG, "Reset Reason: %s", buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -107,7 +106,6 @@ const char *DebugComponent::get_wakeup_cause_(std::span<char, RESET_REASON_BUFFE
|
||||
} else {
|
||||
wake_reason = "unknown source";
|
||||
}
|
||||
ESP_LOGD(TAG, "Wakeup Reason: %s", wake_reason);
|
||||
// Return the static string directly - no need to copy to buffer
|
||||
return wake_reason;
|
||||
}
|
||||
@@ -172,7 +170,6 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
}
|
||||
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT
|
||||
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT
|
||||
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
|
||||
pos = buf_append_printf(buf, size, pos, "|Flash: %" PRIu32 "kB Speed:%" PRIu32 "MHz Mode:%s", flash_size, flash_speed,
|
||||
flash_mode);
|
||||
#endif
|
||||
@@ -194,39 +191,46 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
if (info.features != 0) {
|
||||
pos = buf_append_printf(buf, size, pos, "%sOther:0x%" PRIx32, first_feature ? "" : ", ", info.features);
|
||||
}
|
||||
ESP_LOGD(TAG, "Chip: Model=%s, Cores=%u, Revision=%u", model, info.cores, info.revision);
|
||||
pos = buf_append_printf(buf, size, pos, " Cores:%u Revision:%u", info.cores, info.revision);
|
||||
|
||||
uint32_t cpu_freq_mhz = arch_get_cpu_freq_hz() / 1000000;
|
||||
ESP_LOGD(TAG, "CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
||||
pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32 " MHz", cpu_freq_mhz);
|
||||
|
||||
// Framework detection
|
||||
#ifdef USE_ARDUINO
|
||||
ESP_LOGD(TAG, "Framework: Arduino");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: Arduino");
|
||||
#elif defined(USE_ESP32)
|
||||
ESP_LOGD(TAG, "Framework: ESP-IDF");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: ESP-IDF");
|
||||
#else
|
||||
ESP_LOGW(TAG, "Framework: UNKNOWN");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: UNKNOWN");
|
||||
#endif
|
||||
|
||||
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
|
||||
pos = buf_append_printf(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
|
||||
|
||||
uint8_t mac[6];
|
||||
get_mac_address_raw(mac);
|
||||
ESP_LOGD(TAG, "EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
pos = buf_append_printf(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3],
|
||||
mac[4], mac[5]);
|
||||
|
||||
char reason_buffer[RESET_REASON_BUFFER_SIZE];
|
||||
const char *reset_reason = get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
||||
pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
|
||||
|
||||
const char *wakeup_cause = get_wakeup_cause_(std::span<char, RESET_REASON_BUFFER_SIZE>(reason_buffer));
|
||||
|
||||
uint8_t mac[6];
|
||||
get_mac_address_raw(mac);
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"ESP32 debug info:\n"
|
||||
" Chip: %s\n"
|
||||
" Cores: %u\n"
|
||||
" Revision: %u\n"
|
||||
" CPU Frequency: %" PRIu32 " MHz\n"
|
||||
" ESP-IDF Version: %s\n"
|
||||
" EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X\n"
|
||||
" Reset Reason: %s\n"
|
||||
" Wakeup Cause: %s",
|
||||
model, info.cores, info.revision, cpu_freq_mhz, esp_get_idf_version(), mac[0], mac[1], mac[2], mac[3],
|
||||
mac[4], mac[5], reset_reason, wakeup_cause);
|
||||
#if defined(USE_ARDUINO)
|
||||
ESP_LOGD(TAG, " Flash: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed, flash_mode);
|
||||
#endif
|
||||
// Framework detection
|
||||
#ifdef USE_ARDUINO
|
||||
ESP_LOGD(TAG, " Framework: Arduino");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: Arduino");
|
||||
#else
|
||||
ESP_LOGD(TAG, " Framework: ESP-IDF");
|
||||
pos = buf_append_printf(buf, size, pos, "|Framework: ESP-IDF");
|
||||
#endif
|
||||
|
||||
pos = buf_append_printf(buf, size, pos, "|ESP-IDF: %s", esp_get_idf_version());
|
||||
pos = buf_append_printf(buf, size, pos, "|EFuse MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3],
|
||||
mac[4], mac[5]);
|
||||
pos = buf_append_printf(buf, size, pos, "|Reset: %s", reset_reason);
|
||||
pos = buf_append_printf(buf, size, pos, "|Wakeup: %s", wakeup_cause);
|
||||
|
||||
return pos;
|
||||
|
||||
@@ -128,14 +128,16 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
// NOLINTEND(readability-static-accessed-through-instance)
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Chip ID: 0x%08" PRIX32 "\n"
|
||||
"SDK Version: %s\n"
|
||||
"Core Version: %s\n"
|
||||
"Boot Version=%u Mode=%u\n"
|
||||
"CPU Frequency: %u\n"
|
||||
"Flash Chip ID=0x%08" PRIX32 "\n"
|
||||
"Reset Reason: %s\n"
|
||||
"Reset Info: %s",
|
||||
"ESP8266 debug info:\n"
|
||||
" Chip ID: 0x%08" PRIX32 "\n"
|
||||
" SDK Version: %s\n"
|
||||
" Core Version: %s\n"
|
||||
" Boot Version: %u\n"
|
||||
" Boot Mode: %u\n"
|
||||
" CPU Frequency: %u\n"
|
||||
" Flash Chip ID: 0x%08" PRIX32 "\n"
|
||||
" Reset Reason: %s\n"
|
||||
" Reset Info: %s",
|
||||
chip_id, sdk_version, get_core_version_str(core_version_buffer), boot_version, boot_mode, cpu_freq,
|
||||
flash_chip_id, reset_reason, get_reset_info_str(reset_info_buffer, resetInfo.reason));
|
||||
|
||||
|
||||
@@ -27,12 +27,14 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
uint32_t mac_id = lt_cpu_get_mac_id();
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"LibreTiny Version: %s\n"
|
||||
"Chip: %s (%04x) @ %u MHz\n"
|
||||
"Chip ID: 0x%06" PRIX32 "\n"
|
||||
"Board: %s\n"
|
||||
"Flash: %" PRIu32 " KiB / RAM: %" PRIu32 " KiB\n"
|
||||
"Reset Reason: %s",
|
||||
"LibreTiny debug info:\n"
|
||||
" Version: %s\n"
|
||||
" Chip: %s (%04x) @ %u MHz\n"
|
||||
" Chip ID: 0x%06" PRIX32 "\n"
|
||||
" Board: %s\n"
|
||||
" Flash: %" PRIu32 " KiB\n"
|
||||
" RAM: %" PRIu32 " KiB\n"
|
||||
" Reset Reason: %s",
|
||||
lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), mac_id,
|
||||
lt_get_board_code(), flash_kib, ram_kib, reset_reason);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#ifdef USE_ZEPHYR
|
||||
#include <climits>
|
||||
#include "esphome/core/log.h"
|
||||
#include <esphome/components/zephyr/reset_reason.h>
|
||||
#include <zephyr/drivers/hwinfo.h>
|
||||
#include <hal/nrf_power.h>
|
||||
#include <cstdint>
|
||||
@@ -15,16 +16,6 @@ static const char *const TAG = "debug";
|
||||
constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR = 0xFFC;
|
||||
constexpr std::uintptr_t MBR_BOOTLOADER_ADDR = 0xFF8;
|
||||
|
||||
static size_t append_reset_reason(char *buf, size_t size, size_t pos, bool set, const char *reason) {
|
||||
if (!set) {
|
||||
return pos;
|
||||
}
|
||||
if (pos > 0) {
|
||||
pos = buf_append_printf(buf, size, pos, ", ");
|
||||
}
|
||||
return buf_append_printf(buf, size, pos, "%s", reason);
|
||||
}
|
||||
|
||||
static inline uint32_t read_mem_u32(uintptr_t addr) {
|
||||
return *reinterpret_cast<volatile uint32_t *>(addr); // NOLINT(performance-no-int-to-ptr)
|
||||
}
|
||||
@@ -57,39 +48,7 @@ static inline uint32_t sd_version_get() {
|
||||
}
|
||||
|
||||
const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
|
||||
char *buf = buffer.data();
|
||||
const size_t size = RESET_REASON_BUFFER_SIZE;
|
||||
|
||||
uint32_t cause;
|
||||
auto ret = hwinfo_get_reset_cause(&cause);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Unable to get reset cause: %d", ret);
|
||||
buf[0] = '\0';
|
||||
return buf;
|
||||
}
|
||||
size_t pos = 0;
|
||||
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_PIN, "External pin");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_SOFTWARE, "Software reset");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_BROWNOUT, "Brownout (drop in voltage)");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_POR, "Power-on reset (POR)");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_WATCHDOG, "Watchdog timer expiration");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_DEBUG, "Debug event");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_SECURITY, "Security violation");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_LOW_POWER_WAKE, "Waking up from low power mode");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_CPU_LOCKUP, "CPU lock-up detected");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_PARITY, "Parity error");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_PLL, "PLL error");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_CLOCK, "Clock error");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_HARDWARE, "Hardware reset");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_USER, "User reset");
|
||||
pos = append_reset_reason(buf, size, pos, cause & RESET_TEMPERATURE, "Temperature reset");
|
||||
|
||||
// Ensure null termination if nothing was written
|
||||
if (pos == 0) {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
const char *buf = zephyr::get_reset_reason(buffer);
|
||||
ESP_LOGD(TAG, "Reset Reason: %s", buf);
|
||||
return buf;
|
||||
}
|
||||
@@ -120,13 +79,13 @@ static void fa_cb(const struct flash_area *fa, void *user_data) {
|
||||
void DebugComponent::log_partition_info_() {
|
||||
#if CONFIG_FLASH_MAP_LABELS
|
||||
ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
|
||||
"| Label | Offset | Size\n"
|
||||
"--------------------------------------------"
|
||||
"| Label | Offset | Size");
|
||||
ESP_LOGCONFIG(TAG, "--------------------------------------------"
|
||||
"-----------------------------------------------");
|
||||
#else
|
||||
ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
|
||||
"| Offset | Size\n"
|
||||
"-----------------------------------------"
|
||||
"| Offset | Size");
|
||||
ESP_LOGCONFIG(TAG, "-----------------------------------------"
|
||||
"------------------------------");
|
||||
#endif
|
||||
flash_area_foreach(fa_cb, nullptr);
|
||||
@@ -325,11 +284,12 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
char mac_pretty[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
|
||||
get_mac_address_pretty_into_buffer(mac_pretty);
|
||||
ESP_LOGD(TAG,
|
||||
"Code page size: %u, code size: %u, device id: 0x%08x%08x\n"
|
||||
"Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x\n"
|
||||
"Device address type: %s, address: %s\n"
|
||||
"Part code: nRF%x, version: %c%c%c%c, package: %s\n"
|
||||
"RAM: %ukB, Flash: %ukB, production test: %sdone",
|
||||
"nRF debug info:\n"
|
||||
" Code page size: %u, code size: %u, device id: 0x%08x%08x\n"
|
||||
" Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x\n"
|
||||
" Device address type: %s, address: %s\n"
|
||||
" Part code: nRF%x, version: %c%c%c%c, package: %s\n"
|
||||
" RAM: %ukB, Flash: %ukB, production test: %sdone",
|
||||
NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE, NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0], NRF_FICR->ER[0],
|
||||
NRF_FICR->ER[1], NRF_FICR->ER[2], NRF_FICR->ER[3], NRF_FICR->IR[0], NRF_FICR->IR[1], NRF_FICR->IR[2],
|
||||
NRF_FICR->IR[3], (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"), mac_pretty, NRF_FICR->INFO.PART,
|
||||
@@ -340,23 +300,22 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
(NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == UICR_PSELRESET_CONNECT_Connected
|
||||
<< UICR_PSELRESET_CONNECT_Pos;
|
||||
ESP_LOGD(
|
||||
TAG, "GPIO as NFC pins: %s, GPIO as nRESET pin: %s",
|
||||
TAG, " GPIO as NFC pins: %s, GPIO as nRESET pin: %s",
|
||||
YESNO((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)),
|
||||
YESNO(n_reset_enabled));
|
||||
if (n_reset_enabled) {
|
||||
uint8_t port = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PORT_Msk) >> UICR_PSELRESET_PORT_Pos;
|
||||
uint8_t pin = (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_PIN_Msk) >> UICR_PSELRESET_PIN_Pos;
|
||||
ESP_LOGD(TAG, "nRESET port P%u.%02u", port, pin);
|
||||
ESP_LOGD(TAG, " nRESET port P%u.%02u", port, pin);
|
||||
}
|
||||
#ifdef USE_BOOTLOADER_MCUBOOT
|
||||
ESP_LOGD(TAG, "bootloader: mcuboot");
|
||||
ESP_LOGD(TAG, " Bootloader: mcuboot");
|
||||
#else
|
||||
ESP_LOGD(TAG, "bootloader: Adafruit, version %u.%u.%u", (BOOTLOADER_VERSION_REGISTER >> 16) & 0xFF,
|
||||
ESP_LOGD(TAG, " Bootloader: Adafruit, version %u.%u.%u", (BOOTLOADER_VERSION_REGISTER >> 16) & 0xFF,
|
||||
(BOOTLOADER_VERSION_REGISTER >> 8) & 0xFF, BOOTLOADER_VERSION_REGISTER & 0xFF);
|
||||
ESP_LOGD(TAG,
|
||||
"MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x\n"
|
||||
"MBR param page addr 0x%08x, UICR param page addr 0x%08x",
|
||||
read_mem_u32(MBR_BOOTLOADER_ADDR), NRF_UICR->NRFFW[0], read_mem_u32(MBR_PARAM_PAGE_ADDR),
|
||||
ESP_LOGD(TAG, " MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x", read_mem_u32(MBR_BOOTLOADER_ADDR),
|
||||
NRF_UICR->NRFFW[0]);
|
||||
ESP_LOGD(TAG, " MBR param page addr 0x%08x, UICR param page addr 0x%08x", read_mem_u32(MBR_PARAM_PAGE_ADDR),
|
||||
NRF_UICR->NRFFW[1]);
|
||||
if (is_sd_present()) {
|
||||
uint32_t const sd_id = sd_id_get();
|
||||
@@ -367,7 +326,7 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
ver[1] = (sd_version - ver[0] * 1000000) / 1000;
|
||||
ver[2] = (sd_version - ver[0] * 1000000 - ver[1] * 1000);
|
||||
|
||||
ESP_LOGD(TAG, "SoftDevice: S%u %u.%u.%u", sd_id, ver[0], ver[1], ver[2]);
|
||||
ESP_LOGD(TAG, " SoftDevice: S%u %u.%u.%u", sd_id, ver[0], ver[1], ver[2]);
|
||||
#ifdef USE_SOFTDEVICE_ID
|
||||
#ifdef USE_SOFTDEVICE_VERSION
|
||||
if (USE_SOFTDEVICE_ID != sd_id || USE_SOFTDEVICE_VERSION != ver[0]) {
|
||||
@@ -393,10 +352,8 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
|
||||
}
|
||||
return res;
|
||||
};
|
||||
ESP_LOGD(TAG,
|
||||
"NRFFW %s\n"
|
||||
"NRFHW %s",
|
||||
uicr(NRF_UICR->NRFFW, 13).c_str(), uicr(NRF_UICR->NRFHW, 12).c_str());
|
||||
ESP_LOGD(TAG, " NRFFW %s", uicr(NRF_UICR->NRFFW, 13).c_str());
|
||||
ESP_LOGD(TAG, " NRFHW %s", uicr(NRF_UICR->NRFHW, 12).c_str());
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -40,9 +40,11 @@ void DeepSleepComponent::loop() {
|
||||
this->begin_sleep();
|
||||
}
|
||||
|
||||
#ifdef USE_LOOP_PRIORITY
|
||||
float DeepSleepComponent::get_loop_priority() const {
|
||||
return -100.0f; // run after everything else is ready
|
||||
}
|
||||
#endif
|
||||
|
||||
void DeepSleepComponent::set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; }
|
||||
|
||||
|
||||
@@ -113,7 +113,9 @@ class DeepSleepComponent : public Component {
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
#ifdef USE_LOOP_PRIORITY
|
||||
float get_loop_priority() const override;
|
||||
#endif
|
||||
float get_setup_priority() const override;
|
||||
|
||||
/// Helper to enter deep sleep mode
|
||||
|
||||
@@ -187,18 +187,18 @@ uint8_t DetRangeCfgCommand::on_message(std::string &message) {
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG,
|
||||
"Updated detection area config:\n"
|
||||
"Detection area 1 from %.02fm to %.02fm.",
|
||||
" Detection area 1 from %.02fm to %.02fm.",
|
||||
this->min1_, this->max1_);
|
||||
if (this->min2_ >= 0 && this->max2_ >= 0) {
|
||||
ESP_LOGI(TAG, "Detection area 2 from %.02fm to %.02fm.", this->min2_, this->max2_);
|
||||
ESP_LOGI(TAG, " Detection area 2 from %.02fm to %.02fm.", this->min2_, this->max2_);
|
||||
}
|
||||
if (this->min3_ >= 0 && this->max3_ >= 0) {
|
||||
ESP_LOGI(TAG, "Detection area 3 from %.02fm to %.02fm.", this->min3_, this->max3_);
|
||||
ESP_LOGI(TAG, " Detection area 3 from %.02fm to %.02fm.", this->min3_, this->max3_);
|
||||
}
|
||||
if (this->min4_ >= 0 && this->max4_ >= 0) {
|
||||
ESP_LOGI(TAG, "Detection area 4 from %.02fm to %.02fm.", this->min4_, this->max4_);
|
||||
ESP_LOGI(TAG, " Detection area 4 from %.02fm to %.02fm.", this->min4_, this->max4_);
|
||||
}
|
||||
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
|
||||
ESP_LOGD(TAG, " Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet.
|
||||
@@ -222,10 +222,10 @@ uint8_t SetLatencyCommand::on_message(std::string &message) {
|
||||
} else if (message == "Done") {
|
||||
ESP_LOGI(TAG,
|
||||
"Updated output latency config:\n"
|
||||
"Signal that someone was detected is delayed by %.03f s.\n"
|
||||
"Signal that nobody is detected anymore is delayed by %.03f s.",
|
||||
" Signal that someone was detected is delayed by %.03f s.\n"
|
||||
" Signal that nobody is detected anymore is delayed by %.03f s.",
|
||||
this->delay_after_detection_, this->delay_after_disappear_);
|
||||
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
|
||||
ESP_LOGD(TAG, " Used command: %s", this->cmd_.c_str());
|
||||
return 1; // Command done
|
||||
}
|
||||
return 0; // Command not done yet
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace esphome {
|
||||
namespace display {
|
||||
static const char *const TAG = "display";
|
||||
|
||||
const Color COLOR_OFF(0, 0, 0, 0);
|
||||
const Color COLOR_ON(255, 255, 255, 255);
|
||||
// COLOR_OFF and COLOR_ON are now inline constexpr in display.h
|
||||
|
||||
void Display::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
|
||||
void Display::clear() { this->fill(COLOR_OFF); }
|
||||
@@ -811,9 +810,9 @@ bool Display::clamp_y_(int y, int h, int &min_y, int &max_y) {
|
||||
return min_y < max_y;
|
||||
}
|
||||
|
||||
const uint8_t TESTCARD_FONT[3][8] PROGMEM = {{0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00}, // 'R'
|
||||
{0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00}, // 'G'
|
||||
{0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00}}; // 'B'
|
||||
constexpr uint8_t TESTCARD_FONT[3][8] PROGMEM = {{0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00}, // 'R'
|
||||
{0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00}, // 'G'
|
||||
{0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00}}; // 'B'
|
||||
|
||||
void Display::test_card() {
|
||||
int w = get_width(), h = get_height(), image_w, image_h;
|
||||
|
||||
@@ -298,9 +298,9 @@ using display_writer_t = DisplayWriter<Display>;
|
||||
}
|
||||
|
||||
/// Turn the pixel OFF.
|
||||
extern const Color COLOR_OFF;
|
||||
inline constexpr Color COLOR_OFF(0, 0, 0, 0);
|
||||
/// Turn the pixel ON.
|
||||
extern const Color COLOR_ON;
|
||||
inline constexpr Color COLOR_ON(255, 255, 255, 255);
|
||||
|
||||
class BaseImage {
|
||||
public:
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "dlms_meter.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#if defined(USE_ESP8266_FRAMEWORK_ARDUINO)
|
||||
#include <bearssl/bearssl.h>
|
||||
#elif defined(USE_ESP32)
|
||||
@@ -410,7 +408,7 @@ void DlmsMeterComponent::decode_obis_(uint8_t *plaintext, uint16_t message_lengt
|
||||
if (current_position + 1 < message_length) {
|
||||
int8_t scaler = static_cast<int8_t>(plaintext[current_position + 1]);
|
||||
if (scaler != 0) {
|
||||
value *= powf(10.0f, scaler);
|
||||
value *= pow10_int(scaler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ class Dsmr : public Component, public uart::UARTDevice {
|
||||
void dump_config() override;
|
||||
|
||||
void set_decryption_key(const char *decryption_key);
|
||||
// Remove before 2026.8.0
|
||||
ESPDEPRECATED("Pass .c_str() - e.g. set_decryption_key(key.c_str()). Removed in 2026.8.0", "2026.2.0")
|
||||
void set_decryption_key(const std::string &decryption_key) { this->set_decryption_key(decryption_key.c_str()); }
|
||||
void set_max_telegram_length(size_t length) { this->max_telegram_len_ = length; }
|
||||
void set_request_pin(GPIOPin *request_pin) { this->request_pin_ = request_pin; }
|
||||
void set_request_interval(uint32_t interval) { this->request_interval_ = interval; }
|
||||
|
||||
@@ -14,12 +14,17 @@ static const int PORT = 5568;
|
||||
E131Component::E131Component() {}
|
||||
|
||||
E131Component::~E131Component() {
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
if (this->socket_) {
|
||||
this->socket_->close();
|
||||
}
|
||||
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||
this->udp_.stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
void E131Component::setup() {
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
this->socket_ = socket::socket_ip(SOCK_DGRAM, IPPROTO_IP);
|
||||
|
||||
int enable = 1;
|
||||
@@ -50,6 +55,13 @@ void E131Component::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||
if (!this->udp_.begin(PORT)) {
|
||||
ESP_LOGW(TAG, "Cannot bind E1.31 to port %d.", PORT);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
join_igmp_groups_();
|
||||
}
|
||||
@@ -58,19 +70,20 @@ void E131Component::loop() {
|
||||
E131Packet packet;
|
||||
int universe = 0;
|
||||
uint8_t buf[1460];
|
||||
ssize_t len;
|
||||
|
||||
ssize_t len = this->socket_->read(buf, sizeof(buf));
|
||||
if (len == -1) {
|
||||
return;
|
||||
}
|
||||
// Drain all queued packets so multi-universe frames are applied
|
||||
// atomically before the light writes. Without this, each universe
|
||||
// packet would trigger a separate full-strip write causing tearing.
|
||||
while ((len = this->read_(buf, sizeof(buf))) > 0) {
|
||||
if (!this->packet_(buf, (size_t) len, universe, packet)) {
|
||||
ESP_LOGV(TAG, "Invalid packet received of size %d.", (int) len);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this->packet_(buf, (size_t) len, universe, packet)) {
|
||||
ESP_LOGV(TAG, "Invalid packet received of size %zd.", len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->process_(universe, packet)) {
|
||||
ESP_LOGV(TAG, "Ignored packet for %d universe of size %d.", universe, packet.count);
|
||||
if (!this->process_(universe, packet)) {
|
||||
ESP_LOGV(TAG, "Ignored packet for %d universe of size %d.", universe, packet.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_NETWORK
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
#include "esphome/components/socket/socket.h"
|
||||
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||
#include <WiFiUdp.h>
|
||||
#endif
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -23,6 +26,11 @@ struct E131Packet {
|
||||
uint8_t values[E131_MAX_PROPERTY_VALUES_COUNT];
|
||||
};
|
||||
|
||||
struct UniverseConsumer {
|
||||
uint16_t universe;
|
||||
uint16_t consumers;
|
||||
};
|
||||
|
||||
class E131Component : public esphome::Component {
|
||||
public:
|
||||
E131Component();
|
||||
@@ -38,16 +46,30 @@ class E131Component : public esphome::Component {
|
||||
void set_method(E131ListenMethod listen_method) { this->listen_method_ = listen_method; }
|
||||
|
||||
protected:
|
||||
inline ssize_t read_(uint8_t *buf, size_t len) {
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
return this->socket_->read(buf, len);
|
||||
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||
if (!this->udp_.parsePacket())
|
||||
return -1;
|
||||
return this->udp_.read(buf, len);
|
||||
#endif
|
||||
}
|
||||
bool packet_(const uint8_t *data, size_t len, int &universe, E131Packet &packet);
|
||||
bool process_(int universe, const E131Packet &packet);
|
||||
bool join_igmp_groups_();
|
||||
UniverseConsumer *find_universe_(int universe);
|
||||
void join_(int universe);
|
||||
void leave_(int universe);
|
||||
|
||||
E131ListenMethod listen_method_{E131_MULTICAST};
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
std::unique_ptr<socket::Socket> socket_;
|
||||
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||
WiFiUDP udp_;
|
||||
#endif
|
||||
std::vector<E131AddressableLightEffect *> light_effects_;
|
||||
std::map<int, int> universe_consumers_;
|
||||
std::vector<UniverseConsumer> universe_consumers_;
|
||||
};
|
||||
|
||||
} // namespace e131
|
||||
|
||||
@@ -60,17 +60,19 @@ union E131RawPacket {
|
||||
const size_t E131_MIN_PACKET_SIZE = reinterpret_cast<size_t>(&((E131RawPacket *) nullptr)->property_values[1]);
|
||||
|
||||
bool E131Component::join_igmp_groups_() {
|
||||
if (listen_method_ != E131_MULTICAST)
|
||||
if (this->listen_method_ != E131_MULTICAST)
|
||||
return false;
|
||||
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||
if (this->socket_ == nullptr)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
for (auto universe : universe_consumers_) {
|
||||
if (!universe.second)
|
||||
for (auto &entry : this->universe_consumers_) {
|
||||
if (!entry.consumers)
|
||||
continue;
|
||||
|
||||
ip4_addr_t multicast_addr =
|
||||
network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff));
|
||||
network::IPAddress(239, 255, ((entry.universe >> 8) & 0xff), ((entry.universe >> 0) & 0xff));
|
||||
|
||||
err_t err;
|
||||
{
|
||||
@@ -79,34 +81,47 @@ bool E131Component::join_igmp_groups_() {
|
||||
}
|
||||
|
||||
if (err) {
|
||||
ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", universe.first);
|
||||
ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", entry.universe);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UniverseConsumer *E131Component::find_universe_(int universe) {
|
||||
for (auto &entry : this->universe_consumers_) {
|
||||
if (entry.universe == universe)
|
||||
return &entry;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void E131Component::join_(int universe) {
|
||||
// store only latest received packet for the given universe
|
||||
auto consumers = ++universe_consumers_[universe];
|
||||
|
||||
if (consumers > 1) {
|
||||
return; // we already joined before
|
||||
auto *consumer = this->find_universe_(universe);
|
||||
if (consumer != nullptr) {
|
||||
if (consumer->consumers++ > 0) {
|
||||
return; // we already joined before
|
||||
}
|
||||
} else {
|
||||
this->universe_consumers_.push_back({static_cast<uint16_t>(universe), 1});
|
||||
}
|
||||
|
||||
if (join_igmp_groups_()) {
|
||||
if (this->join_igmp_groups_()) {
|
||||
ESP_LOGD(TAG, "Joined %d universe for E1.31.", universe);
|
||||
}
|
||||
}
|
||||
|
||||
void E131Component::leave_(int universe) {
|
||||
auto consumers = --universe_consumers_[universe];
|
||||
auto *consumer = this->find_universe_(universe);
|
||||
if (consumer == nullptr)
|
||||
return;
|
||||
|
||||
if (consumers > 0) {
|
||||
if (--consumer->consumers > 0) {
|
||||
return; // we have other consumers of the given universe
|
||||
}
|
||||
|
||||
if (listen_method_ == E131_MULTICAST) {
|
||||
if (this->listen_method_ == E131_MULTICAST) {
|
||||
ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff));
|
||||
|
||||
LwIPLock lock;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user